diff --git a/tests/FSharp.Refactor.Tests/FSharp.Refactor.Tests.fsproj b/tests/FSharp.Refactor.Tests/FSharp.Refactor.Tests.fsproj
index 126cbab..12bd02c 100644
--- a/tests/FSharp.Refactor.Tests/FSharp.Refactor.Tests.fsproj
+++ b/tests/FSharp.Refactor.Tests/FSharp.Refactor.Tests.fsproj
@@ -113,6 +113,18 @@
+
+
+
+
+
+
+ <_Parameter1>Net48ReferenceAssemblies
+ <_Parameter2>$(PkgMicrosoft_NETFramework_ReferenceAssemblies_net48)/build/.NETFramework/v4.8
+
diff --git a/tests/FSharp.Refactor.Tests/Parsing.fs b/tests/FSharp.Refactor.Tests/Parsing.fs
index 9681413..c491df7 100644
--- a/tests/FSharp.Refactor.Tests/Parsing.fs
+++ b/tests/FSharp.Refactor.Tests/Parsing.fs
@@ -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, false)
+ |> Seq.cast
+ |> 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>.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)
@@ -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.
diff --git a/tests/FSharp.Refactor.Tests/PerfTests.fs b/tests/FSharp.Refactor.Tests/PerfTests.fs
index 760716a..7525016 100644
--- a/tests/FSharp.Refactor.Tests/PerfTests.fs
+++ b/tests/FSharp.Refactor.Tests/PerfTests.fs
@@ -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 ----