Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions tests/FSharp.Refactor.Tests/FSharp.Refactor.Tests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,18 @@
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.0" />
<PackageReference Update="FSharp.Core" Version="10.1.201" />
<!-- The .NET Framework 4.8 reference assemblies, for the legacy-framework
compilations in Parsing.fs. Only Windows ships them in Program Files;
this package carries the same files to every OS. Its assets stay out
of the build: the tests only read the files from its folder. -->
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies.net48" Version="1.0.3" GeneratePathProperty="true" ExcludeAssets="all" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
<_Parameter1>Net48ReferenceAssemblies</_Parameter1>
<_Parameter2>$(PkgMicrosoft_NETFramework_ReferenceAssemblies_net48)/build/.NETFramework/v4.8</_Parameter2>
</AssemblyAttribute>
</ItemGroup>

<ItemGroup>
Expand Down
53 changes: 46 additions & 7 deletions tests/FSharp.Refactor.Tests/Parsing.fs
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,53 @@ let parseAndCheck (source: string) : ParsedInput * ISourceText * FSharpCheckFile
requireTypechecks "parseAndCheck" source checkResults
tree, sourceText, checkResults

/// `parseAndCheck` against the LEGACY .NET Framework reference set (the
/// machine's mscorlib), without requiring a clean typecheck.
/// The .NET Framework 4.8 reference assemblies a legacy compilation reads,
/// from the Microsoft.NETFramework.ReferenceAssemblies.net48 package (see the
/// test project). `assumeDotNetFramework = true` alone resolves them from
/// Program Files, which only Windows has: on Linux the script got no usable
/// mscorlib, `string` itself did not resolve, and a legacy test could only
/// pass for the wrong reason.
let private net48References =
lazy
(let directory =
System.Reflection.Assembly
.GetExecutingAssembly()
.GetCustomAttributes(typeof<System.Reflection.AssemblyMetadataAttribute>, false)
|> Seq.cast<System.Reflection.AssemblyMetadataAttribute>
|> Seq.find (fun a -> a.Key = "Net48ReferenceAssemblies")
|> fun a -> a.Value

[
"mscorlib"
"System"
"System.Core"
"System.Numerics"
"System.Xml"
"System.Data"
"Facades/netstandard"
"Facades/System.Runtime"
]
|> List.map (fun name -> Path.Combine(directory, name + ".dll"))
// FSharp.Core targets netstandard, which the facade above bridges
|> fun framework -> typeof<list<int>>.Assembly.Location :: framework)

/// `parseAndCheck` against the LEGACY .NET Framework reference set
/// (net48's mscorlib), without requiring a clean typecheck.
let parseAndCheckLegacyFrameworkAllowingErrors (source: string) : ParsedInput * ISourceText * FSharpCheckFileResults =
let sourceText = SourceText.ofString source

let options, _ =
checker.GetProjectOptionsFromScript("Legacy.fsx", sourceText, assumeDotNetFramework = true)
|> Async.RunSynchronously
let options =
let scriptOptions, _ =
checker.GetProjectOptionsFromScript("Legacy.fsx", sourceText, assumeDotNetFramework = true)
|> Async.RunSynchronously

{ scriptOptions with
OtherOptions =
[|
yield! scriptOptions.OtherOptions |> Array.filter (fun o -> not (o.StartsWith "-r:"))
for reference in net48References.Value -> "-r:" + reference
|]
}

let parseResults, answer =
checker.ParseAndCheckFileInProject("Legacy.fsx", source.GetHashCode(), sourceText, options)
Expand All @@ -137,8 +176,8 @@ let parseAndCheckLegacyFrameworkAllowingErrors (source: string) : ParsedInput *
| FSharpCheckFileAnswer.Aborted ->
failwith $"Typechecking was aborted, calling parseAndCheckLegacyFramework with source: {source}"

/// `parseAndCheck` against the LEGACY .NET Framework reference set (the
/// machine's mscorlib): the compilation a netstandard2.0/net4x project
/// `parseAndCheck` against the LEGACY .NET Framework reference set
/// (net48's mscorlib): the compilation a netstandard2.0/net4x project
/// sees, where the char and span overloads of String do not exist. The
/// rules that gate on a modern framework must stay quiet here. Fails the
/// test when the input has a type error there.
Expand Down
61 changes: 30 additions & 31 deletions tests/FSharp.Refactor.Tests/PerfTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -901,38 +901,37 @@ let ``FR0106, FR0166 and FR0167 stay quiet in a legacy .NET Framework compilatio
// the same shapes fire against modern .NET above; against mscorlib the
// char and span overloads of String are absent, so nothing is proven
// and nothing fires — this is how netstandard2.0/net4x stay untouched.
// Only Windows ships the .NET Framework reference assemblies a legacy
// script compilation resolves; elsewhere the fixture cannot typecheck
if OperatingSystem.IsWindows() then
let source =
fsharp
"""
module Test
open System
open System.Text
let f (s: string) (sb: StringBuilder) =
sb.Append(s.Substring(6, 5)) |> ignore
let a = s.[..5] = "ORDER-"
let b = s.Length >= 6 && s.Substring(0, 6) = "ORDER-"
let mutable n = 0
for c in s.ToCharArray() do n <- n + int c
let d = Array.exists Char.IsDigit (s.ToCharArray())
Int32.Parse(s.Substring(0, 2)), a, b, n, d
"""

let tree, sourceText, check =
FSharp.Refactor.Tests.Parsing.parseAndCheckLegacyFramework source

Assert.False(FSharp.Refactor.OptionModule.hasErrors check, "the legacy fixture itself must typecheck")
Assert.Empty(FSharp.Refactor.SubstringSpan.find tree sourceText check)
Assert.Empty(FSharp.Refactor.PrefixCompare.find true tree sourceText check)
Assert.Empty(FSharp.Refactor.CharArrayCopy.find tree sourceText check)
// The legacy compilation reads the net48 reference assemblies from a NuGet
// package (Parsing.fs), so this runs on every OS, not only Windows
let source =
fsharp
"""
module Test
open System
open System.Text
let f (s: string) (sb: StringBuilder) =
sb.Append(s.Substring(6, 5)) |> ignore
let a = s.[..5] = "ORDER-"
let b = s.Length >= 6 && s.Substring(0, 6) = "ORDER-"
let mutable n = 0
for c in s.ToCharArray() do n <- n + int c
let d = Array.exists Char.IsDigit (s.ToCharArray())
Int32.Parse(s.Substring(0, 2)), a, b, n, d
"""

// and the modern compilation of the very same text fires all three
let tree, sourceText, check = FSharp.Refactor.Tests.Parsing.parseAndCheck source
Assert.Equal(2, (FSharp.Refactor.SubstringSpan.find tree sourceText check).Length)
Assert.Equal(2, (FSharp.Refactor.PrefixCompare.find true tree sourceText check).Length)
Assert.Equal(2, (FSharp.Refactor.CharArrayCopy.find tree sourceText check).Length)
let tree, sourceText, check =
FSharp.Refactor.Tests.Parsing.parseAndCheckLegacyFramework source

Assert.False(FSharp.Refactor.OptionModule.hasErrors check, "the legacy fixture itself must typecheck")
Assert.Empty(FSharp.Refactor.SubstringSpan.find tree sourceText check)
Assert.Empty(FSharp.Refactor.PrefixCompare.find true tree sourceText check)
Assert.Empty(FSharp.Refactor.CharArrayCopy.find tree sourceText check)

// and the modern compilation of the very same text fires all three
let tree, sourceText, check = FSharp.Refactor.Tests.Parsing.parseAndCheck source
Assert.Equal(2, (FSharp.Refactor.SubstringSpan.find tree sourceText check).Length)
Assert.Equal(2, (FSharp.Refactor.PrefixCompare.find true tree sourceText check).Length)
Assert.Equal(2, (FSharp.Refactor.CharArrayCopy.find tree sourceText check).Length)

// ---- CapabilityFix dual-framework emission ----

Expand Down
Loading