diff --git a/Language-Version-History.md b/Language-Version-History.md new file mode 100644 index 0000000000..72da5b5b33 --- /dev/null +++ b/Language-Version-History.md @@ -0,0 +1,107 @@ +Features Added in F# Language Versions +==================== + +# [F# 1.0](https://docs.microsoft.com/en-us/archive/blogs/dsyme/welcome-to-dons-f-blog) + +- Discriminated unions +- Records +- Tuples +- Pattern matching +- Type abbreviations +- Object expressions +- Structs +- Signature files +- Imperative programming +- Modules (no functors) +- Nested modules +- .NET Interoperability + +# [F# 1.1](https://docs.microsoft.com/en-us/archive/blogs/dsyme/a-taste-of-whats-new-in-f-1-1) + +- Interactive environment +- Object programming +- Encapsulation Extensions + +# [F# 2.0](https://fsharp.org/specs/language-spec/2.0/FSharpSpec-2.0-April-2012.pdf) + +- Active patterns +- Units of measure +- Sequence expressions +- Asynchronous programming +- Agent programming +- Extension members +- Named arguments +- Optional arguments +- Array slicing +- Quotations +- Native interoperability +- Computation expressions + +# [F# 3.0](https://fsharp.org/specs/language-spec/3.0/FSharpSpec-3.0-final.pdf) + +- Type providers +- LINQ query expressions +- CLIMutable attribute +- Triple-quoted strings +- Auto-properties +- Provided units-of-measure + +# [F# 3.1](https://fsharp.org/specs/language-spec/3.1/FSharpSpec-3.1-final.pdf) + +- Named union type fields +- Extensions to array slicing +- Type inference enhancements + +# [F# 4.0](https://fsharp.org/specs/language-spec/4.0/FSharpSpec-4.0-final.pdf) + +- `printf` on unitized values +- Extension property initializers +- Non-null provided types +- Primary constructors as functions +- Static parameters for provided methods +- `printf` interpolation +- Extended `#if` grammar +- Multiple interface instantiations +- Optional type args +- Params dictionaries + +# [F# 4.1](https://fsharp.org/specs/language-spec/4.1/FSharpSpec-4.1-latest.pdf) + +- Struct tuples which inter-operate with C# tuples +- Struct annotations for Records +- Struct annotations for Single-case Discriminated Unions +- Underscores in numeric literals +- Caller info argument attributes +- Result type and some basic Result functions +- Mutually referential types and modules within the same file +- Implicit `Module` syntax on modules with shared name as type +- Byref returns, supporting consuming C# `ref`-returning methods +- Error message improvements +- Support for `fixed` + +# [F# 4.5](https://docs.microsoft.com/en-us/dotnet/fsharp/whats-new/fsharp-45) + +- Versioning alignment of binary, package, and language +- Support for `Span<'T>` and related types +- Ability to produce `byref` returns +- The `voidptr` type +- The `inref<'T>` and `outref<'T>` types to represent readonly and write-only `byref`s +- `IsByRefLike` structs +- `IsReadOnly` structs +- Extension method support for `byref<'T>`/`inref<'T>`/`outref<'T>` +- `match!` keyword in computation expressions +- Relaxed upcast with `yield` in F# sequence/list/array expressions +- Relaxed indentation with list and array expressions +- Enumeration cases emitted as public + +# [F# 4.6](https://docs.microsoft.com/en-us/dotnet/fsharp/whats-new/fsharp-46) + +- Anonymous records +- `ValueOption` module functions + +# [F# 4.7](https://docs.microsoft.com/en-us/dotnet/fsharp/whats-new/fsharp-47) + +- Compiler support for `LangVersion` +- Implicit `yield`s +- No more required double underscore (wildcard identifier) +- Indentation relaxations for parameters passed to constructors and static methods diff --git a/clean.sh b/clean.sh index 5fa6ffd30a..ad94376d9f 100755 --- a/clean.sh +++ b/clean.sh @@ -24,6 +24,7 @@ DEAD_DIRS=( "tests/fsharp/tests.fs" "tests/fsharp/typecheck" "tests/fsharpqa" + "tests/scripts" "VisualFSharp.sln" "vsintegration" ) diff --git a/eng/Build.ps1 b/eng/Build.ps1 index 0445711695..51a71f9db2 100644 --- a/eng/Build.ps1 +++ b/eng/Build.ps1 @@ -363,16 +363,17 @@ try { if ($testFSharpQA -and -not $noVisualStudio) { Push-Location "$RepoRoot\tests\fsharpqa\source" + $nugetPackages = Get-PackagesDir $resultsRoot = "$ArtifactsDir\TestResults\$configuration" $resultsLog = "test-net40-fsharpqa-results.log" $errorLog = "test-net40-fsharpqa-errors.log" $failLog = "test-net40-fsharpqa-errors" - $perlPackageRoot = "$env:USERPROFILE\.nuget\packages\StrawberryPerl\5.28.0.1"; + $perlPackageRoot = "$nugetPackages\StrawberryPerl\5.28.0.1"; $perlExe = "$perlPackageRoot\bin\perl.exe" Create-Directory $resultsRoot UpdatePath $env:HOSTED_COMPILER = 1 - $env:CSC_PIPE = "$env:USERPROFILE\.nuget\packages\Microsoft.Net.Compilers\2.7.0\tools\csc.exe" + $env:CSC_PIPE = "$nugetPackages\Microsoft.Net.Compilers\2.7.0\tools\csc.exe" $env:FSCOREDLLPATH = "$ArtifactsDir\bin\fsc\$configuration\net472\FSharp.Core.dll" $env:LINK_EXE = "$RepoRoot\tests\fsharpqa\testenv\bin\link\link.exe" $env:OSARCH = $env:PROCESSOR_ARCHITECTURE diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 3d0782ecae..66cc21e803 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -3,9 +3,9 @@ - + https://github.com/dotnet/arcade - 15f00efd583eab4372b2e9ca25bd80ace5b119ad + 0b8ce7c1c078eefb4cbc4d7e67ffc02f1f73382d diff --git a/eng/Versions.props b/eng/Versions.props index 58ff67ba06..e24dd7d43c 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -89,7 +89,6 @@ 4.3.0 4.3.0 4.3.0 - 4.6.0 4.3.0 4.11.0 4.3.0 diff --git a/fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj b/fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj index d88f41a1ca..105eb66405 100644 --- a/fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj +++ b/fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj @@ -525,11 +525,23 @@ Driver\DotNetFrameworkDependencies.fs - - Driver/DependencyManager.fsi + + Driver\AssemblyResolveHandler.fsi - - Driver/DependencyManager.fs + + Driver\AssemblyResolveHandler.fs + + + Driver\NativeDllResolveHandler.fsi + + + Driver\NativeDllResolveHandler.fs + + + Driver/DependencyProvider.fsi + + + Driver/DependencyProvider.fs Driver/CompileOps.fsi diff --git a/fcs/RELEASE_NOTES.md b/fcs/RELEASE_NOTES.md index f9127d499c..77b8cd8e40 100644 --- a/fcs/RELEASE_NOTES.md +++ b/fcs/RELEASE_NOTES.md @@ -1,3 +1,14 @@ +#### 34.1.1 + +From dotnet/fsharp:3777cd4d8..836da28c0: + +* Slight tweaks to error messages around numeric literals (Thanks @Happypig375) +* Deny taking native address of an immutable local value (Thanks @TIHan) +* Fixes to reported ranges for wildcard self-identifiers, module abbreviations, nested modules, attributes, nested types, and fields (Thanks @auduchinok) +* Better compiler error recovery for errors in constructor expressions (Thanks @auduchinok) +* Fix handling of F# Options in C# members with regards to nullable type interop (Thanks @TIHan) +* Move dependency handling of native dlls to the DependencyManager (Thanks @KevinRansom) + #### 34.1.0 From dotnet/fsharp:3af8959b6..9d69b49b7: diff --git a/global.json b/global.json index 6a33a4ecc1..024090cb36 100644 --- a/global.json +++ b/global.json @@ -10,7 +10,7 @@ } }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "1.0.0-beta.20113.5", + "Microsoft.DotNet.Arcade.Sdk": "1.0.0-beta.20124.2", "Microsoft.DotNet.Helix.Sdk": "2.0.0-beta.19069.2" } } diff --git a/src/fsharp/CompileOps.fs b/src/fsharp/CompileOps.fs index dcc6c32839..2b5e4f88a6 100644 --- a/src/fsharp/CompileOps.fs +++ b/src/fsharp/CompileOps.fs @@ -2199,7 +2199,7 @@ type TcConfigBuilder = mutable productNameForBannerText: string /// show the MS (c) notice, e.g. with help or fsi? mutable showBanner: bool - + /// show times between passes? mutable showTimes: bool mutable showLoadedAssemblies: bool @@ -2229,7 +2229,7 @@ type TcConfigBuilder = mutable emitDebugInfoInQuotations: bool mutable exename: string option - + // If true - the compiler will copy FSharp.Core.dll along the produced binaries mutable copyFSharpCore: CopyFSharpCoreFlag @@ -2390,9 +2390,24 @@ type TcConfigBuilder = noConditionalErasure = false pathMap = PathMap.empty langVersion = LanguageVersion("default") - dependencyProvider = new DependencyProvider() + dependencyProvider = Unchecked.defaultof } + // Directories to start probing in + // Algorithm: + // Search for native libraries using: + // 1. Include directories + // 2. compilerToolPath directories + // 3. reference dll's + // 4. The implicit include directory + member private tcConfigB.nativeProbingRoots () = + seq { + yield! tcConfigB.includes + yield! tcConfigB.compilerToolPaths + yield! (tcConfigB.referencedDLLs |> Seq.map(fun ref -> Path.GetDirectoryName(ref.Text))) + yield tcConfigB.implicitIncludeDir + } |> Seq.distinct + static member CreateNew(legacyReferenceResolver, defaultFSharpBinariesDir, reduceMemoryUsage, implicitIncludeDir, isInteractive, isInvalidationSupported, defaultCopyFSharpCore, tryGetMetadataSnapshot) = @@ -2401,17 +2416,20 @@ type TcConfigBuilder = if (String.IsNullOrEmpty defaultFSharpBinariesDir) then failwith "Expected a valid defaultFSharpBinariesDir" - { TcConfigBuilder.Initial with - implicitIncludeDir = implicitIncludeDir - defaultFSharpBinariesDir = defaultFSharpBinariesDir - reduceMemoryUsage = reduceMemoryUsage - legacyReferenceResolver = legacyReferenceResolver - isInteractive = isInteractive - isInvalidationSupported = isInvalidationSupported - copyFSharpCore = defaultCopyFSharpCore - tryGetMetadataSnapshot = tryGetMetadataSnapshot - useFsiAuxLib = isInteractive - } + let tcConfigBuilder = + { TcConfigBuilder.Initial with + implicitIncludeDir = implicitIncludeDir + defaultFSharpBinariesDir = defaultFSharpBinariesDir + reduceMemoryUsage = reduceMemoryUsage + legacyReferenceResolver = legacyReferenceResolver + isInteractive = isInteractive + isInvalidationSupported = isInvalidationSupported + copyFSharpCore = defaultCopyFSharpCore + tryGetMetadataSnapshot = tryGetMetadataSnapshot + useFsiAuxLib = isInteractive + } + tcConfigBuilder.dependencyProvider <- new DependencyProvider(NativeResolutionProbe(tcConfigBuilder.nativeProbingRoots)) + tcConfigBuilder member tcConfigB.ResolveSourceFile(m, nm, pathLoadedFrom) = use unwindBuildPhase = PushThreadBuildPhaseUntilUnwind BuildPhase.Parameter @@ -4964,10 +4982,13 @@ let ProcessMetaCommandsFromInput if not canHaveScriptMetaCommands then errorR(HashReferenceNotAllowedInNonScript m) - let reportError errorType error = - match errorType with - | ErrorReportType.Warning -> warning(Error(error,m)) - | ErrorReportType.Error -> errorR(Error(error, m)) + let reportError = + let report errorType err msg = + let error = err, msg + match errorType with + | ErrorReportType.Warning -> warning(Error(error, m)) + | ErrorReportType.Error -> errorR(Error(error, m)) + ResolvingErrorReport (report) match args with | [path] -> @@ -5257,10 +5278,13 @@ module ScriptPreprocessClosure = match packageManagerLines with | [] -> () | (_, _, m)::_ -> - let reportError errorType error = - match errorType with - | ErrorReportType.Warning -> warning(Error(error,m)) - | ErrorReportType.Error -> errorR(Error(error, m)) + let reportError = + let report errorType err msg = + let error = err, msg + match errorType with + | ErrorReportType.Warning -> warning(Error(error, m)) + | ErrorReportType.Error -> errorR(Error(error, m)) + ResolvingErrorReport (report) match origTcConfig.packageManagerLines |> Map.tryFind packageManagerKey with | Some oldDependencyManagerLines when oldDependencyManagerLines = packageManagerLines -> () @@ -5273,22 +5297,23 @@ module ScriptPreprocessClosure = | dependencyManager -> let inline snd3 (_, b, _) = b let packageManagerTextLines = packageManagerLines |> List.map snd3 - match tcConfig.dependencyProvider.Resolve(dependencyManager, tcConfig.implicitIncludeDir, mainFile, scriptName, ".fsx", packageManagerTextLines, reportError, executionTfm) with - | true, _references, generatedScripts, additionalIncludeFolders -> + let result = tcConfig.dependencyProvider.Resolve(dependencyManager, ".fsx", packageManagerTextLines, reportError, executionTfm, tcConfig.implicitIncludeDir, mainFile, scriptName) + match result.Success with + | true -> // Resolution produced no errors - if not (Seq.isEmpty additionalIncludeFolders) then + if not (Seq.isEmpty result.Roots) then let tcConfigB = tcConfig.CloneOfOriginalBuilder - for folder in additionalIncludeFolders do + for folder in result.Roots do tcConfigB.AddIncludePath(m, folder, "") tcConfigB.packageManagerLines <- tcConfigB.packageManagerLines |> Map.map(fun _ l -> l |> List.map(fun (_, p, m) -> true, p, m)) tcConfig <- TcConfig.Create(tcConfigB, validate=false) - for script in generatedScripts do + for script in result.SourceFiles do let scriptText = File.ReadAllText script loadScripts.Add script |> ignore let iSourceText = SourceText.ofString scriptText yield! loop (ClosureSource(script, m, iSourceText, true)) - | false, _, _, _ -> + | false -> // Resolution produced errors update packagerManagerLines entries to note these failure // failed resolutions will no longer be considered let tcConfigB = tcConfig.CloneOfOriginalBuilder diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index 2f26477c61..b854d70b17 100755 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1021,7 +1021,7 @@ lexUnexpectedChar,"Unexpected character '%s'" 1153,lexInvalidFloat,"Invalid floating point number" 1154,lexOusideDecimal,"This number is outside the allowable range for decimal literals" 1155,lexOusideThirtyTwoBitFloat,"This number is outside the allowable range for 32-bit floats" -1156,lexInvalidNumericLiteral,"This is not a valid numeric literal. Valid numeric literals include 1, 0x1, 0b0001 (int), 1u (uint32), 1L (int64), 1UL (uint64), 1s (int16), 1y (sbyte), 1uy (byte), 1.0 (float), 1.0f (float32), 1.0m (decimal), 1I (BigInteger)." +1156,lexInvalidNumericLiteral,"This is not a valid numeric literal. Valid numeric literals include 1, 0x1, 0o1, 0b1, 1l (int), 1u (uint32), 1L (int64), 1UL (uint64), 1s (int16), 1y (sbyte), 1uy (byte), 1.0 (float), 1.0f (float32), 1.0m (decimal), 1I (BigInteger)." 1157,lexInvalidByteLiteral,"This is not a valid byte literal" 1158,lexInvalidCharLiteral,"This is not a valid character literal" 1159,lexThisUnicodeOnlyInStringLiterals,"This Unicode encoding is only valid in string literals" diff --git a/src/fsharp/FSharp.DependencyManager.Nuget/FSDependencyManager.txt b/src/fsharp/FSharp.DependencyManager.Nuget/FSDependencyManager.txt index a163aafc0f..2905c0ec88 100644 --- a/src/fsharp/FSharp.DependencyManager.Nuget/FSDependencyManager.txt +++ b/src/fsharp/FSharp.DependencyManager.Nuget/FSDependencyManager.txt @@ -1,2 +1,5 @@ # FSharp.Build resource strings +cantReferenceSystemPackage,"PackageManager can not reference the System Package '%s'" +requiresAValue,"%s requires a value" +unableToApplyImplicitArgument,"Unable to apply implicit argument number %d" notUsed,"Not used." \ No newline at end of file diff --git a/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.ProjectFile.fs b/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.ProjectFile.fs index 7d9478148f..a4945da4c7 100644 --- a/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.ProjectFile.fs +++ b/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.ProjectFile.fs @@ -187,7 +187,7 @@ $(PACKAGEREFERENCES) diff --git a/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs b/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs index 4f6d78e78c..a5038fd984 100644 --- a/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs +++ b/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs @@ -152,16 +152,16 @@ module internal Utilities = else None - let drainStreamToFile (stream: StreamReader) filename = - use file = File.OpenWrite(filename) - use writer = new StreamWriter(file) + let drainStreamToMemory (stream: StreamReader) = + let mutable list = ResizeArray() let rec copyLines () = match stream.ReadLine() with | null -> () | line -> - writer.WriteLine(line) + list.Add line copyLines () copyLines () + list.ToArray() let executeBuild pathToExe arguments workingDir = match pathToExe with @@ -179,24 +179,24 @@ module internal Utilities = p.StartInfo <- psi p.Start() |> ignore - let standardOutput = Path.Combine(workingDir, "StandardOutput.txt") - let standardError = Path.Combine(workingDir, "StandardError.txt") - drainStreamToFile p.StandardOutput (Path.Combine(workingDir, standardOutput)) - drainStreamToFile p.StandardError (Path.Combine(workingDir, standardError)) + let stdOut = drainStreamToMemory p.StandardOutput + let stdErr = drainStreamToMemory p.StandardError + +#if Debug + File.WriteAllLines(Path.Combine(workingDir, "StandardOutput.txt"), stdOut) + File.WriteAllLines(Path.Combine(workingDir, "StandardError.txt"), stdErr) +#endif p.WaitForExit() + if p.ExitCode <> 0 then //Write StandardError.txt to err stream - let text = File.ReadAllText(standardOutput) - Console.Out.Write(text) - - //Write StandardOutput.txt to out stream - let text = File.ReadAllText(standardError) - Console.Out.Write(text) + for line in stdOut do Console.Out.WriteLine(line) + for line in stdErr do Console.Error.WriteLine(line) - p.ExitCode = 0 + p.ExitCode = 0, stdOut, stdErr - | None -> false + | None -> false, Array.empty, Array.empty let buildProject projectPath binLogPath = let binLoggingArguments = @@ -213,7 +213,7 @@ module internal Utilities = let workingDir = Path.GetDirectoryName projectPath - let succeeded = + let succeeded, stdOut, stdErr = if not (isRunningOnCoreClr) then // The Desktop build uses "msbuild" to build executeBuild msbuildExePath (arguments "") workingDir @@ -223,4 +223,4 @@ module internal Utilities = let outputFile = projectPath + ".resolvedReferences.paths" let resultOutFile = if succeeded && File.Exists(outputFile) then Some outputFile else None - succeeded, resultOutFile + succeeded, stdOut, stdErr, resultOutFile diff --git a/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.fs b/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.fs index bc6bee837f..9848577272 100644 --- a/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.fs +++ b/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.fs @@ -10,7 +10,7 @@ open System.IO open FSharp.DependencyManager.Nuget open FSharp.DependencyManager.Nuget.Utilities open FSharp.DependencyManager.Nuget.ProjectFile - +open FSDependencyManager module FSharpDependencyManager = @@ -43,7 +43,7 @@ module FSharpDependencyManager = let parsePackageReferenceOption (line: string) = let validatePackageName package packageName = if String.Compare(packageName, package, StringComparison.OrdinalIgnoreCase) = 0 then - raise (ArgumentException(sprintf "PackageManager can not reference the System Package '%s'" packageName)) // @@@@@@@@@@@@@@@@@@@@@@@ Globalize me please + raise (ArgumentException(SR.cantReferenceSystemPackage(packageName))) let rec parsePackageReferenceOption' (options: (string option * string option) list) (implicitArgumentCount: int) (packageReference: PackageReference option) = let current = match packageReference with @@ -61,11 +61,11 @@ module FSharpDependencyManager = let setVersion v = Some { current with Version = v } match opt with | Some "include", Some v -> addInclude v |> parsePackageReferenceOption' rest implicitArgumentCount - | Some "include", None -> raise (ArgumentException(sprintf "%s requires a value" "Include")) // @@@@@@@@@@@@@@@@@@@@@@@ Globalize me please + | Some "include", None -> raise (ArgumentException(SR.requiresAValue("Include"))) | Some "version", Some v -> setVersion v |> parsePackageReferenceOption' rest implicitArgumentCount - | Some "version", None -> raise (ArgumentException(sprintf "%s requires a value" "Version")) // @@@@@@@@@@@@@@@@@@@@@@@ Globalize me please + | Some "version", None -> setVersion "*" |> parsePackageReferenceOption' rest implicitArgumentCount | Some "restoresources", Some v -> Some { current with RestoreSources = concat current.RestoreSources v } |> parsePackageReferenceOption' rest implicitArgumentCount - | Some "restoresources", None -> raise (ArgumentException(sprintf "%s requires a value" "RestoreSources")) // @@@@@@@@@@@@@@@@@@@@@@@ Globalize me please + | Some "restoresources", None -> raise (ArgumentException(SR.requiresAValue("RestoreSources"))) | Some "script", Some v -> Some { current with Script = v } |> parsePackageReferenceOption' rest implicitArgumentCount | Some "bl", value -> match value with @@ -88,7 +88,7 @@ module FSharpDependencyManager = match implicitArgumentCount with | 0 -> addInclude v | 1 -> setVersion v - | _ -> raise (ArgumentException(sprintf "Unable to apply implicit argument number %d" (implicitArgumentCount + 1))) // @@@@@@@@@@@@@@@@@@@@@@@ Globalize me please + | _ -> raise (ArgumentException(SR.unableToApplyImplicitArgument(implicitArgumentCount + 1))) |> parsePackageReferenceOption' rest (implicitArgumentCount + 1) | _ -> parsePackageReferenceOption' rest implicitArgumentCount packageReference let options = getOptions line @@ -98,12 +98,34 @@ module FSharpDependencyManager = |> List.distinct |> (fun l -> l, binLogPath) + +/// The results of ResolveDependencies +type ResolveDependenciesResult (success: bool, stdOut: string array, stdError: string array, resolutions: string seq, sourceFiles: string seq, roots: string seq) = + + /// Succeded? + member __.Success = success + + /// The resolution output log + member __.StdOut = stdOut + + /// The resolution error log (* process stderror *) + member __.StdError = stdError + + /// The resolution paths + member __.Resolutions = resolutions + + /// The source code file paths + member __.SourceFiles = sourceFiles + + /// The roots to package directories + member __.Roots = roots + type [] FSharpDependencyManager (outputDir:string option) = let key = "nuget" let name = "MsBuild Nuget DependencyManager" let scriptsPath = - let path = Path.Combine(Path.GetTempPath(), key, Process.GetCurrentProcess().Id.ToString()) + let path = Path.Combine(Path.GetTempPath(), key, Process.GetCurrentProcess().Id.ToString() + "--"+ Guid.NewGuid().ToString()) match outputDir with | None -> path | Some v -> Path.Combine(path, v) @@ -112,8 +134,12 @@ type [] FSharpDependencyManager (outputDir:string op let deleteScripts () = try +#if !Debug if Directory.Exists(scriptsPath) then - () //Directory.Delete(scriptsPath, true) + Directory.Delete(scriptsPath, true) +#else + () +#endif with | _ -> () let deleteAtExit = @@ -136,7 +162,7 @@ type [] FSharpDependencyManager (outputDir:string op member __.Key = key - member __.ResolveDependencies(scriptExt:string, packageManagerTextLines:string seq, tfm: string) : bool * string seq * string seq * string seq = + member __.ResolveDependencies(scriptExt:string, packageManagerTextLines:string seq, tfm: string) : obj = let scriptExt, poundRprefix = match scriptExt with @@ -170,7 +196,7 @@ type [] FSharpDependencyManager (outputDir:string op writeFile projectPath generateProjBody - let result, resolutionsFile = buildProject projectPath binLogPath + let result, stdOut, stdErr, resolutionsFile = buildProject projectPath binLogPath match resolutionsFile with | Some file -> let resolutions = getResolutionsFromFile file @@ -183,10 +209,10 @@ type [] FSharpDependencyManager (outputDir:string op List.concat [ [scriptPath]; loads] |> List.toSeq let includes = (findIncludesFromResolutions resolutions) |> Array.toSeq - result, references, scripts, includes + ResolveDependenciesResult(result, stdOut, stdErr, references, scripts, includes) | None -> let empty = Seq.empty - result, empty, empty, empty + ResolveDependenciesResult(result, stdOut, stdErr, empty, empty, empty) - generateAndBuildProjectArtifacts + generateAndBuildProjectArtifacts :> obj diff --git a/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.fsi b/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.fsi index 38b535bc8b..bdb4791ed9 100644 --- a/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.fsi +++ b/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.fsi @@ -2,14 +2,36 @@ namespace FSharp.DependencyManager.Nuget -open System.Collections.Generic - module internal FSharpDependencyManager = val formatPackageReference: PackageReference -> seq val parsePackageReference: string list -> PackageReference list * string option option + +/// The results of ResolveDependencies +[] +type ResolveDependenciesResult = + + /// Succeded? + member Success: bool + + /// The resolution output log + member StdOut: string array + + /// The resolution error log (* process stderror *) + member StdError: string array + + /// The resolution paths + member Resolutions: string seq + + /// The source code file paths + member SourceFiles: string seq + + /// The roots to package directories + member Roots: string seq + + type [] FSharpDependencyManager = new: outputDir:string option -> FSharpDependencyManager member Name: string member Key:string - member ResolveDependencies: scriptExt:string * packageManagerTextLines:string seq * tfm: string -> bool * string seq * string seq * string seq + member ResolveDependencies: scriptExt:string * packageManagerTextLines:string seq * tfm: string -> obj diff --git a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.cs.xlf b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.cs.xlf index 1b2b789124..c5e96b0cc1 100644 --- a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.cs.xlf +++ b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.cs.xlf @@ -2,11 +2,26 @@ + + PackageManager can not reference the System Package '{0}' + PackageManager can not reference the System Package '{0}' + + Not used. Not used. + + {0} requires a value + {0} requires a value + + + + Unable to apply implicit argument number {0} + Unable to apply implicit argument number {0} + + \ No newline at end of file diff --git a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.de.xlf b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.de.xlf index fad4a92c19..3be8fa52cc 100644 --- a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.de.xlf +++ b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.de.xlf @@ -2,11 +2,26 @@ + + PackageManager can not reference the System Package '{0}' + PackageManager can not reference the System Package '{0}' + + Not used. Not used. + + {0} requires a value + {0} requires a value + + + + Unable to apply implicit argument number {0} + Unable to apply implicit argument number {0} + + \ No newline at end of file diff --git a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.es.xlf b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.es.xlf index af7ece6ad9..b9350a2106 100644 --- a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.es.xlf +++ b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.es.xlf @@ -2,11 +2,26 @@ + + PackageManager can not reference the System Package '{0}' + PackageManager can not reference the System Package '{0}' + + Not used. Not used. + + {0} requires a value + {0} requires a value + + + + Unable to apply implicit argument number {0} + Unable to apply implicit argument number {0} + + \ No newline at end of file diff --git a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.fr.xlf b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.fr.xlf index 42aa351aa0..db01670254 100644 --- a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.fr.xlf +++ b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.fr.xlf @@ -2,11 +2,26 @@ + + PackageManager can not reference the System Package '{0}' + PackageManager can not reference the System Package '{0}' + + Not used. Not used. + + {0} requires a value + {0} requires a value + + + + Unable to apply implicit argument number {0} + Unable to apply implicit argument number {0} + + \ No newline at end of file diff --git a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.it.xlf b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.it.xlf index da872e4f39..c9d68b1285 100644 --- a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.it.xlf +++ b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.it.xlf @@ -2,11 +2,26 @@ + + PackageManager can not reference the System Package '{0}' + PackageManager can not reference the System Package '{0}' + + Not used. Not used. + + {0} requires a value + {0} requires a value + + + + Unable to apply implicit argument number {0} + Unable to apply implicit argument number {0} + + \ No newline at end of file diff --git a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.ja.xlf b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.ja.xlf index e9e3444fcf..0d7cb1551c 100644 --- a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.ja.xlf +++ b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.ja.xlf @@ -2,11 +2,26 @@ + + PackageManager can not reference the System Package '{0}' + PackageManager can not reference the System Package '{0}' + + Not used. Not used. + + {0} requires a value + {0} requires a value + + + + Unable to apply implicit argument number {0} + Unable to apply implicit argument number {0} + + \ No newline at end of file diff --git a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.ko.xlf b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.ko.xlf index 13f0865b93..0d6afc0342 100644 --- a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.ko.xlf +++ b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.ko.xlf @@ -2,11 +2,26 @@ + + PackageManager can not reference the System Package '{0}' + PackageManager can not reference the System Package '{0}' + + Not used. Not used. + + {0} requires a value + {0} requires a value + + + + Unable to apply implicit argument number {0} + Unable to apply implicit argument number {0} + + \ No newline at end of file diff --git a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.pl.xlf b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.pl.xlf index e9718434a1..4534d68b47 100644 --- a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.pl.xlf +++ b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.pl.xlf @@ -2,11 +2,26 @@ + + PackageManager can not reference the System Package '{0}' + PackageManager can not reference the System Package '{0}' + + Not used. Not used. + + {0} requires a value + {0} requires a value + + + + Unable to apply implicit argument number {0} + Unable to apply implicit argument number {0} + + \ No newline at end of file diff --git a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.pt-BR.xlf b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.pt-BR.xlf index 6fb3374c22..788b984148 100644 --- a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.pt-BR.xlf +++ b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.pt-BR.xlf @@ -2,11 +2,26 @@ + + PackageManager can not reference the System Package '{0}' + PackageManager can not reference the System Package '{0}' + + Not used. Not used. + + {0} requires a value + {0} requires a value + + + + Unable to apply implicit argument number {0} + Unable to apply implicit argument number {0} + + \ No newline at end of file diff --git a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.ru.xlf b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.ru.xlf index 98624d7996..4d363d34d3 100644 --- a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.ru.xlf +++ b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.ru.xlf @@ -2,11 +2,26 @@ + + PackageManager can not reference the System Package '{0}' + PackageManager can not reference the System Package '{0}' + + Not used. Not used. + + {0} requires a value + {0} requires a value + + + + Unable to apply implicit argument number {0} + Unable to apply implicit argument number {0} + + \ No newline at end of file diff --git a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.tr.xlf b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.tr.xlf index a94d8e068e..db70fe17c1 100644 --- a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.tr.xlf +++ b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.tr.xlf @@ -2,11 +2,26 @@ + + PackageManager can not reference the System Package '{0}' + PackageManager can not reference the System Package '{0}' + + Not used. Not used. + + {0} requires a value + {0} requires a value + + + + Unable to apply implicit argument number {0} + Unable to apply implicit argument number {0} + + \ No newline at end of file diff --git a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.zh-Hans.xlf b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.zh-Hans.xlf index 44e599a414..b18683e476 100644 --- a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.zh-Hans.xlf +++ b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.zh-Hans.xlf @@ -2,11 +2,26 @@ + + PackageManager can not reference the System Package '{0}' + PackageManager can not reference the System Package '{0}' + + Not used. Not used. + + {0} requires a value + {0} requires a value + + + + Unable to apply implicit argument number {0} + Unable to apply implicit argument number {0} + + \ No newline at end of file diff --git a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.zh-Hant.xlf b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.zh-Hant.xlf index 83fa3521e6..5f1d2a019e 100644 --- a/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.zh-Hant.xlf +++ b/src/fsharp/FSharp.DependencyManager.Nuget/xlf/FSDependencyManager.txt.zh-Hant.xlf @@ -2,11 +2,26 @@ + + PackageManager can not reference the System Package '{0}' + PackageManager can not reference the System Package '{0}' + + Not used. Not used. + + {0} requires a value + {0} requires a value + + + + Unable to apply implicit argument number {0} + Unable to apply implicit argument number {0} + + \ No newline at end of file diff --git a/src/fsharp/Interactive.DependencyManager/AssemblyResolveHandler.fs b/src/fsharp/Interactive.DependencyManager/AssemblyResolveHandler.fs new file mode 100644 index 0000000000..bc40a0dfd7 --- /dev/null +++ b/src/fsharp/Interactive.DependencyManager/AssemblyResolveHandler.fs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Interactive.DependencyManager + +open System +open System.Collections.Generic +open System.IO +open System.Reflection + +#if NETSTANDARD +open System.Runtime.Loader +#endif + +/// Signature for ResolutionProbe callback +/// host implements this, it's job is to return a list of assembly paths to probe. +type AssemblyResolutionProbe = delegate of Unit -> IEnumerable + + +/// Type that encapsulates AssemblyResolveHandler for managed packages +type AssemblyResolveHandler (assemblyProbingPaths: AssemblyResolutionProbe) = + +#if NETSTANDARD + let resolveAssemblyNetStandard (ctxt: AssemblyLoadContext) (assemblyName: AssemblyName): Assembly = + + let loadAssembly path = + ctxt.LoadFromAssemblyPath(path) + +#else + let resolveAssemblyNET (assemblyName: AssemblyName): Assembly = + + let loadAssembly assemblyPath = + Assembly.LoadFrom(assemblyPath) + +#endif + + let assemblyPaths = + match assemblyProbingPaths with + | null -> Seq.empty + | _ -> assemblyProbingPaths.Invoke() + + try + // args.Name is a displayname formatted assembly version. + // E.g: "System.IO.FileSystem, Version=4.1.1.0, Culture=en-US, PublicKeyToken=b03f5f7f11d50a3a" + let simpleName = assemblyName.Name + let assemblyPathOpt = assemblyPaths |> Seq.tryFind(fun path -> Path.GetFileNameWithoutExtension(path) = simpleName) + match assemblyPathOpt with + | Some path -> + loadAssembly path + | None -> Unchecked.defaultof + + with | _ -> Unchecked.defaultof + +#if NETSTANDARD + let handler = Func(resolveAssemblyNetStandard) + do AssemblyLoadContext.Default.add_Resolving(handler) + + interface IDisposable with + member _x.Dispose() = + AssemblyLoadContext.Default.remove_Resolving(handler) + +#else + let handler = new ResolveEventHandler(fun _ (args: ResolveEventArgs) -> resolveAssemblyNET (new AssemblyName(args.Name))) + do AppDomain.CurrentDomain.add_AssemblyResolve(handler) + + interface IDisposable with + member _x.Dispose() = + AppDomain.CurrentDomain.remove_AssemblyResolve(handler) + +#endif diff --git a/src/fsharp/Interactive.DependencyManager/AssemblyResolveHandler.fsi b/src/fsharp/Interactive.DependencyManager/AssemblyResolveHandler.fsi new file mode 100644 index 0000000000..b2314e0d31 --- /dev/null +++ b/src/fsharp/Interactive.DependencyManager/AssemblyResolveHandler.fsi @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Interactive.DependencyManager + +open System +open System.Collections.Generic + +/// Signature for ResolutionProbe callback +/// host implements this, it's job is to return a list of assembly paths to probe. +type AssemblyResolutionProbe = delegate of Unit -> IEnumerable + + +/// Handle Assembly resolution +type AssemblyResolveHandler = + + /// Construct a new DependencyProvider + new: assemblyProbingPaths: AssemblyResolutionProbe -> AssemblyResolveHandler + + interface IDisposable diff --git a/src/fsharp/Interactive.DependencyManager/DependencyManager.fsi b/src/fsharp/Interactive.DependencyManager/DependencyManager.fsi deleted file mode 100644 index b1163c0597..0000000000 --- a/src/fsharp/Interactive.DependencyManager/DependencyManager.fsi +++ /dev/null @@ -1,53 +0,0 @@ - -// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -/// Helper members to integrate DependencyManagers into F# codebase -namespace Interactive.DependencyManager - -open System - -/// Wraps access to a DependencyManager implementation -[] -type IDependencyManagerProvider = - /// Name of the dependency manager - abstract Name: string - - /// Key that identifies the types of dependencies that this DependencyManager operates on - /// E.g - /// nuget: indicates that this DM is for nuget packages - /// paket: indicates that this DM is for paket scripts, which manage nuget packages, github source dependencies etc ... - abstract Key: string - - /// Resolve the dependencies, for the given set of arguments, go find the .dll references, scripts and additional include values. - abstract ResolveDependencies: scriptDir: string * mainScriptName: string * scriptName: string * scriptExt: string * packageManagerTextLines: string seq * tfm: string -> bool * string seq * string seq * string seq - -/// Indicates to the Error reporting callbacks, severity of the error -[] -type ErrorReportType = - | Warning - | Error - - -/// Provides DependencyManagement functions. -/// Class is IDisposable -type DependencyProvider = - interface System.IDisposable - - /// Construct a new DependencyProvider - new : unit -> DependencyProvider - - /// Returns a formatted error message for the host to present - member CreatePackageManagerUnknownError : compilerTools:seq * outputDir:string * packageManagerKey:string * reportError:(ErrorReportType ->int * string -> unit) -> int * string - - /// Remove the dependency mager with the specified key - member RemoveDependencyManagerKey : packageManagerKey:string * path:string -> string - - /// Resolve reference for a list of package manager lines - member Resolve : packageManager:IDependencyManagerProvider * implicitIncludeDir:string * mainScriptName:string * fileName:string * scriptExt:string * packageManagerTextLines:seq * reportError:(ErrorReportType -> int * string -> unit) * executionTfm:string -> bool * seq * seq * seq - - /// Fetch a dependencymanager that supports a specific key - member TryFindDependencyManagerByKey : compilerTools:seq * outputDir:string * reportError:(ErrorReportType -> int * string -> unit) * key:string -> IDependencyManagerProvider - - /// TryFindDependencyManagerInPath - given a #r "key:sometext" go and find a DependencyManager that satisfies the key - member TryFindDependencyManagerInPath : compilerTools:seq * outputDir:string * reportError:(ErrorReportType -> int * string -> unit) * path:string -> string * IDependencyManagerProvider - diff --git a/src/fsharp/Interactive.DependencyManager/DependencyManager.fs b/src/fsharp/Interactive.DependencyManager/DependencyProvider.fs similarity index 54% rename from src/fsharp/Interactive.DependencyManager/DependencyManager.fs rename to src/fsharp/Interactive.DependencyManager/DependencyProvider.fs index 6434d3ff5e..306532a95f 100644 --- a/src/fsharp/Interactive.DependencyManager/DependencyManager.fs +++ b/src/fsharp/Interactive.DependencyManager/DependencyProvider.fs @@ -5,7 +5,7 @@ namespace Interactive.DependencyManager open System open System.IO open System.Reflection -open FSharp.Reflection +open System.Runtime.InteropServices open Internal.Utilities.FSharpEnvironment module ReflectionHelper = @@ -14,6 +14,7 @@ module ReflectionHelper = let resolveDependenciesMethodName = "ResolveDependencies" let namePropertyName = "Name" let keyPropertyName = "Key" + let arrEmpty = Array.empty let seqEmpty = Seq.empty let assemblyHasAttribute (theAssembly: Assembly) attributeName = @@ -30,7 +31,7 @@ module ReflectionHelper = let getInstanceProperty<'treturn> (theType: Type) propertyName = try - let property = theType.GetProperty(propertyName, typeof<'treturn>) + let property = theType.GetProperty(propertyName, BindingFlags.Public ||| BindingFlags.NonPublic ||| BindingFlags.Instance, Unchecked.defaultof, typeof<'treturn>, Array.empty, Array.empty) if isNull property then None elif not (property.GetGetMethod().IsStatic) @@ -57,19 +58,48 @@ module ReflectionHelper = | _ -> e open ReflectionHelper +open Microsoft.FSharp.Reflection -(* Shape of Dependency Manager contract, resolved using reflection *) -[] -type IDependencyManagerProvider = - abstract Name: string - abstract Key: string - abstract ResolveDependencies: scriptDir: string * mainScriptName: string * scriptName: string * scriptExt: string * packageManagerTextLines: string seq * tfm: string -> bool * string seq * string seq * string seq +/// Indicate the type of error to report [] type ErrorReportType = | Warning | Error + +type ResolvingErrorReport = delegate of ErrorReportType * int * string -> unit + + +(* Shape of Dependency Manager contract, resolved using reflection *) +/// The results of ResolveDependencies +type IResolveDependenciesResult = + + /// Succeded? + abstract Success: bool + + /// The resolution output log + abstract StdOut: string array + + /// The resolution error log (* process stderror *) + abstract StdError: string array + + /// The resolution paths + abstract Resolutions: string seq + + /// The source code file paths + abstract SourceFiles: string seq + + /// The roots to package directories + abstract Roots: string seq + + +[] +type IDependencyManagerProvider = + abstract Name: string + abstract Key: string + abstract ResolveDependencies: scriptDir: string * mainScriptName: string * scriptName: string * scriptExt: string * packageManagerTextLines: string seq * tfm: string -> IResolveDependenciesResult + type ReflectionDependencyManagerProvider(theType: Type, nameProperty: PropertyInfo, keyProperty: PropertyInfo, resolveDeps: MethodInfo option, resolveDepsEx: MethodInfo option,outputDir: string option) = let instance = Activator.CreateInstance(theType, [|outputDir :> obj|]) let nameProperty = nameProperty.GetValue >> string @@ -88,6 +118,67 @@ type ReflectionDependencyManagerProvider(theType: Type, nameProperty: PropertyIn let resolveMethodEx = getInstanceMethod theType [| typeof; typeof; typeof |] resolveDependenciesMethodName Some (fun () -> new ReflectionDependencyManagerProvider(theType, nameProperty, keyProperty, resolveMethod, resolveMethodEx, outputDir) :> IDependencyManagerProvider) + static member MakeResultFromObject(result: obj) = { + new IResolveDependenciesResult with + /// Succeded? + member __.Success = + match getInstanceProperty (result.GetType()) "Success" with + | None -> false + | Some p -> p.GetValue(result) :?> bool + + /// The resolution output log + member __.StdOut = + match getInstanceProperty (result.GetType()) "StdOut" with + | None -> Array.empty + | Some p -> p.GetValue(result) :?> string array + + /// The resolution error log (* process stderror *) + member __.StdError = + match getInstanceProperty (result.GetType()) "StdError" with + | None -> Array.empty + | Some p -> p.GetValue(result) :?> string array + + /// The resolution paths + member __.Resolutions = + match getInstanceProperty (result.GetType()) "Resolutions" with + | None -> Seq.empty + | Some p -> p.GetValue(result) :?> string seq + + /// The source code file paths + member __.SourceFiles = + match getInstanceProperty (result.GetType()) "SourceFiles" with + | None -> Seq.empty + | Some p -> p.GetValue(result) :?> string seq + + /// The roots to package directories + member __.Roots = + match getInstanceProperty (result.GetType()) "Roots" with + | None -> Seq.empty + | Some p -> p.GetValue(result) :?> string seq + } + + static member MakeResultFromFields(success: bool, stdOut: string array, stdError: string array, resolutions: string seq, sourceFiles: string seq, roots: string seq) = { + new IResolveDependenciesResult with + /// Succeded? + member __.Success = success + + /// The resolution output log + member __.StdOut = stdOut + + /// The resolution error log (* process stderror *) + member __.StdError = stdError + + /// The resolution paths + member __.Resolutions = resolutions + + /// The source code file paths + member __.SourceFiles = sourceFiles + + /// The roots to package directories + member __.Roots = roots + } + + interface IDependencyManagerProvider with /// Name of dependency Manager @@ -97,8 +188,7 @@ type ReflectionDependencyManagerProvider(theType: Type, nameProperty: PropertyIn member __.Key = instance |> keyProperty /// Resolve the dependencies for the given arguments - /// - member this.ResolveDependencies(scriptDir, mainScriptName, scriptName, scriptExt, packageManagerTextLines, tfm): bool * string seq * string seq * string seq = + member this.ResolveDependencies(scriptDir, mainScriptName, scriptName, scriptExt, packageManagerTextLines, tfm): IResolveDependenciesResult = // The ResolveDependencies method, has two signatures, the original signaature in the variable resolveDeps and the updated signature resoveDepsEx // The resolve method can return values in two different tuples: @@ -115,27 +205,37 @@ type ReflectionDependencyManagerProvider(theType: Type, nameProperty: PropertyIn else None, [||] - let succeeded, references, generatedScripts, additionalIncludeFolders = - let result = - match method with - | Some m -> m.Invoke(instance, arguments) :?> _ - | None -> false, seqEmpty, seqEmpty, seqEmpty + match method with + | Some m -> + let result = m.Invoke(instance, arguments) // Verify the number of arguments returned in the tuple returned by resolvedependencies, it can be: - // 4 - (bool * string list * string list * string list) + // 1 - object with properties // 3 - (bool * string list * string list) - let tupleFields = result |> FSharpValue.GetTupleFields - match tupleFields |> Array.length with - | 4 -> tupleFields.[0] :?> bool, tupleFields.[1] :?> string seq, tupleFields.[2] :?> string seq, tupleFields.[3] :?> string seq - | 3 -> tupleFields.[0] :?> bool, seqEmpty, tupleFields.[1] :?> string list |> List.toSeq, tupleFields.[2] :?> string list |> List.toSeq - | _ -> false, seqEmpty, seqEmpty, seqEmpty - succeeded, references, generatedScripts, additionalIncludeFolders + // Support legacy api return shape (bool, string seq, string seq) --- original paket packagemanager + if Microsoft.FSharp.Reflection.FSharpType.IsTuple (result.GetType()) then + // Verify the number of arguments returned in the tuple returned by resolvedependencies, it can be: + // 3 - (bool * string list * string list) + let success, sourceFiles, packageRoots = + let tupleFields = result |> FSharpValue.GetTupleFields + match tupleFields |> Array.length with + | 3 -> tupleFields.[0] :?> bool, tupleFields.[1] :?> string list |> List.toSeq, tupleFields.[2] :?> string list |> List.toSeq + | _ -> false, seqEmpty, seqEmpty + ReflectionDependencyManagerProvider.MakeResultFromFields(success, Array.empty, Array.empty, Seq.empty, sourceFiles, packageRoots) + else + ReflectionDependencyManagerProvider.MakeResultFromObject(result) + + | None -> + ReflectionDependencyManagerProvider.MakeResultFromFields(false, Array.empty, Array.empty, Seq.empty, Seq.empty, Seq.empty) /// Provides DependencyManagement functions. /// Class is IDisposable -type DependencyProvider () = +type DependencyProvider (assemblyProbingPaths: AssemblyResolutionProbe, nativeProbingRoots: NativeResolutionProbe) = + + let dllResolveHandler = new NativeDllResolveHandler(nativeProbingRoots) :> IDisposable + let assemblyResolveHandler = new AssemblyResolveHandler(assemblyProbingPaths) :> IDisposable // Resolution Path = Location of FSharp.Compiler.Private.dll let assemblySearchPaths = lazy ( @@ -145,7 +245,7 @@ type DependencyProvider () = yield AppDomain.CurrentDomain.BaseDirectory ]) - let enumerateDependencyManagerAssemblies compilerTools reportError = + let enumerateDependencyManagerAssemblies compilerTools (reportError: ResolvingErrorReport) = getCompilerToolsDesignTimeAssemblyPaths compilerTools |> Seq.append (assemblySearchPaths.Force()) |> Seq.collect (fun path -> @@ -159,13 +259,14 @@ type DependencyProvider () = with | e -> let e = stripTieWrapper e - reportError ErrorReportType.Warning (InteractiveDependencyManager.SR.couldNotLoadDependencyManagerExtension(path,e.Message)) + let n, m = InteractiveDependencyManager.SR.couldNotLoadDependencyManagerExtension(path,e.Message) + reportError.Invoke(ErrorReportType.Warning, n, m) None) |> Seq.filter (fun a -> assemblyHasAttribute a dependencyManagerAttributeName) let mutable registeredDependencyManagers: Map option= None - let RegisteredDependencyManagers (compilerTools: string seq) (outputDir: string option) (reportError: ErrorReportType -> int * string -> unit) = + let RegisteredDependencyManagers (compilerTools: string seq) (outputDir: string option) (reportError: ResolvingErrorReport) = match registeredDependencyManagers with | Some managers -> managers @@ -191,22 +292,26 @@ type DependencyProvider () = None managers - /// Returns a formatted error message for the host to present - member _.CreatePackageManagerUnknownError(compilerTools: string seq, outputDir: string, packageManagerKey: string, reportError: ErrorReportType -> int * string -> unit) = + /// Returns a formatted error message for the host to presentconstruct with just nativeProbing handler + new (nativeProbingRoots: NativeResolutionProbe) = + new DependencyProvider(Unchecked.defaultof, nativeProbingRoots) + /// Returns a formatted error message for the host to present + member __.CreatePackageManagerUnknownError (compilerTools: string seq, outputDir: string, packageManagerKey: string, reportError: ResolvingErrorReport) = let registeredKeys = String.Join(", ", RegisteredDependencyManagers compilerTools (Option.ofString outputDir) reportError |> Seq.map (fun kv -> kv.Value.Key)) let searchPaths = assemblySearchPaths.Force() InteractiveDependencyManager.SR.packageManagerUnknown(packageManagerKey, String.Join(", ", searchPaths, compilerTools), registeredKeys) /// Fetch a dependencymanager that supports a specific key - member this.TryFindDependencyManagerInPath (compilerTools: string seq, outputDir: string, reportError: ErrorReportType -> int * string -> unit, path: string): string * IDependencyManagerProvider = + member this.TryFindDependencyManagerInPath (compilerTools: string seq, outputDir: string, reportError: ResolvingErrorReport, path: string): string * IDependencyManagerProvider = try if path.Contains ":" && not (Path.IsPathRooted path) then let managers = RegisteredDependencyManagers compilerTools (Option.ofString outputDir) reportError match managers |> Seq.tryFind (fun kv -> path.StartsWith(kv.Value.Key + ":" )) with | None -> - reportError ErrorReportType.Error (this.CreatePackageManagerUnknownError(compilerTools, outputDir, (path.Split(':').[0]), reportError)) + let err, msg = this.CreatePackageManagerUnknownError(compilerTools, outputDir, (path.Split(':').[0]), reportError) + reportError.Invoke(ErrorReportType.Error, err, msg) null, Unchecked.defaultof | Some kv -> path, kv.Value @@ -215,16 +320,17 @@ type DependencyProvider () = with | e -> let e = stripTieWrapper e - reportError ErrorReportType.Error (InteractiveDependencyManager.SR.packageManagerError(e.Message)) + let err, msg = InteractiveDependencyManager.SR.packageManagerError(e.Message) + reportError.Invoke(ErrorReportType.Error, err, msg) null, Unchecked.defaultof /// Remove the dependency mager with the specified key - member _.RemoveDependencyManagerKey(packageManagerKey:string, path:string): string = + member __.RemoveDependencyManagerKey(packageManagerKey:string, path:string): string = path.Substring(packageManagerKey.Length + 1).Trim() /// Fetch a dependencymanager that supports a specific key - member _.TryFindDependencyManagerByKey (compilerTools: string seq, outputDir: string, reportError: ErrorReportType -> int * string -> unit, key: string): IDependencyManagerProvider = + member __.TryFindDependencyManagerByKey (compilerTools: string seq, outputDir: string, reportError: ResolvingErrorReport, key: string): IDependencyManagerProvider = try RegisteredDependencyManagers compilerTools (Option.ofString outputDir) reportError @@ -234,21 +340,34 @@ type DependencyProvider () = with | e -> let e = stripTieWrapper e - reportError ErrorReportType.Error (InteractiveDependencyManager.SR.packageManagerError(e.Message)) + let err, msg = InteractiveDependencyManager.SR.packageManagerError(e.Message) + reportError.Invoke(ErrorReportType.Error, err, msg) Unchecked.defaultof /// Resolve reference for a list of package manager lines - member _.Resolve (packageManager:IDependencyManagerProvider, implicitIncludeDir: string, mainScriptName: string, fileName: string, scriptExt: string, packageManagerTextLines: string seq, reportError: ErrorReportType -> int * string -> unit, executionTfm: string): bool * string seq * string seq * string seq = + member __.Resolve (packageManager:IDependencyManagerProvider, + scriptExt: string, + packageManagerTextLines: string seq, + reportError: ResolvingErrorReport, + executionTfm: string, + []implicitIncludeDir: string, + []mainScriptName: string, + []fileName: string): IResolveDependenciesResult = try packageManager.ResolveDependencies(implicitIncludeDir, mainScriptName, fileName, scriptExt, packageManagerTextLines, executionTfm) with e -> let e = stripTieWrapper e - reportError ErrorReportType.Error (InteractiveDependencyManager.SR.packageManagerError(e.Message)) - false, Seq.empty, Seq.empty, Seq.empty + let err, msg = (InteractiveDependencyManager.SR.packageManagerError(e.Message)) + reportError.Invoke(ErrorReportType.Error, err, msg) + ReflectionDependencyManagerProvider.MakeResultFromFields(false, arrEmpty, arrEmpty, seqEmpty, seqEmpty, seqEmpty) interface IDisposable with - member _.Dispose() = + + member __.Dispose() = + // Unregister everything registeredDependencyManagers <- None + dllResolveHandler.Dispose() + assemblyResolveHandler.Dispose() diff --git a/src/fsharp/Interactive.DependencyManager/DependencyProvider.fsi b/src/fsharp/Interactive.DependencyManager/DependencyProvider.fsi new file mode 100644 index 0000000000..742bb35fb5 --- /dev/null +++ b/src/fsharp/Interactive.DependencyManager/DependencyProvider.fsi @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +/// Helper members to integrate DependencyManagers into F# codebase +namespace Interactive.DependencyManager + +open System +open System.Runtime.InteropServices + + +/// The results of ResolveDependencies +type IResolveDependenciesResult = + + /// Succeded? + abstract Success: bool + + /// The resolution output log + abstract StdOut: string array + + /// The resolution error log (* process stderror *) + abstract StdError: string array + + /// The resolution paths + abstract Resolutions: string seq + + /// The source code file paths + abstract SourceFiles: string seq + + /// The roots to package directories + abstract Roots: string seq + + +/// Wraps access to a DependencyManager implementation +[] +type IDependencyManagerProvider = + + /// Name of the dependency manager + abstract Name: string + + /// Key that identifies the types of dependencies that this DependencyManager operates on + /// E.g + /// nuget: indicates that this DM is for nuget packages + /// paket: indicates that this DM is for paket scripts, which manage nuget packages, github source dependencies etc ... + abstract Key: string + + /// Resolve the dependencies, for the given set of arguments, go find the .dll references, scripts and additional include values. + abstract ResolveDependencies: scriptDir: string * mainScriptName: string * scriptName: string * scriptExt: string * packageManagerTextLines: string seq * tfm: string -> IResolveDependenciesResult + + +/// Todo describe this API +[] +type ErrorReportType = + | Warning + | Error + + +type ResolvingErrorReport = delegate of ErrorReportType * int * string -> unit + + +/// Provides DependencyManagement functions. +/// Class is IDisposable +type DependencyProvider = + interface System.IDisposable + + /// Construct a new DependencyProvider + new: assemblyProbingPaths: AssemblyResolutionProbe * nativeProbingRoots: NativeResolutionProbe -> DependencyProvider + new: nativeProbingRoots: NativeResolutionProbe -> DependencyProvider + + /// Returns a formatted error message for the host to present + member CreatePackageManagerUnknownError: string seq * string * string * ResolvingErrorReport -> int * string + + /// Remove the dependency mager with the specified key + member RemoveDependencyManagerKey: packageManagerKey: string * path: string -> string + + /// Resolve reference for a list of package manager lines + member Resolve : packageManager: IDependencyManagerProvider * scriptExt: string * packageManagerTextLines: string seq * reportError: ResolvingErrorReport * executionTfm: string * []implicitIncludeDir: string * []mainScriptName: string * []fileName: string -> IResolveDependenciesResult + + /// Fetch a dependencymanager that supports a specific key + member TryFindDependencyManagerByKey: compilerTools: string seq * outputDir: string * reportError: ResolvingErrorReport * key: string -> IDependencyManagerProvider + + /// TryFindDependencyManagerInPath - given a #r "key:sometext" go and find a DependencyManager that satisfies the key + member TryFindDependencyManagerInPath: compilerTools: string seq * outputDir: string * reportError: ResolvingErrorReport * path: string -> string * IDependencyManagerProvider diff --git a/src/fsharp/Interactive.DependencyManager/Interactive.DependencyManager.fsproj b/src/fsharp/Interactive.DependencyManager/Interactive.DependencyManager.fsproj index fdf516baf5..901a3ba65a 100644 --- a/src/fsharp/Interactive.DependencyManager/Interactive.DependencyManager.fsproj +++ b/src/fsharp/Interactive.DependencyManager/Interactive.DependencyManager.fsproj @@ -4,7 +4,8 @@ Library - netstandard2.0 + net472;netstandard2.0 + netstandard2.0 Interactive.DependencyManager $(NoWarn);45;55;62;75;1204 true @@ -23,8 +24,13 @@ - - + + + + + + + @@ -49,6 +55,7 @@ + diff --git a/src/fsharp/Interactive.DependencyManager/NativeDllResolveHandler.fs b/src/fsharp/Interactive.DependencyManager/NativeDllResolveHandler.fs new file mode 100644 index 0000000000..ee15f5cb31 --- /dev/null +++ b/src/fsharp/Interactive.DependencyManager/NativeDllResolveHandler.fs @@ -0,0 +1,129 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Interactive.DependencyManager + +open System +open System.Collections.Generic +open System.IO +open System.Reflection +open System.Runtime.InteropServices + +#if NETSTANDARD +open System.Runtime.Loader + +// Cut down AssemblyLoadContext, for loading native libraries +type NativeAssemblyLoadContext () = + inherit AssemblyLoadContext() + + member this.LoadNativeLibrary(path: string): IntPtr = + base.LoadUnmanagedDllFromPath(path) + + override _.Load(_path: AssemblyName): Assembly = + raise (NotImplementedException()) + + static member NativeLoadContext = new NativeAssemblyLoadContext() +#endif + + +/// Signature for Native library resolution probe callback +/// host implements this, it's job is to return a list of package roots to probe. +type NativeResolutionProbe = delegate of Unit -> IEnumerable + + +/// Type that encapsulates Native library probing for managed packages +type NativeDllResolveHandler (_nativeProbingRoots: NativeResolutionProbe) = +#if NETSTANDARD + let probingFileNames (name: string) = + // coreclr native library probing algorithm: https://github.com/dotnet/coreclr/blob/9773db1e7b1acb3ec75c9cc0e36bd62dcbacd6d5/src/System.Private.CoreLib/shared/System/Runtime/Loader/LibraryNameVariation.Unix.cs + let isRooted = Path.IsPathRooted name + let useSuffix s = not (name.Contains(s + ".") || name.EndsWith(s)) // linux devs often append version # to libraries I.e mydll.so.5.3.2 + let usePrefix = name.IndexOf(Path.DirectorySeparatorChar) = -1 // If name has directory information no add no prefix + && name.IndexOf(Path.AltDirectorySeparatorChar) = -1 + && name.IndexOf(Path.PathSeparator) = -1 + && name.IndexOf(Path.VolumeSeparatorChar) = -1 + let prefix = [| "lib" |] + let suffix = [| + if RuntimeInformation.IsOSPlatform(OSPlatform.Windows) then + ".dll" + ".exe" + elif RuntimeInformation.IsOSPlatform(OSPlatform.OSX) then + ".dylib" + else + ".so" + |] + + [| + yield name // Bare name + if not (isRooted) then + for s in suffix do + if useSuffix s then // Suffix without prefix + yield (sprintf "%s%s" name s) + if usePrefix then + for p in prefix do // Suffix with prefix + yield (sprintf "%s%s%s" p name s) + elif usePrefix then + for p in prefix do // Prefix + yield (sprintf "%s%s" p name) + |] + + // Computer valid dotnet-rids for this environment: + // https://docs.microsoft.com/en-us/dotnet/core/rid-catalog + // + // Where rid is: win, win-x64, win-x86, osx-x64, linux-x64 etc ... + let probingRids = + let processArchitecture = RuntimeInformation.ProcessArchitecture + let baseRid = + if RuntimeInformation.IsOSPlatform(OSPlatform.Windows) then "win" + elif RuntimeInformation.IsOSPlatform(OSPlatform.OSX) then "osx" + else "linux" + let platformRid = + match processArchitecture with + | Architecture.X64 -> baseRid + "-x64" + | Architecture.X86 -> baseRid + "-x86" + | Architecture.Arm64 -> baseRid + "-arm64" + | _ -> baseRid + "arm" + [| "any"; baseRid; platformRid |] + + let _resolveUnmanagedDll (_: Assembly) (name: string): IntPtr = + + // Enumerate probing roots looking for a dll that matches the probing name in the probed locations + let probeForNativeLibrary root rid name = + // Look for name in root + probingFileNames name |> Array.tryPick(fun name -> + let path = Path.Combine(root, "runtimes", rid, "native", name) + if File.Exists(path) then + Some path + else + None) + + let probe = + match _nativeProbingRoots with + | null -> None + | _ -> _nativeProbingRoots.Invoke() + |> Seq.tryPick(fun root -> + probingFileNames name |> Seq.tryPick(fun name -> + let path = Path.Combine(root, name) + if File.Exists(path) then + Some path + else + probingRids |> Seq.tryPick(fun rid -> probeForNativeLibrary root rid name))) + + match probe with + | Some path -> NativeAssemblyLoadContext.NativeLoadContext.LoadNativeLibrary(path) + | None -> IntPtr.Zero + + // netstandard 2.1 has this property, unfortunately we don't build with that yet + //public event Func ResolvingUnmanagedDll + let eventInfo = typeof.GetEvent("ResolvingUnmanagedDll") + let handler = Func (_resolveUnmanagedDll) + + do if not (isNull eventInfo) then eventInfo.AddEventHandler(AssemblyLoadContext.Default, handler) +#endif + + interface IDisposable with + member _x.Dispose() = +#if NETSTANDARD + if not (isNull eventInfo) then + eventInfo.RemoveEventHandler(AssemblyLoadContext.Default, handler) +#endif + () \ No newline at end of file diff --git a/src/fsharp/Interactive.DependencyManager/NativeDllResolveHandler.fsi b/src/fsharp/Interactive.DependencyManager/NativeDllResolveHandler.fsi new file mode 100644 index 0000000000..233632b678 --- /dev/null +++ b/src/fsharp/Interactive.DependencyManager/NativeDllResolveHandler.fsi @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Interactive.DependencyManager + +open System +open System.Collections.Generic + +/// Signature for Native library resolution probe callback +/// host implements this, it's job is to return a list of package roots to probe. +type NativeResolutionProbe = delegate of Unit -> IEnumerable + + +// Cut down AssemblyLoadContext, for loading native libraries +type NativeDllResolveHandler = + + /// Construct a new DependencyProvider + new: nativeProbingRoots: NativeResolutionProbe -> NativeDllResolveHandler + + interface IDisposable diff --git a/src/fsharp/MethodCalls.fs b/src/fsharp/MethodCalls.fs index 341664d873..5f55249644 100644 --- a/src/fsharp/MethodCalls.fs +++ b/src/fsharp/MethodCalls.fs @@ -189,7 +189,7 @@ let AdjustCalledArgTypeForOptionals (g: TcGlobals) enforceNullableOptionalsKnown // CSharpMethod(x = arg), optional C#-style argument, may have type Nullable. // The arg should have type ty. However for backwards compat, we also allow arg to have type Nullable | CallerSide _ -> - if isNullableTy g calledArgTy && g.langVersion.SupportsFeature LanguageFeature.NullableOptionalInterop then + if isNullableTy g calledArgTy && g.langVersion.SupportsFeature LanguageFeature.NullableOptionalInterop then // If inference has worked out it's a nullable then use this if isNullableTy g callerArg.CallerArgumentType then calledArgTy @@ -1186,11 +1186,6 @@ let AdjustCallerArgForOptional tcFieldInit eCallerMemberName (infoReader: InfoRe let minfo = GetIntrinsicConstructorInfosOfType infoReader m calledArgTy |> List.head let callerArgExprCoerced = mkCoerceIfNeeded g calledNonOptTy callerArgTy callerArgExpr MakeMethInfoCall amap m minfo [] [callerArgExprCoerced] - elif isOptionTy g calledArgTy then - // CSharpMethod(x=b) when 'b' has nullable type and 'x' has optional type --> CSharpMethod(Some b.Value) - let calledNonOptTy = destOptionTy g calledArgTy - let callerArgExprCoerced = mkCoerceIfNeeded g calledNonOptTy callerArgTy callerArgExpr - mkSome g (destNullableTy g callerArgTy) callerArgExprCoerced m else // CSharpMethod(x=b) --> CSharpMethod(?x=b) callerArgExpr diff --git a/src/fsharp/TastOps.fs b/src/fsharp/TastOps.fs index 761acf38b8..3dd76a5074 100644 --- a/src/fsharp/TastOps.fs +++ b/src/fsharp/TastOps.fs @@ -6078,6 +6078,16 @@ let mkDerefAddrExpr mAddrGet expr mExpr exprTy = /// have intended effect (i.e. is a readonly pointer and/or a defensive copy). let rec mkExprAddrOfExprAux g mustTakeAddress useReadonlyForGenericArrayAddress mut expr addrExprVal m = if mustTakeAddress then + let isNativePtr = + match addrExprVal with + | Some vf -> valRefEq g vf g.addrof2_vref + | _ -> false + + // If we are taking the native address using "&&" to get a nativeptr, disallow if it's readonly. + let checkTakeNativeAddress readonly = + if isNativePtr && readonly then + error(Error(FSComp.SR.tastValueMustBeMutable(), m)) + match expr with // LVALUE of "*x" where "x" is byref is just the byref itself | Expr.Op (TOp.LValueOp (LByrefGet, vref), _, [], m) when MustTakeAddressOfByrefGet g vref || CanTakeAddressOfByrefGet g vref mut -> @@ -6090,6 +6100,7 @@ let rec mkExprAddrOfExprAux g mustTakeAddress useReadonlyForGenericArrayAddress | Expr.Val (vref, _, m) when MustTakeAddressOfVal g vref || CanTakeAddressOfImmutableVal g m vref mut -> let readonly = not (MustTakeAddressOfVal g vref) let writeonly = false + checkTakeNativeAddress readonly None, mkValAddr m readonly vref, readonly, writeonly // LVALUE of "e.f" where "f" is an instance F# field or record field. @@ -6134,15 +6145,11 @@ let rec mkExprAddrOfExprAux g mustTakeAddress useReadonlyForGenericArrayAddress // LVALUE of "e.[n]" where e is an array of structs | Expr.App (Expr.Val (vf, _, _), _, [elemTy], [aexpr;nexpr], _) when (valRefEq g vf g.array_get_vref) -> - + let readonly = false // array address is never forced to be readonly let writeonly = false let shape = ILArrayShape.SingleDimensional let ilInstrReadOnlyAnnotation = if isTyparTy g elemTy && useReadonlyForGenericArrayAddress then ReadonlyAddress else NormalAddress - let isNativePtr = - match addrExprVal with - | Some vf -> valRefEq g vf g.addrof2_vref - | _ -> false None, mkArrayElemAddress g (readonly, ilInstrReadOnlyAnnotation, isNativePtr, shape, elemTy, [aexpr; nexpr], m), readonly, writeonly // LVALUE of "e.[n1, n2]", "e.[n1, n2, n3]", "e.[n1, n2, n3, n4]" where e is an array of structs @@ -6153,11 +6160,6 @@ let rec mkExprAddrOfExprAux g mustTakeAddress useReadonlyForGenericArrayAddress let writeonly = false let shape = ILArrayShape.FromRank args.Length let ilInstrReadOnlyAnnotation = if isTyparTy g elemTy && useReadonlyForGenericArrayAddress then ReadonlyAddress else NormalAddress - let isNativePtr = - match addrExprVal with - | Some vf -> valRefEq g vf g.addrof2_vref - | _ -> false - None, mkArrayElemAddress g (readonly, ilInstrReadOnlyAnnotation, isNativePtr, shape, elemTy, (aexpr :: args), m), readonly, writeonly // LVALUE: "&meth(args)" where meth has a byref or inref return. Includes "&span.[idx]". diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 2cf45098b9..a3f38aacad 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -13982,7 +13982,12 @@ module MutRecBindingChecking = | Phase2AInherit (synBaseTy, arg, baseValOpt, m) -> let baseTy, tpenv = TcType cenv NoNewTypars CheckCxs ItemOccurence.Use envInstance tpenv synBaseTy let baseTy = baseTy |> convertToTypeWithMetadataIfPossible g - let inheritsExpr, tpenv = TcNewExpr cenv envInstance tpenv baseTy (Some synBaseTy.Range) true arg m + let inheritsExpr, tpenv = + try + TcNewExpr cenv envInstance tpenv baseTy (Some synBaseTy.Range) true arg m + with e -> + errorRecovery e m + mkUnit g m, tpenv let envInstance = match baseValOpt with Some baseVal -> AddLocalVal cenv.tcSink scopem baseVal envInstance | None -> envInstance let envNonRec = match baseValOpt with Some baseVal -> AddLocalVal cenv.tcSink scopem baseVal envNonRec | None -> envNonRec let innerState = (tpenv, envInstance, envStatic, envNonRec, generalizedRecBinds, preGeneralizationRecBinds, uncheckedRecBindsTable) diff --git a/src/fsharp/fsi/fsi.fs b/src/fsharp/fsi/fsi.fs index ad1cf10bbc..098ede415c 100644 --- a/src/fsharp/fsi/fsi.fs +++ b/src/fsharp/fsi/fsi.fs @@ -19,9 +19,6 @@ open System.Threading open System.Reflection open System.Runtime.CompilerServices open System.Runtime.InteropServices -#if NETSTANDARD -open System.Runtime.Loader -#endif open FSharp.Compiler open FSharp.Compiler.AbstractIL @@ -1288,10 +1285,14 @@ type internal FsiDynamicCompiler match packageManagerLines with | [] -> istate | (_, _, m)::_ -> - let reportError errorType error = - match errorType with - | ErrorReportType.Warning -> warning(Error(error,m)) - | ErrorReportType.Error -> errorR(Error(error, m)) + let reportError = + let report errorType err msg = + let error = err, msg + match errorType with + | ErrorReportType.Warning -> warning(Error(error, m)) + | ErrorReportType.Error -> errorR(Error(error, m)) + ResolvingErrorReport (report) + let outputDir = tcConfigB.outputDir |> Option.defaultValue "" match tcConfigB.dependencyProvider.TryFindDependencyManagerByKey(tcConfigB.compilerToolPaths, outputDir, reportError, packageManagerKey) with @@ -1303,17 +1304,17 @@ type internal FsiDynamicCompiler let removeErrorLinesFromScript () = tcConfigB.packageManagerLines <- tcConfigB.packageManagerLines |> Map.map(fun _ l -> l |> List.filter(fun (tried, _, _) -> tried)) try - match tcConfigB.dependencyProvider.Resolve(dependencyManager, tcConfigB.implicitIncludeDir, "stdin.fsx", "stdin.fsx", ".fsx", packageManagerTextLines, reportError, executionTfm) with - | false, _, _, _ -> istate // error already reported - | succeeded, _references, generatedScripts, additionalIncludeFolders -> - if succeeded then - tcConfigB.packageManagerLines <- tcConfigB.packageManagerLines |> Map.map(fun _ l -> l |> List.map(fun (_, p, m) -> true, p, m)) - else - removeErrorLinesFromScript () - for folder in additionalIncludeFolders do + let result = tcConfigB.dependencyProvider.Resolve(dependencyManager, ".fsx", packageManagerTextLines, reportError, executionTfm, tcConfigB.implicitIncludeDir, "stdin.fsx", "stdin.fsx") + match result.Success with + | false -> + removeErrorLinesFromScript () + istate // error already reported + | true -> + tcConfigB.packageManagerLines <- tcConfigB.packageManagerLines |> Map.map(fun _ l -> l |> List.map(fun (_, p, m) -> true, p, m)) + for folder in result.Roots do tcConfigB.AddIncludePath(m, folder, "") - let scripts = generatedScripts |> Seq.toList - if generatedScripts |> Seq.length > 0 then + let scripts = result.SourceFiles |> Seq.toList + if scripts |> Seq.length > 0 then fsiDynamicCompiler.EvalSourceFiles(ctok, istate, m, scripts, lexResourceManager, errorLogger) else istate with _ -> @@ -1557,20 +1558,6 @@ type internal FsiInterruptController(fsiOptions: FsiCommandLineOptions, fsiConso module internal MagicAssemblyResolution = -#if NETSTANDARD - // Cut down AssemblyLoadContext, for loading native libraries - type FsiNativeAssemblyLoadContext () = - inherit AssemblyLoadContext() - - member this.LoadNativeLibrary(path: string): IntPtr = - base.LoadUnmanagedDllFromPath(path) - - override _.Load(_path: AssemblyName): Assembly = - raise (NotImplementedException()) - - static member NativeLoadContext = new FsiNativeAssemblyLoadContext() -#endif - // See bug 5501 for details on decision to use UnsafeLoadFrom here. // Summary: // It is an explicit user trust decision to load an assembly with #r. Scripts are not run automatically (for example, by double-clicking in explorer). @@ -1579,7 +1566,7 @@ module internal MagicAssemblyResolution = [] let private assemblyLoadFrom (path:string) = Assembly.UnsafeLoadFrom(path) - let Install(tcConfigB, tcImports: TcImports, fsiDynamicCompiler: FsiDynamicCompiler, fsiConsoleOutput: FsiConsoleOutput) = + let Install(tcConfigB, tcImports: TcImports, fsiDynamicCompiler: FsiDynamicCompiler, fsiConsoleOutput: FsiConsoleOutput) = let ResolveAssembly (ctok, m, tcConfigB, tcImports: TcImports, fsiDynamicCompiler: FsiDynamicCompiler, fsiConsoleOutput: FsiConsoleOutput, fullAssemName: string) = @@ -1672,100 +1659,6 @@ module internal MagicAssemblyResolution = stopProcessingRecovery e range0 null -#if NETSTANDARD - let probingFileNames (name: string) = - // coreclr native library probing algorithm: https://github.com/dotnet/coreclr/blob/9773db1e7b1acb3ec75c9cc0e36bd62dcbacd6d5/src/System.Private.CoreLib/shared/System/Runtime/Loader/LibraryNameVariation.Unix.cs - let isRooted = Path.IsPathRooted name - let useSuffix s = not (name.Contains(s + ".") || name.EndsWith(s)) // linux devs often append version # to libraries I.e mydll.so.5.3.2 - let usePrefix = name.IndexOf(Path.DirectorySeparatorChar) = -1 // If name has directory information no add no prefix - && name.IndexOf(Path.AltDirectorySeparatorChar) = -1 - && name.IndexOf(Path.PathSeparator) = -1 - && name.IndexOf(Path.VolumeSeparatorChar) = -1 - let prefix = [| "lib" |] - let suffix = [| - if RuntimeInformation.IsOSPlatform(OSPlatform.Windows) then - ".dll" - ".exe" - elif RuntimeInformation.IsOSPlatform(OSPlatform.OSX) then - ".dylib" - else - ".so" - |] - - [| - yield name // Bare name - if not (isRooted) then - for s in suffix do - if useSuffix s then // Suffix without prefix - yield (sprintf "%s%s" name s) - if usePrefix then - for p in prefix do // Suffix with prefix - yield (sprintf "%s%s%s" p name s) - elif usePrefix then - for p in prefix do // Prefix - yield (sprintf "%s%s" p name) - |] - - // Directories to start probing in - // Algorithm: - // Search for native libraries using: - // 1. Include directories - // 2. compilerToolPath directories - // 3. reference dll's - // 4. The implicit include directory - let probingRoots = - seq { - yield! tcConfigB.includes - yield! tcConfigB.compilerToolPaths - yield! (tcConfigB.referencedDLLs |> Seq.map(fun ref -> Path.GetDirectoryName(ref.Text))) - yield tcConfigB.implicitIncludeDir - } |>Seq.distinct - - // Computer valid dotnet-rids for this environment: - // https://docs.microsoft.com/en-us/dotnet/core/rid-catalog - // - // Where rid is: win, win-x64, win-x86, osx-x64, linux-x64 etc ... - let probingRids = - let processArchitecture = RuntimeInformation.ProcessArchitecture - let baseRid = - if RuntimeInformation.IsOSPlatform(OSPlatform.Windows) then "win" - elif RuntimeInformation.IsOSPlatform(OSPlatform.OSX) then "osx" - else "linux" - let platformRid = - match processArchitecture with - | Architecture.X64 -> baseRid + "-x64" - | Architecture.X86 -> baseRid + "-x86" - | Architecture.Arm64 -> baseRid + "-arm64" - | _ -> baseRid + "arm" - [| "any"; baseRid; platformRid |] - - let resolveUnmanagedDll (assembly : Assembly) (name : string) : IntPtr = - ignore assembly - - // Enumerate probing roots looking for a dll that matches the probing name in the probed locations - let probeForNativeLibrary root rid name = - // Look for name in root - probingFileNames name |> Array.tryPick(fun name -> - let path = Path.Combine(root, "runtimes", rid, "native", name) - if File.Exists(path) then - Some path - else - None) - - let probe = - probingRoots |> Seq.tryPick(fun root -> - probingFileNames name |> Seq.tryPick(fun name -> - let path = Path.Combine(root, name) - if File.Exists(path) then - Some path - else - probingRids |> Seq.tryPick(fun rid -> probeForNativeLibrary root rid name))) - - match probe with - | Some path -> FsiNativeAssemblyLoadContext.NativeLoadContext.LoadNativeLibrary(path) - | None -> IntPtr.Zero -#endif - let rangeStdin = rangeN Lexhelp.stdinMockFilename 0 let resolveAssembly = new ResolveEventHandler(fun _ args -> @@ -1776,22 +1669,9 @@ module internal MagicAssemblyResolution = AppDomain.CurrentDomain.add_AssemblyResolve(resolveAssembly) -#if NETSTANDARD - // netstandard 2.1 has this property, unfortunately we don't build with that yet - //public event Func ResolvingUnmanagedDll - let resolveUnmanagedHandler = Func (resolveUnmanagedDll) - let eventInfo = typeof.GetEvent("ResolvingUnmanagedDll") - if not (isNull eventInfo) then - eventInfo.AddEventHandler(AssemblyLoadContext.Default, resolveUnmanagedHandler) -#endif - { new System.IDisposable with member x.Dispose() = AppDomain.CurrentDomain.remove_AssemblyResolve(resolveAssembly) -#if NETSTANDARD - if not (isNull eventInfo) then - eventInfo.RemoveEventHandler(AssemblyLoadContext.Default, resolveUnmanagedHandler) -#endif } //---------------------------------------------------------------------------- @@ -1994,10 +1874,13 @@ type internal FsiInteractionProcessor fsiDynamicCompiler.EvalSourceFiles (ctok, istate, m, sourceFiles, lexResourceManager, errorLogger),Completed None | IHash (ParsedHashDirective(("reference" | "r"), [path], m), _) -> - let reportError errorType error = - match errorType with - | ErrorReportType.Warning -> warning(Error(error,m)) - | ErrorReportType.Error -> errorR(Error(error, m)) + let reportError = + let report errorType err msg = + let error = err, msg + match errorType with + | ErrorReportType.Warning -> warning(Error(error, m)) + | ErrorReportType.Error -> errorR(Error(error, m)) + ResolvingErrorReport (report) let dm = tcConfigB.dependencyProvider.TryFindDependencyManagerInPath(tcConfigB.compilerToolPaths, tcConfigB.outputDir |> Option.defaultValue "", reportError, path) match dm with @@ -2660,7 +2543,7 @@ type FsiEvaluationSession (fsi: FsiEvaluationSessionHostConfig, argv:string[], i /// This reference cell holds the most recent interactive state let initialInteractiveState = fsiDynamicCompiler.GetInitialInteractiveState () - + let fsiStdinLexerProvider = FsiStdinLexerProvider(tcConfigB, fsiStdinSyphon, fsiConsoleInput, fsiConsoleOutput, fsiOptions, lexResourceManager) let fsiInteractionProcessor = FsiInteractionProcessor(fsi, tcConfigB, fsiOptions, fsiDynamicCompiler, fsiConsolePrompt, fsiConsoleOutput, fsiInterruptController, fsiStdinLexerProvider, lexResourceManager, initialInteractiveState) diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 66ee0bd792..59a07765b3 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -676,7 +676,7 @@ fileModuleSpec: | opt_attributes opt_declVisibility moduleIntro moduleSpfnsPossiblyEmptyBlock { if Option.isSome $2 then errorR(Error(FSComp.SR.parsVisibilityDeclarationsShouldComePriorToIdentifier(),rhs parseState 2)) let m2 = rhs parseState 3 - let m = (rhs2 parseState 3 4) + let m = (rhs2 parseState 1 4) let isRec,path2,xml,vis = $3 (fun (isRec2,path,_) -> if not (isNil path) then errorR(Error(FSComp.SR.parsNamespaceOrModuleNotBoth(),m2)) @@ -745,7 +745,7 @@ moduleSpfn: if not (isNil $1) then raiseParseErrorAt (rhs parseState 1) (FSComp.SR.parsIgnoreAttributesOnModuleAbbreviation()) match vis with | Some vis -> raiseParseErrorAt (rhs parseState 1) (FSComp.SR.parsIgnoreVisibilityOnModuleAbbreviationAlwaysPrivate(vis.ToString())) - | _ -> SynModuleSigDecl.ModuleAbbrev(List.head path,$5,rhs2 parseState 3 5) } + | _ -> SynModuleSigDecl.ModuleAbbrev(List.head path,$5,rhs2 parseState 1 5) } | opt_attributes opt_declVisibility moduleIntro colonOrEquals moduleSpecBlock { let isRec, path, xml, vis = $3 @@ -753,7 +753,7 @@ moduleSpfn: if isRec then raiseParseErrorAt (rhs parseState 3) (FSComp.SR.parsInvalidUseOfRec()) let info = ComponentInfo($1,[],[],path,xml,false,vis,rhs parseState 3) if Option.isSome $2 then errorR(Error(FSComp.SR.parsVisibilityDeclarationsShouldComePriorToIdentifier(),rhs parseState 2)) - SynModuleSigDecl.NestedModule(info, isRec, $5, rhs2 parseState 3 5) } + SynModuleSigDecl.NestedModule(info, isRec, $5, rhs2 parseState 1 5) } | opt_attributes opt_declVisibility tyconSpfns { if Option.isSome $2 then errorR(Error(FSComp.SR.parsVisibilityDeclarationsShouldComePriorToIdentifier(),rhs parseState 2)) @@ -778,7 +778,7 @@ valSpfn: { if Option.isSome $2 then errorR(Error(FSComp.SR.parsVisibilityDeclarationsShouldComePriorToIdentifier(),rhs parseState 2)) let attr1,attr2,isInline,isMutable,vis2,id,doc,explicitValTyparDecls,(ty,arity),konst = ($1),($4),($5),($6),($7),($8),grabXmlDoc(parseState,3),($9),($11),($12) if not (isNil attr2) then errorR(Deprecated(FSComp.SR.parsAttributesMustComeBeforeVal(),rhs parseState 4)) - let m = rhs2 parseState 3 11 + let m = rhs2 parseState 1 11 let valSpfn = ValSpfn((attr1@attr2),id,explicitValTyparDecls,ty,arity,isInline,isMutable,doc, vis2,konst,m) SynModuleSigDecl.Val(valSpfn,m) } @@ -965,21 +965,23 @@ classMemberSpfn: SynMemberSig.Inherit ($4,unionRanges (rhs parseState 3) ($4).Range) } | opt_attributes opt_declVisibility VAL fieldDecl - { if Option.isSome $2 then errorR(Error(FSComp.SR.parsVisibilityDeclarationsShouldComePriorToIdentifier(),rhs parseState 2)) - let fld = $4 $1 false - SynMemberSig.ValField(fld,rhs2 parseState 3 4) } + { let wholeRange = rhs2 parseState 1 4 + if Option.isSome $2 then errorR (Error (FSComp.SR.parsVisibilityDeclarationsShouldComePriorToIdentifier (), rhs parseState 2)) + let fld = $4 $1 false wholeRange + SynMemberSig.ValField (fld, wholeRange) } | opt_attributes opt_declVisibility STATIC VAL fieldDecl - { if Option.isSome $2 then errorR(Error(FSComp.SR.parsVisibilityDeclarationsShouldComePriorToIdentifier(),rhs parseState 2)) - SynMemberSig.ValField($5 $1 true,rhs2 parseState 3 5) } + { let wholeRange = rhs2 parseState 1 5 + if Option.isSome $2 then errorR (Error (FSComp.SR.parsVisibilityDeclarationsShouldComePriorToIdentifier (), rhs parseState 2)) + SynMemberSig.ValField($5 $1 true wholeRange, wholeRange) } | opt_attributes opt_declVisibility STATIC typeKeyword tyconSpfn { if Option.isSome $2 then errorR(Error(FSComp.SR.parsVisibilityDeclarationsShouldComePriorToIdentifier(),rhs parseState 2)) - SynMemberSig.NestedType($5,rhs2 parseState 3 5) } + SynMemberSig.NestedType($5,rhs2 parseState 1 5) } | opt_attributes opt_declVisibility NEW COLON topTypeWithTypeConstraints { let vis,doc,(ty,valSynInfo) = $2,grabXmlDoc(parseState,3),$5 - let m = unionRanges (rhs parseState 3) ty.Range + let m = unionRanges (rhs parseState 1) ty.Range let isInline = false let valSpfn = ValSpfn ($1, mkSynId (rhs parseState 3) "new", noInferredTypars, ty, valSynInfo, isInline, false, doc, vis, None, m) SynMemberSig.Member(valSpfn, CtorMemberFlags,m) } @@ -1254,7 +1256,7 @@ moduleDefn: | Choice2Of2 def -> if not (isSingleton path) then raiseParseErrorAt (rhs parseState 3) (FSComp.SR.parsModuleAbbreviationMustBeSimpleName()) let info = ComponentInfo(attribs,[],[],path,xml,false,vis,rhs parseState 3) - [ SynModuleDecl.NestedModule(info, isRec, def, false,(rhs2 parseState 3 4, def) ||> unionRangeWithListBy (fun d -> d.Range) ) ] } + [ SynModuleDecl.NestedModule(info, isRec, def, false,(rhs2 parseState 1 4, def) ||> unionRangeWithListBy (fun d -> d.Range) ) ] } /* unattached custom attributes */ | attributes recover @@ -1850,8 +1852,8 @@ classDefnMember: if Option.isSome $2 then errorR(Error(FSComp.SR.parsInterfacesHaveSameVisibilityAsEnclosingType(),rhs parseState 3)) let mWhole = match $5 with - | None -> rhs2 parseState 3 4 - | Some(mems) -> (rhs2 parseState 3 4, mems) ||> unionRangeWithListBy (fun (mem:SynMemberDefn) -> mem.Range) + | None -> rhs2 parseState 1 4 + | Some(mems) -> (rhs2 parseState 1 4, mems) ||> unionRangeWithListBy (fun (mem:SynMemberDefn) -> mem.Range) [ SynMemberDefn.Interface ($4, $5, mWhole) ] } | opt_attributes opt_declVisibility abstractMemberFlags opt_inline nameop opt_explicitValTyparDecls COLON topTypeWithTypeConstraints classMemberSpfnGetSet opt_ODECLEND @@ -1860,7 +1862,7 @@ classDefnMember: let getSetRangeOpt, getSet = $9 let getSetAdjuster arity = match arity,getSet with SynValInfo([],_),MemberKind.Member -> MemberKind.PropertyGet | _ -> getSet let wholeRange = - let m = rhs parseState 3 + let m = rhs parseState 1 match getSetRangeOpt with | None -> unionRanges m ty.Range | Some m2 -> unionRanges m m2 @@ -1875,11 +1877,13 @@ classDefnMember: | opt_attributes opt_declVisibility valDefnDecl opt_ODECLEND { if Option.isSome $2 then errorR(Error(FSComp.SR.parsVisibilityDeclarationsShouldComePriorToIdentifier(),rhs parseState 2)) - $3 None $1 false } + let rangeStart = rhs parseState 1 + $3 rangeStart $1 false } | opt_attributes opt_declVisibility STATIC valDefnDecl opt_ODECLEND { if Option.isSome $2 then errorR(Error(FSComp.SR.parsVisibilityDeclarationsShouldComePriorToIdentifier(),rhs parseState 2)) - $4 (Some (rhs parseState 3)) $1 true } + let rangeStart = rhs parseState 1 + $4 rangeStart $1 true } | opt_attributes opt_declVisibility memberFlags autoPropsDefnDecl opt_ODECLEND { let rangeStart = rhs parseState 1 @@ -1889,7 +1893,7 @@ classDefnMember: $4 $1 isStatic flags rangeStart } | opt_attributes opt_declVisibility NEW atomicPattern optAsSpec EQUALS typedSeqExprBlock opt_ODECLEND - { let m = unionRanges (rhs2 parseState 3 6) $7.Range + { let m = unionRanges (rhs2 parseState 1 6) $7.Range let expr = $7 let valSynData = SynValData (Some CtorMemberFlags, SynValInfo([SynInfo.InferSynArgInfoFromPat $4],SynInfo.unnamedRetVal), $5) let vis = $2 @@ -1900,7 +1904,7 @@ classDefnMember: | opt_attributes opt_declVisibility STATIC typeKeyword tyconDefn { if Option.isSome $2 then errorR(Error(FSComp.SR.parsVisibilityDeclarationsShouldComePriorToIdentifier(),rhs parseState 2)) - [ SynMemberDefn.NestedType($5,None,rhs2 parseState 3 5) ] } + [ SynMemberDefn.NestedType($5,None,rhs2 parseState 1 5) ] } /* A 'val' definition in an object type definition */ @@ -1909,8 +1913,8 @@ valDefnDecl: { let mRhs = rhs2 parseState 4 6 let doc = grabXmlDoc(parseState,4) let mValDecl = rhs2 parseState 1 6 - (fun mLeft attribs isStatic -> - let mValDecl = match mLeft with None -> mValDecl | Some m -> unionRanges m mValDecl + (fun rangeStart attribs isStatic -> + let mValDecl = unionRanges rangeStart mValDecl let fld = Field(attribs,isStatic,Some $4,$6,$2,doc,$3,mRhs) [ SynMemberDefn.ValField(fld, mValDecl) ]) } @@ -1942,8 +1946,8 @@ atomicPatternLongIdent: | pathOp { (None,$1) } | access UNDERSCORE DOT pathOp { if not (parseState.LexBuffer.SupportsFeature LanguageFeature.SingleUnderscorePattern) then - raiseParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnexpectedSymbolDot()) - let (LongIdentWithDots(lid,dotms)) = $4 in (Some($1),LongIdentWithDots(ident("_",rhs parseState 1)::lid, rhs parseState 2::dotms)) + raiseParseErrorAt (rhs parseState 3) (FSComp.SR.parsUnexpectedSymbolDot()) + let (LongIdentWithDots(lid,dotms)) = $4 in (Some($1),LongIdentWithDots(ident("_",rhs parseState 2)::lid, rhs parseState 3::dotms)) } | access pathOp { (Some($1), $2) } @@ -2294,20 +2298,20 @@ attrUnionCaseDecl: | opt_attributes opt_access unionCaseName OF unionCaseRepr opt_OBLOCKSEP { if Option.isSome $2 then errorR(Error(FSComp.SR.parsUnionCasesCannotHaveVisibilityDeclarations(),rhs parseState 2)) - let mDecl = rhs2 parseState 3 5 + let mDecl = rhs2 parseState 1 5 (fun xmlDoc -> Choice2Of2 (UnionCase ( $1, $3,UnionCaseFields $5,xmlDoc,None,mDecl))) } | opt_attributes opt_access unionCaseName COLON topType opt_OBLOCKSEP { if Option.isSome $2 then errorR(Error(FSComp.SR.parsUnionCasesCannotHaveVisibilityDeclarations(),rhs parseState 2)) libraryOnlyWarning(lhs parseState) - let mDecl = rhs2 parseState 3 5 + let mDecl = rhs2 parseState 1 5 (fun xmlDoc -> Choice2Of2 (UnionCase ( $1, $3,UnionCaseFullType $5,xmlDoc,None,mDecl))) } | opt_attributes opt_access unionCaseName EQUALS constant opt_OBLOCKSEP { if Option.isSome $2 then errorR(Error(FSComp.SR.parsEnumFieldsCannotHaveVisibilityDeclarations(),rhs parseState 2)) - let mDecl = rhs2 parseState 3 5 + let mDecl = rhs2 parseState 1 5 (fun xmlDoc -> Choice1Of2 (EnumCase ( $1, $3,$5,xmlDoc,mDecl))) } @@ -2365,18 +2369,18 @@ recdFieldDeclList: /* A field declaration in a record type */ recdFieldDecl: - | opt_attributes fieldDecl - { let fld = $2 $1 false - let (Field(a,b,c,d,e,f,vis,g)) = fld - if Option.isSome vis then errorR(Error(FSComp.SR.parsRecordFieldsCannotHaveVisibilityDeclarations(),rhs parseState 2)) - Field(a,b,c,d,e,f,None,g) } + | opt_attributes fieldDecl + { let wholeRange = rhs2 parseState 1 2 + let fld = $2 $1 false wholeRange + let (Field (a, b, c, d, e, f, vis, wholeRange)) = fld + if Option.isSome vis then errorR (Error (FSComp.SR.parsRecordFieldsCannotHaveVisibilityDeclarations (), rhs parseState 2)) + Field (a, b, c, d, e, f, None, wholeRange) } /* Part of a field or val declaration in a record type or object type */ fieldDecl: | opt_mutable opt_access ident COLON typ - { let mRhs = rhs2 parseState 3 5 - let xmlDoc = grabXmlDoc(parseState,3) - (fun attrs stat -> Field(attrs, stat,Some $3,$5,$1,xmlDoc,$2,mRhs)) } + { let xmlDoc = grabXmlDoc (parseState, 3) + fun attrs stat wholeRange -> Field(attrs, stat, Some $3, $5, $1, xmlDoc, $2, wholeRange) } /* An exception definition */ diff --git a/tests/scripts/codingConventions.fsx b/tests/scripts/codingConventions.fsx deleted file mode 100644 index 02e0dc541a..0000000000 --- a/tests/scripts/codingConventions.fsx +++ /dev/null @@ -1,149 +0,0 @@ -// Print some stats about some very very basic code formatting conventions - -open System.IO - -let lines = - [| for dir in [ "src/fsharp"; "src/fsharp/FSharp.Core"; "src/fsharp/symbols"; "src/fsharp/service"; "src/absil" ]do - for file in Directory.EnumerateFiles(__SOURCE_DIRECTORY__ + "/../../" + dir, "*.fs") do - // TcGlobals.fs gets an exception - let lines = File.ReadAllLines file - for (line, lineText) in Array.indexed lines do - - // We hardwire some exceptions - if not (Path.GetFileName(file) = "service.fs") && // churning - not (lineText.Contains("SuppressMessage")) && // old fxcop annotation - not (Path.GetFileName(file) = "TcGlobals.fs") && - not (Path.GetFileName(file) = "tast.fs" && line > 2100 && line < 2400) then - - yield file, (line+1, lineText) |] - - -printfn "------ LINE LENGTH ANALYSIS ----------" -let totalLines = lines.Length -let buckets = lines |> Array.groupBy (fun (_file, (_line, lineText)) -> lineText.Length / 10) |> Array.sortByDescending (fun (key, vs) -> key) - -for (key, sz) in buckets do - printfn "bucket %d-%d - %%%2.1f" (key*10) (key*10+9) (double sz.Length / double totalLines * 100.0) - -printfn "top bucket: " - -for (file, (line, text)) in snd buckets.[0] do - printfn "%s %d %s..." file line text.[0..50] - -let numLong = lines |> Array.filter (fun (_, (line, lineText)) -> lineText.Length > 120) |> Array.length -let numHuge = lines |> Array.filter (fun (_, (line, lineText)) -> lineText.Length > 160) |> Array.length -let numHumungous = lines |> Array.filter (fun (_, (line, lineText)) -> lineText.Length > 200) |> Array.length - -printfn "%d long lines = %2.2f%%" numLong (double numLong / double totalLines) -printfn "%d huge lines = %2.2f%%" numHuge (double numHuge / double totalLines) -printfn "%d humungous lines = %2.2f%%" numHumungous (double numHumungous / double totalLines) - -printfn "------ SPACE AFTER COMMA ANALYSIS ----------" - -let commas = - lines - |> Array.groupBy fst - |> Array.map (fun (file, lines) -> - file, - lines - |> Array.sumBy (fun (_, (_, line)) -> - line |> Seq.pairwise |> Seq.filter (fun (c1, c2) -> c1 = ',' && c2 <> ' ') |> Seq.length)) - |> Array.sortByDescending snd - -printfn "Top files that have commas without spaces: %A" (Array.truncate 10 commas) - - -printfn "------DANGLINE SEMICOLONS----------" - -let semis = - lines - |> Array.groupBy fst - |> Array.map (fun (file, lines) -> - file, - lines - |> Array.filter (fun (_, (_, line)) -> line.Trim().EndsWith(";")) - |> Array.length) - |> Array.sortByDescending snd - -printfn "Top files that have semicolon at end of line: %A" (Array.truncate 10 semis) - - -printfn "------NO SPACE AFTER COLON----------" - -open System.Text.RegularExpressions - -let noSpaceAfterColons = - let re = Regex(":[a-zA-Z]") - lines - |> Array.groupBy fst - |> Array.map (fun (file, lines) -> - file, - lines - |> Array.filter (fun (_, (_, line)) -> re.IsMatch(line)) - |> Array.length) - |> Array.sortByDescending snd - -printfn "Top files that have no space after colon:\n%A" (Array.truncate 10 noSpaceAfterColons) - -printfn "------ SPACE BEFORE COLON----------" - - -let spaceBeforeColon = - let re = Regex("[^\\)] : [a-zA-Z]") - lines - |> Array.groupBy fst - |> Array.map (fun (file, lines) -> - file, - lines - |> Array.filter (fun (_, (_, line)) -> re.IsMatch(line)) - |> Array.length) - |> Array.sortByDescending snd - -printfn "Top files that have extra space before colon:\n%A" (Array.truncate 10 spaceBeforeColon) - -printfn "------ Internal spacing----------" - - -let internalSpacing = - let re = Regex("[^ ] [^ ]") - lines - |> Array.groupBy fst - |> Array.map (fun (file, lines) -> - file, - lines - |> Array.filter (fun (_, (_, line)) -> re.IsMatch(line)) - |> Array.length) - |> Array.sortByDescending snd - -printfn "Top files that have internal spacing in lines:\n%A" (Array.truncate 10 internalSpacing) - -printfn "------ cenv.g ----------" - -let cenv_dot_g = - let re = Regex("cenv\.g") - lines - |> Array.groupBy fst - |> Array.map (fun (file, lines) -> - file, - lines - |> Array.filter (fun (_, (_, line)) -> re.IsMatch(line)) - |> Array.length) - |> Array.sortByDescending snd - -printfn "Top files that have endless cenv.g:\n%A" (Array.truncate 10 cenv_dot_g) - -printfn "------ parenthesized atomic expressions (id) ----------" - -let parens_id = - let re = Regex("\([a-zA-Z0-9]+\)") - lines - |> Array.groupBy fst - |> Array.map (fun (file, lines) -> - file, - lines - |> Array.filter (fun (_, (_, line)) -> re.IsMatch(line)) - |> Array.length) - |> Array.sortByDescending snd - -printfn "Top files that have parenthesized atomic expressionsg:\n%A" (Array.truncate 10 parens_id) - diff --git a/tests/scripts/update-baselines.fsx b/tests/scripts/update-baselines.fsx deleted file mode 100644 index 06e9c13ef8..0000000000 --- a/tests/scripts/update-baselines.fsx +++ /dev/null @@ -1,83 +0,0 @@ -open System -open System.IO - -// this script is usefull for tests using a .bsl file (baseline) containing expected compiler output -// which is matched against .vserr or .err file aside once the test has run -// the script replaces all the .bsl/.bslpp with either .err or .vserr - -let diff path1 path2 = - let result = System.Text.StringBuilder() - let append s = result.AppendLine s |> ignore - - if not <| File.Exists(path1) then failwithf "Invalid path %s" path1 - if not <| File.Exists(path2) then failwithf "Invalid path %s" path2 - - let lines1 = File.ReadAllLines(path1) - let lines2 = File.ReadAllLines(path2) - - let minLines = min lines1.Length lines2.Length - - for i = 0 to (minLines - 1) do - - let normalizePath line = line - let line1 = normalizePath lines1.[i] - let line2 = normalizePath lines2.[i] - - if not (line1.Contains "// MVID") && - not (line1.Contains "// Image base:") && - not (line1.Contains ".line") && - not (line1.Contains " .ver ") && - not (line1.Contains "// Offset:") then - if line1 <> line2 then - append <| sprintf "diff between [%s] and [%s]" path1 path2 - append <| sprintf "line %d" (i+1) - append <| sprintf " - %s" line1 - append <| sprintf " + %s" line2 - - if lines1.Length <> lines2.Length then - append <| sprintf "diff between [%s] and [%s]" path1 path2 - append <| sprintf "diff at line %d" minLines - lines1.[minLines .. (lines1.Length - 1)] |> Array.iter (append << sprintf "- %s") - lines2.[minLines .. (lines2.Length - 1)] |> Array.iter (append << sprintf "+ %s") - - result.ToString() - -let fsdiff actualFile expectedFile = - let errorText = System.IO.File.ReadAllText actualFile - - let result = diff expectedFile actualFile - if result <> "" then - printfn "%s" result - - result - -let directories = - [ - "fsharp/typecheck/sigs" - "fsharp/typecheck/overloads" - "fsharpqa/Source" - ] - |> List.map (fun d -> Path.Combine(__SOURCE_DIRECTORY__, ".." , d) |> DirectoryInfo) - -let extensionPatterns = ["*.bsl"; "*.vsbsl"; "*.il.bsl"] -for d in directories do - for p in extensionPatterns do - for bslFile in d.GetFiles (p, SearchOption.AllDirectories) do - let baseLineFileName = bslFile.FullName - if not (baseLineFileName.EndsWith("bslpp")) then - let errFileName = - if baseLineFileName.EndsWith "vsbsl" then Path.ChangeExtension(baseLineFileName, "vserr") - elif baseLineFileName.EndsWith "il.bsl" then baseLineFileName.Replace("il.bsl", "il") - else Path.ChangeExtension(baseLineFileName, "err") - let baseLineFile = FileInfo(baseLineFileName) - let errFile = FileInfo(errFileName) - //let baseLineFilePreProcess = FileInfo(Path.ChangeExtension(errFile.FullName, "bslpp")) - - if baseLineFile.Exists && errFile.Exists then - - //printfn "consider %s and %s" baseLineFile.FullName errFileName - if fsdiff errFileName baseLineFileName <> "" then - - printfn "%s not matching, replacing with %s" baseLineFileName errFileName - if not (fsi.CommandLineArgs |> Array.contains "-n") then - errFile.CopyTo(baseLineFileName, true) |> ignore diff --git a/tests/service/Common.fs b/tests/service/Common.fs index cd180a0900..e62233448b 100644 --- a/tests/service/Common.fs +++ b/tests/service/Common.fs @@ -312,8 +312,8 @@ let getSymbolUses (source: string) = typeCheckResults.GetAllUsesOfAllSymbolsInFile() |> Async.RunSynchronously let getSymbols (source: string) = - getSymbolUses source - |> Array.map (fun symbolUse -> symbolUse.Symbol) + let symbolUses = getSymbolUses source + symbolUses |> Array.map (fun symbolUse -> symbolUse.Symbol) let getSymbolName (symbol: FSharpSymbol) = @@ -329,8 +329,10 @@ let getSymbolName (symbol: FSharpSymbol) = let assertContainsSymbolWithName name source = - getSymbols source - |> Array.choose getSymbolName + let symbols = getSymbols source + let names = symbols |> Array.choose getSymbolName + + names |> Array.contains name |> shouldEqual true diff --git a/tests/service/EditorTests.fs b/tests/service/EditorTests.fs index 9ed74c96f2..459aa75ea9 100644 --- a/tests/service/EditorTests.fs +++ b/tests/service/EditorTests.fs @@ -1361,3 +1361,14 @@ type R = y """ assertContainsSymbolWithName "y" source + + +[] +let ``Inherit ctor arg recovery`` () = + let source = """ + type T() as this = + inherit System.Exception('a', 'a') + + let x = this + """ + assertContainsSymbolWithName "x" source diff --git a/tests/service/StructureTests.fs b/tests/service/StructureTests.fs index c3583fe5ab..028e05e4b3 100644 --- a/tests/service/StructureTests.fs +++ b/tests/service/StructureTests.fs @@ -66,8 +66,13 @@ let ``nested module``() = """ module MyModule = () + +[] +module Module = + () """ - => [ (2, 0, 3, 6), (2, 15, 3, 6) ] + => [ (2, 0, 3, 6), (2, 15, 3, 6) + (5, 0, 7, 6), (6, 13, 7, 6) ] [] let ``module with multiline function``() = @@ -113,10 +118,10 @@ type Color = let ``record with interface``() = """ type Color = - { Red: int - Green: int - Blue: int - } + { Red: + int + mutable Blue: + int } interface IDisposable with member __.Dispose() = @@ -124,8 +129,9 @@ type Color = """ => [ (2, 5, 10, 55), (2, 11, 10, 55) - (3, 4, 4, 14), (3, 4, 4, 14) + (3, 4, 6, 15), (3, 4, 6, 15) (3, 6, 4, 13), (3, 6, 4, 13) + (5, 6, 6, 13), (5, 6, 6, 13) (8, 4, 10, 55), (8, 25, 10, 55) (9, 8, 10, 55), (9, 27, 10, 55) (9, 15, 10, 55), (9, 27, 10, 55) ] @@ -607,3 +613,65 @@ module NestedModule = """ => [ (4, 0, 5, 15), (4, 13, 5, 15) (9, 0, 10, 15), (9, 19, 10, 15) ] + +[] +let ``Member val`` () = + """ +type T() = + member val field1 = + () + + [] + member val field2 = + () + + static member val field3 = + () + + [] + static member val field4 = + () +""" + => [ (2, 5, 15, 10), (2, 7, 15, 10) + (3, 4, 4, 10), (3, 4, 4, 10) + (6, 4, 8, 10), (6, 4, 8, 10) + (10, 4, 11, 10), (10, 4, 11, 10) + (13, 4, 15, 10), (13, 4, 15, 10) ] + +[] +let ``Secondary constructors`` () = + """ +type T() = + new () = + T () + + internal new () = + T () + + [] + new () = + T () +""" + => [ (2, 5, 11, 12), (2, 7, 11, 12) + (3, 4, 4, 12), (3, 7, 4, 12) + (3, 4, 4, 12), (4, 8, 4, 12) + (6, 4, 7, 12), (6, 16, 7, 12) + (6, 4, 7, 12), (7, 8, 7, 12) + (9, 4, 11, 12), (10, 7, 11, 12) + (9, 4, 11, 12), (11, 8, 11, 12) ] + + +[] +let ``Abstract members`` () = + """ +type T() = + abstract Foo: + int + + [] + abstract Foo: + int +""" + => [ (2, 5, 8, 11), (2, 7, 8, 11) + (3, 4, 4, 11), (4, 8, 4, 11) + (6, 4, 8, 11), (8, 8, 8, 11) ]