diff --git a/src/fsharp/CompileOps.fs b/src/fsharp/CompileOps.fs index 63dba3fed83..0759d5899fa 100644 --- a/src/fsharp/CompileOps.fs +++ b/src/fsharp/CompileOps.fs @@ -2308,8 +2308,6 @@ type TcConfigBuilder = mutable pathMap: PathMap mutable langVersion: LanguageVersion - - mutable dependencyProvider: DependencyProvider } static member Initial = @@ -2449,7 +2447,6 @@ type TcConfigBuilder = noConditionalErasure = false pathMap = PathMap.empty langVersion = LanguageVersion("default") - dependencyProvider = Unchecked.defaultof } // Directories to start probing in @@ -2459,13 +2456,17 @@ type TcConfigBuilder = // 2. compilerToolPath directories // 3. reference dll's // 4. The implicit include directory - member private tcConfigB.nativeProbingRoots () = + // + // NOTE: it is important this is a delayed IEnumerable sequence. It is recomputed + // each time a resolution happens and additional paths may be added as a result. + member tcConfigB.GetNativeProbingRoots () = seq { yield! tcConfigB.includes yield! tcConfigB.compilerToolPaths yield! (tcConfigB.referencedDLLs |> Seq.map(fun ref -> Path.GetDirectoryName(ref.Text))) yield tcConfigB.implicitIncludeDir - } |> Seq.distinct + } + |> Seq.distinct static member CreateNew(legacyReferenceResolver, defaultFSharpBinariesDir, reduceMemoryUsage, implicitIncludeDir, isInteractive, isInvalidationSupported, defaultCopyFSharpCore, tryGetMetadataSnapshot) = @@ -2487,7 +2488,6 @@ type TcConfigBuilder = tryGetMetadataSnapshot = tryGetMetadataSnapshot useFsiAuxLib = isInteractive } - tcConfigBuilder.dependencyProvider <- new DependencyProvider(NativeResolutionProbe(tcConfigBuilder.nativeProbingRoots)) tcConfigBuilder member tcConfigB.ResolveSourceFile(m, nm, pathLoadedFrom) = @@ -2599,6 +2599,32 @@ type TcConfigBuilder = member tcConfigB.AddDependencyManagerText (packageManager: IDependencyManagerProvider, lt, m, path: string) = tcConfigB.packageManagerLines <- PackageManagerLine.AddLineWithKey packageManager.Key lt path m tcConfigB.packageManagerLines + member tcConfigB.AddReferenceDirective (dependencyProvider: DependencyProvider, m, path: string, directive) = + let output = tcConfigB.outputDir |> Option.defaultValue "" + + let reportError = + ResolvingErrorReport (fun errorType err msg -> + let error = err, msg + match errorType with + | ErrorReportType.Warning -> warning(Error(error, m)) + | ErrorReportType.Error -> errorR(Error(error, m))) + + let dm = dependencyProvider.TryFindDependencyManagerInPath(tcConfigB.compilerToolPaths, output , reportError, path) + + match dm with + | _, dependencyManager when not(isNull dependencyManager) -> + if tcConfigB.langVersion.SupportsFeature(LanguageFeature.PackageManagement) then + tcConfigB.AddDependencyManagerText (dependencyManager, directive, m, path) + else + errorR(Error(FSComp.SR.packageManagementRequiresVFive(), m)) + + | _, _ when directive = Directive.Include -> + errorR(Error(FSComp.SR.poundiNotSupportedByRegisteredDependencyManagers(), m)) + + // #r "Assembly" + | path, _ -> + tcConfigB.AddReferencedAssemblyByPath (m, path) + member tcConfigB.RemoveReferencedAssemblyByPath (m, path) = tcConfigB.referencedDLLs <- tcConfigB.referencedDLLs |> List.filter (fun ar -> not (Range.equals ar.Range m) || ar.Text <> path) @@ -2920,8 +2946,6 @@ type TcConfig private (data: TcConfigBuilder, validate: bool) = member x.legacyReferenceResolver = data.legacyReferenceResolver - member x.dependencyProvider = data.dependencyProvider - member tcConfig.CloneOfOriginalBuilder = { data with conditionalCompilationDefines=data.conditionalCompilationDefines } @@ -3290,6 +3314,8 @@ type TcConfig private (data: TcConfigBuilder, validate: bool) = member tcConfig.CoreLibraryDllReference() = fslibReference + member tcConfig.GetNativeProbingRoots() = data.GetNativeProbingRoots() + let ReportWarning options err = warningOn err (options.WarnLevel) (options.WarnOn) && not (List.contains (GetDiagnosticNumber err) (options.WarnOff)) @@ -3999,7 +4025,9 @@ and TcImportsWeakHack (tcImports: WeakReference) = /// Represents a table of imported assemblies with their resolutions. /// Is a disposable object, but it is recommended not to explicitly call Dispose unless you absolutely know nothing will be using its contents after the disposal. /// Otherwise, simply allow the GC to collect this and it will properly call Dispose from the finalizer. -and [] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAssemblyResolutions, importsBase: TcImports option, ilGlobalsOpt, compilationThread: ICompilationThread) as this = +and [] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAssemblyResolutions, importsBase: TcImports option, + ilGlobalsOpt, compilationThread: ICompilationThread, + dependencyProviderOpt: DependencyProvider option) as this = let mutable resolutions = initialResolutions let mutable importsBase: TcImports option = importsBase @@ -4330,6 +4358,14 @@ and [] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse | ILScopeRef.Assembly a -> a.Name = nm | _ -> false) + member tcImports.DependencyProvider = + CheckDisposed() + match dependencyProviderOpt with + | None -> + Debug.Assert(false, "this should never be called on FrameworkTcImports") + new DependencyProvider(null, null) + | Some dependencyProvider -> dependencyProvider + member tcImports.GetImportMap() = CheckDisposed() let loaderInterface = @@ -4893,7 +4929,7 @@ and [] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse let tcResolutions = TcAssemblyResolutions.BuildFromPriorResolutions(ctok, tcConfig, frameworkDLLs, []) let tcAltResolutions = TcAssemblyResolutions.BuildFromPriorResolutions(ctok, tcConfig, nonFrameworkDLLs, []) - let frameworkTcImports = new TcImports(tcConfigP, tcResolutions, None, None, tcConfig.compilationThread) + let frameworkTcImports = new TcImports(tcConfigP, tcResolutions, None, None, tcConfig.compilationThread, None) // Fetch the primaryAssembly from the referenced assemblies otherwise let primaryAssemblyReference = @@ -5019,24 +5055,27 @@ and [] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse |> List.map (function UnresolvedAssemblyReference(file, originalReferences) -> file, originalReferences) |> List.iter reportAssemblyNotResolved - static member BuildNonFrameworkTcImports (ctok, tcConfigP: TcConfigProvider, tcGlobals: TcGlobals, baseTcImports, nonFrameworkReferences, knownUnresolved) = + static member BuildNonFrameworkTcImports + (ctok, tcConfigP: TcConfigProvider, tcGlobals: TcGlobals, baseTcImports, + nonFrameworkReferences, knownUnresolved, dependencyProvider) = + cancellable { let tcConfig = tcConfigP.Get ctok let tcResolutions = TcAssemblyResolutions.BuildFromPriorResolutions(ctok, tcConfig, nonFrameworkReferences, knownUnresolved) let references = tcResolutions.GetAssemblyResolutions() - let tcImports = new TcImports(tcConfigP, tcResolutions, Some baseTcImports, Some tcGlobals.ilg, tcConfig.compilationThread) + let tcImports = new TcImports(tcConfigP, tcResolutions, Some baseTcImports, Some tcGlobals.ilg, tcConfig.compilationThread, Some dependencyProvider) let! _assemblies = tcImports.RegisterAndImportReferencedAssemblies(ctok, references) tcImports.ReportUnresolvedAssemblyReferences knownUnresolved return tcImports } - static member BuildTcImports(ctok, tcConfigP: TcConfigProvider) = + static member BuildTcImports(ctok, tcConfigP: TcConfigProvider, dependencyProvider) = cancellable { let tcConfig = tcConfigP.Get ctok //let foundationalTcImports, tcGlobals = TcImports.BuildFoundationalTcImports tcConfigP let frameworkDLLs, nonFrameworkReferences, knownUnresolved = TcAssemblyResolutions.SplitNonFoundationalResolutions(ctok, tcConfig) let! tcGlobals, frameworkTcImports = TcImports.BuildFrameworkTcImports (ctok, tcConfigP, frameworkDLLs, nonFrameworkReferences) - let! tcImports = TcImports.BuildNonFrameworkTcImports(ctok, tcConfigP, tcGlobals, frameworkTcImports, nonFrameworkReferences, knownUnresolved) + let! tcImports = TcImports.BuildNonFrameworkTcImports(ctok, tcConfigP, tcGlobals, frameworkTcImports, nonFrameworkReferences, knownUnresolved, dependencyProvider) return tcGlobals, tcImports } @@ -5066,10 +5105,12 @@ let RequireDLL (ctok, tcImports: TcImports, tcEnv, thisAssemblyName, m, file) = let ProcessMetaCommandsFromInput (nowarnF: 'state -> range * string -> 'state, - dllRequireF: 'state -> range * string -> 'state, - packageRequireF: 'state -> IDependencyManagerProvider * Directive * range * string -> 'state, + hashReferenceF: 'state -> range * string * Directive -> 'state, loadSourceF: 'state -> range * string -> unit) - (tcConfig:TcConfigBuilder, inp, pathOfMetaCommandSource, state0) = + (tcConfig:TcConfigBuilder, + inp: ParsedInput, + pathOfMetaCommandSource, + state0) = use unwindBuildPhase = PushThreadBuildPhaseUntilUnwind BuildPhase.Parse @@ -5078,50 +5119,25 @@ let ProcessMetaCommandsFromInput | ParsedInput.SigFile (_) -> false | ParsedInput.ImplFile (ParsedImplFileInput (isScript = isScript)) -> isScript - let ProcessMetaCommand state hash = - let mutable matchedm = range0 - try - let processDependencyManagerDirectives directive args m = - if not canHaveScriptMetaCommands then - errorR(HashReferenceNotAllowedInNonScript 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] -> - matchedm <- m - let output = tcConfig.outputDir |> Option.defaultValue "" - let dm = tcConfig.dependencyProvider.TryFindDependencyManagerInPath(tcConfig.compilerToolPaths, output , reportError, path) - match dm with - | _, dependencyManager when not(isNull dependencyManager) -> - if tcConfig.langVersion.SupportsFeature(LanguageFeature.PackageManagement) then - packageRequireF state (dependencyManager, directive, m, path) - else - errorR(Error(FSComp.SR.packageManagementRequiresVFive(), m)) - state + let ProcessDependencyManagerDirective directive args m state = + if not canHaveScriptMetaCommands then + errorR(HashReferenceNotAllowedInNonScript m) - | _, _ when directive = Directive.Include -> - errorR(Error(FSComp.SR.poundiNotSupportedByRegisteredDependencyManagers(), m)) - state + match args with + | [path] -> + let p = + if String.IsNullOrWhiteSpace(path) then "" + else path - // #r "Assembly" - | path, _ -> - let p = - if String.IsNullOrWhiteSpace(path) then "" - else path + hashReferenceF state (m, p, directive) - dllRequireF state (m, p) - - | _ -> - errorR(Error(FSComp.SR.buildInvalidHashrDirective(), m)) - state + | _ -> + errorR(Error(FSComp.SR.buildInvalidHashrDirective(), m)) + state + let ProcessMetaCommand state hash = + let mutable matchedm = range0 + try match hash with | ParsedHashDirective("I", args, m) -> if not canHaveScriptMetaCommands then @@ -5138,10 +5154,12 @@ let ProcessMetaCommandsFromInput List.fold (fun state d -> nowarnF state (m,d)) state numbers | ParsedHashDirective(("reference" | "r"), args, m) -> - processDependencyManagerDirectives Directive.Resolution args m + matchedm<-m + ProcessDependencyManagerDirective Directive.Resolution args m state | ParsedHashDirective(("i"), args, m) -> - processDependencyManagerDirectives Directive.Include args m + matchedm<-m + ProcessDependencyManagerDirective Directive.Include args m state | ParsedHashDirective("load", args, m) -> if not canHaveScriptMetaCommands then @@ -5217,20 +5235,22 @@ let ApplyNoWarnsToTcConfig (tcConfig: TcConfig, inp: ParsedInput, pathOfMetaComm // Clone let tcConfigB = tcConfig.CloneOfOriginalBuilder let addNoWarn = fun () (m,s) -> tcConfigB.TurnWarningOff(m, s) - let addReferencedAssemblyByPath = fun () (_m,_s) -> () - let addDependencyManagerText = fun () (_prefix, _lt, _m, _s) -> () - let addLoadedSource = fun () (_m,_s) -> () - ProcessMetaCommandsFromInput (addNoWarn, addReferencedAssemblyByPath, addDependencyManagerText, addLoadedSource) (tcConfigB, inp, pathOfMetaCommandSource, ()) + let addReference = fun () (_m, _s, _) -> () + let addLoadedSource = fun () (_m, _s) -> () + ProcessMetaCommandsFromInput + (addNoWarn, addReference, addLoadedSource) + (tcConfigB, inp, pathOfMetaCommandSource, ()) TcConfig.Create(tcConfigB, validate=false) -let ApplyMetaCommandsFromInputToTcConfig (tcConfig: TcConfig, inp: ParsedInput, pathOfMetaCommandSource) = +let ApplyMetaCommandsFromInputToTcConfig (tcConfig: TcConfig, inp: ParsedInput, pathOfMetaCommandSource, dependencyProvider) = // Clone let tcConfigB = tcConfig.CloneOfOriginalBuilder let getWarningNumber = fun () _ -> () - let addReferencedAssemblyByPath = fun () (m,s) -> tcConfigB.AddReferencedAssemblyByPath(m,s) - let addDependencyManagerText = fun () (packageManager, lt, m,s) -> tcConfigB.AddDependencyManagerText(packageManager, lt, m, s) + let addReferenceDirective = fun () (m, path, directive) -> tcConfigB.AddReferenceDirective(dependencyProvider, m, path, directive) let addLoadedSource = fun () (m,s) -> tcConfigB.AddLoadedSource(m,s,pathOfMetaCommandSource) - ProcessMetaCommandsFromInput (getWarningNumber, addReferencedAssemblyByPath, addDependencyManagerText, addLoadedSource) (tcConfigB, inp, pathOfMetaCommandSource, ()) + ProcessMetaCommandsFromInput + (getWarningNumber, addReferenceDirective, addLoadedSource) + (tcConfigB, inp, pathOfMetaCommandSource, ()) TcConfig.Create(tcConfigB, validate=false) //---------------------------------------------------------------------------- @@ -5254,22 +5274,34 @@ type LoadClosureInput = type LoadClosure = { /// The source files along with the ranges of the #load positions in each file. SourceFiles: (string * range list) list + /// The resolved references along with the ranges of the #r positions in each file. References: (string * AssemblyResolution list) list + + /// The resolved pacakge references along with the ranges of the #r positions in each file. + PackageReferences: (range * string list)[] + /// The list of references that were not resolved during load closure. These may still be extension references. UnresolvedReferences: UnresolvedAssemblyReference list + /// The list of all sources in the closure with inputs when available Inputs: LoadClosureInput list + /// The #load, including those that didn't resolve OriginalLoadReferences: (range * string * string) list + /// The #nowarns NoWarns: (string * range list) list + /// Diagnostics seen while processing resolutions ResolutionDiagnostics: (PhasedDiagnostic * bool) list + /// Diagnostics seen while parsing root of closure AllRootFileDiagnostics: (PhasedDiagnostic * bool) list + /// Diagnostics seen while processing the compiler options implied root of closure - LoadClosureRootFileDiagnostics: (PhasedDiagnostic * bool) list } + LoadClosureRootFileDiagnostics: (PhasedDiagnostic * bool) list + } [] @@ -5334,8 +5366,8 @@ module ScriptPreprocessClosure = let tcConfigB = TcConfigBuilder.CreateNew (legacyReferenceResolver, defaultFSharpBinariesDir, reduceMemoryUsage, projectDir, - isInteractive, isInvalidationSupported, defaultCopyFSharpCore=CopyFSharpCoreFlag.No, - tryGetMetadataSnapshot=tryGetMetadataSnapshot) + isInteractive, isInvalidationSupported, CopyFSharpCoreFlag.No, + tryGetMetadataSnapshot) applyCommandLineArgs tcConfigB @@ -5372,16 +5404,16 @@ module ScriptPreprocessClosure = [] let ApplyMetaCommandsFromInputToTcConfigAndGatherNoWarn - (tcConfig: TcConfig, inp: ParsedInput, pathOfMetaCommandSource) = + (tcConfig: TcConfig, inp: ParsedInput, + pathOfMetaCommandSource, dependencyProvider) = let tcConfigB = tcConfig.CloneOfOriginalBuilder let mutable nowarns = [] let getWarningNumber = fun () (m, s) -> nowarns <- (s, m) :: nowarns - let addReferencedAssemblyByPath = fun () (m, s) -> tcConfigB.AddReferencedAssemblyByPath(m, s) - let addDependencyManagerText = fun () (packageManagerPrefix, lt, m, s) -> tcConfigB.AddDependencyManagerText(packageManagerPrefix, lt, m, s) + let addReferenceDirective = fun () (m, s, directive) -> tcConfigB.AddReferenceDirective(dependencyProvider, m, s, directive) let addLoadedSource = fun () (m, s) -> tcConfigB.AddLoadedSource(m, s, pathOfMetaCommandSource) try - ProcessMetaCommandsFromInput (getWarningNumber, addReferencedAssemblyByPath, addDependencyManagerText, addLoadedSource) (tcConfigB, inp, pathOfMetaCommandSource, ()) + ProcessMetaCommandsFromInput (getWarningNumber, addReferenceDirective, addLoadedSource) (tcConfigB, inp, pathOfMetaCommandSource, ()) with ReportedError _ -> // Recover by using whatever did end up in the tcConfig () @@ -5393,11 +5425,15 @@ module ScriptPreprocessClosure = let tcConfigB = tcConfig.CloneOfOriginalBuilder TcConfig.Create(tcConfigB, validate=false), nowarns - let FindClosureFiles(mainFile, _m, closureSources, origTcConfig:TcConfig, codeContext, lexResourceManager: Lexhelp.LexResourceManager) = + let FindClosureFiles + (mainFile, _m, closureSources, origTcConfig:TcConfig, + codeContext, lexResourceManager: Lexhelp.LexResourceManager, dependencyProvider: DependencyProvider) = + let mutable tcConfig = origTcConfig let observedSources = Observed() let loadScripts = HashSet<_>() + let packageReferences = Dictionary(HashIdentity.Structural) // Resolve the packages let rec resolveDependencyManagerSources scriptName = @@ -5408,20 +5444,19 @@ module ScriptPreprocessClosure = | [] -> () | { Directive=_; LineStatus=_; Line=_; Range=m } :: _ -> let reportError = - let report errorType err msg = + ResolvingErrorReport (fun errorType err msg -> let error = err, msg match errorType with | ErrorReportType.Warning -> warning(Error(error, m)) - | ErrorReportType.Error -> errorR(Error(error, m)) - ResolvingErrorReport (report) + | ErrorReportType.Error -> errorR(Error(error, m))) match origTcConfig.packageManagerLines |> Map.tryFind packageManagerKey with | Some oldDependencyManagerLines when oldDependencyManagerLines = packageManagerLines -> () | _ -> let outputDir = tcConfig.outputDir |> Option.defaultValue "" - match tcConfig.dependencyProvider.TryFindDependencyManagerByKey(tcConfig.compilerToolPaths, outputDir, reportError, packageManagerKey) with + match dependencyProvider.TryFindDependencyManagerByKey(tcConfig.compilerToolPaths, outputDir, reportError, packageManagerKey) with | null -> - errorR(Error(tcConfig.dependencyProvider.CreatePackageManagerUnknownError(tcConfig.compilerToolPaths, outputDir, packageManagerKey, reportError), m)) + errorR(Error(dependencyProvider.CreatePackageManagerUnknownError(tcConfig.compilerToolPaths, outputDir, packageManagerKey, reportError), m)) | dependencyManager -> let directive d = @@ -5430,10 +5465,15 @@ module ScriptPreprocessClosure = | Directive.Include -> "i" let packageManagerTextLines = packageManagerLines |> List.map(fun l -> directive l.Directive, l.Line) - let result = tcConfig.dependencyProvider.Resolve(dependencyManager, ".fsx", packageManagerTextLines, reportError, executionTfm, executionRid, tcConfig.implicitIncludeDir, mainFile, scriptName) - match result.Success with - | true -> + let result = dependencyProvider.Resolve(dependencyManager, ".fsx", packageManagerTextLines, reportError, executionTfm, executionRid, tcConfig.implicitIncludeDir, mainFile, scriptName) + if result.Success then // Resolution produced no errors + //Write outputs in F# Interactive and compiler + if codeContext <> CodeContext.Editing then + for line in result.StdOut do Console.Out.WriteLine(line) + for line in result.StdError do Console.Error.WriteLine(line) + + packageReferences.[m] <- [ for script in result.SourceFiles do yield! File.ReadAllLines script ] if not (Seq.isEmpty result.Roots) then let tcConfigB = tcConfig.CloneOfOriginalBuilder for folder in result.Roots do @@ -5446,7 +5486,11 @@ module ScriptPreprocessClosure = let iSourceText = SourceText.ofString scriptText yield! loop (ClosureSource(script, m, iSourceText, true)) - | false -> + else + // Send outputs via diagnostics + if (result.StdOut.Length > 0 || result.StdError.Length > 0) then + for line in Array.append result.StdOut result.StdError do + errorR(Error(FSComp.SR.packageManagerError(line), m)) // Resolution produced errors update packagerManagerLines entries to note these failure // failed resolutions will no longer be considered let tcConfigB = tcConfig.CloneOfOriginalBuilder @@ -5472,7 +5516,7 @@ module ScriptPreprocessClosure = let pathOfMetaCommandSource = Path.GetDirectoryName filename let preSources = tcConfig.GetAvailableLoadedSources() - let tcConfigResult, noWarns = ApplyMetaCommandsFromInputToTcConfigAndGatherNoWarn (tcConfig, parsedScriptAst, pathOfMetaCommandSource) + let tcConfigResult, noWarns = ApplyMetaCommandsFromInputToTcConfigAndGatherNoWarn (tcConfig, parsedScriptAst, pathOfMetaCommandSource, dependencyProvider) tcConfig <- tcConfigResult // We accumulate the tcConfig in order to collect assembly references yield! resolveDependencyManagerSources filename @@ -5497,10 +5541,13 @@ module ScriptPreprocessClosure = printfn "yielding non-script source %s" filename yield ClosureFile(filename, m, None, [], [], []) ] - closureSources |> List.collect loop, tcConfig + let sources = closureSources |> List.collect loop + let packageReferences = packageReferences |> Seq.map (fun kvp -> kvp.Key, kvp.Value) |> Seq.toArray + sources, tcConfig, packageReferences + /// Reduce the full directive closure into LoadClosure - let GetLoadClosure(ctok, rootFilename, closureFiles, tcConfig: TcConfig, codeContext) = + let GetLoadClosure(ctok, rootFilename, closureFiles, tcConfig: TcConfig, codeContext, packageReferences) = // Mark the last file as isLastCompiland. let closureFiles = @@ -5565,6 +5612,7 @@ module ScriptPreprocessClosure = let result: LoadClosure = { SourceFiles = List.groupBy fst sourceFiles |> List.map (map2Of2 (List.map snd)) References = List.groupBy fst references |> List.map (map2Of2 (List.map snd)) + PackageReferences = packageReferences UnresolvedReferences = unresolvedReferences Inputs = sourceInputs NoWarns = List.groupBy fst globalNoWarns |> List.map (map2Of2 (List.map snd)) @@ -5582,7 +5630,7 @@ module ScriptPreprocessClosure = useSimpleResolution, useFsiAuxLib, useSdkRefs, lexResourceManager: Lexhelp.LexResourceManager, applyCommandLineArgs, assumeDotNetFramework, - tryGetMetadataSnapshot, reduceMemoryUsage) = + tryGetMetadataSnapshot, reduceMemoryUsage, dependencyProvider) = // Resolve the basic references such as FSharp.Core.dll first, before processing any #I directives in the script // @@ -5606,16 +5654,19 @@ module ScriptPreprocessClosure = tryGetMetadataSnapshot, reduceMemoryUsage) let closureSources = [ClosureSource(filename, range0, sourceText, true)] - let closureFiles, tcConfig = FindClosureFiles(filename, range0, closureSources, tcConfig, codeContext, lexResourceManager) - GetLoadClosure(ctok, filename, closureFiles, tcConfig, codeContext) + let closureFiles, tcConfig, packageReferences = FindClosureFiles(filename, range0, closureSources, tcConfig, codeContext, lexResourceManager, dependencyProvider) + GetLoadClosure(ctok, filename, closureFiles, tcConfig, codeContext, packageReferences) /// Given source filename, find the full load closure /// Used from fsi.fs and fsc.fs, for #load and command line - let GetFullClosureOfScriptFiles(ctok, tcConfig:TcConfig, files:(string*range) list,codeContext,lexResourceManager: Lexhelp.LexResourceManager) = + let GetFullClosureOfScriptFiles + (ctok, tcConfig:TcConfig, files:(string*range) list, codeContext, + lexResourceManager: Lexhelp.LexResourceManager, dependencyProvider) = + let mainFile, mainFileRange = List.last files let closureSources = files |> List.collect (fun (filename, m) -> ClosureSourceOfFilename(filename, m,tcConfig.inputCodePage,true)) - let closureFiles,tcConfig = FindClosureFiles(mainFile, mainFileRange, closureSources, tcConfig, codeContext, lexResourceManager) - GetLoadClosure(ctok, mainFile, closureFiles, tcConfig, codeContext) + let closureFiles, tcConfig, packageReferences = FindClosureFiles(mainFile, mainFileRange, closureSources, tcConfig, codeContext, lexResourceManager, dependencyProvider) + GetLoadClosure(ctok, mainFile, closureFiles, tcConfig, codeContext, packageReferences) type LoadClosure with /// Analyze a script text and find the closure of its references. @@ -5627,20 +5678,22 @@ type LoadClosure with (ctok, legacyReferenceResolver, defaultFSharpBinariesDir, filename: string, sourceText: ISourceText, codeContext, useSimpleResolution: bool, useFsiAuxLib, useSdkRefs, lexResourceManager: Lexhelp.LexResourceManager, - applyCommandLineArgs, assumeDotNetFramework, tryGetMetadataSnapshot, reduceMemoryUsage) = + applyCommandLineArgs, assumeDotNetFramework, tryGetMetadataSnapshot, + reduceMemoryUsage, dependencyProvider) = use unwindBuildPhase = PushThreadBuildPhaseUntilUnwind BuildPhase.Parse ScriptPreprocessClosure.GetFullClosureOfScriptText (ctok, legacyReferenceResolver, defaultFSharpBinariesDir, filename, sourceText, codeContext, useSimpleResolution, useFsiAuxLib, useSdkRefs, lexResourceManager, - applyCommandLineArgs, assumeDotNetFramework, tryGetMetadataSnapshot, reduceMemoryUsage) + applyCommandLineArgs, assumeDotNetFramework, tryGetMetadataSnapshot, reduceMemoryUsage, dependencyProvider) /// Analyze a set of script files and find the closure of their references. static member ComputeClosureOfScriptFiles (ctok, tcConfig: TcConfig, files:(string*range) list, codeContext, - lexResourceManager: Lexhelp.LexResourceManager) = + lexResourceManager: Lexhelp.LexResourceManager, dependencyProvider) = + use unwindBuildPhase = PushThreadBuildPhaseUntilUnwind BuildPhase.Parse - ScriptPreprocessClosure.GetFullClosureOfScriptFiles (ctok, tcConfig, files, codeContext, lexResourceManager) + ScriptPreprocessClosure.GetFullClosureOfScriptFiles (ctok, tcConfig, files, codeContext, lexResourceManager, dependencyProvider) //---------------------------------------------------------------------------- // Initial type checking environment diff --git a/src/fsharp/CompileOps.fsi b/src/fsharp/CompileOps.fsi index ebedf48153d..6d138c411a6 100644 --- a/src/fsharp/CompileOps.fsi +++ b/src/fsharp/CompileOps.fsi @@ -413,8 +413,6 @@ type TcConfigBuilder = mutable pathMap : PathMap mutable langVersion : LanguageVersion - - mutable dependencyProvider: DependencyProvider } static member Initial: TcConfigBuilder @@ -443,6 +441,9 @@ type TcConfigBuilder = static member SplitCommandLineResourceInfo: string -> string * string * ILResourceAccess + // Directories to start probing in for native DLLs for FSI dynamic loading + member GetNativeProbingRoots: unit -> seq + [] // Immutable TcConfig type TcConfig = @@ -559,8 +560,8 @@ type TcConfig = member isInteractive: bool member isInvalidationSupported: bool - member ComputeLightSyntaxInitialStatus: string -> bool + member GetTargetFrameworkDirectories: unit -> string list /// Get the loaded sources that exist and issue a warning for the ones that don't @@ -575,8 +576,11 @@ type TcConfig = member MakePathAbsolute: string -> string member copyFSharpCore: CopyFSharpCoreFlag + member shadowCopyReferences: bool + member useSdkRefs: bool + member langVersion: LanguageVersion static member Create: TcConfigBuilder * validate: bool -> TcConfig @@ -640,18 +644,28 @@ type TcImports = interface System.IDisposable //new: TcImports option -> TcImports member DllTable: NameMap with get + member GetImportedAssemblies: unit -> ImportedAssembly list + member GetCcusInDeclOrder: unit -> CcuThunk list + /// This excludes any framework imports (which may be shared between multiple builds) member GetCcusExcludingBase: unit -> CcuThunk list + member FindDllInfo: CompilationThreadToken * range * string -> ImportedBinary + member TryFindDllInfo: CompilationThreadToken * range * string * lookupOnly: bool -> option + member FindCcuFromAssemblyRef: CompilationThreadToken * range * ILAssemblyRef -> CcuResolutionResult + #if !NO_EXTENSIONTYPING member ProviderGeneratedTypeRoots: ProviderGeneratedType list #endif + member GetImportMap: unit -> Import.ImportMap + member DependencyProvider: DependencyProvider + /// Try to resolve a referenced assembly based on TcConfig settings. member TryResolveAssemblyReference: CompilationThreadToken * AssemblyReference * ResolveAssemblyReferenceMode -> OperationResult @@ -671,13 +685,33 @@ type TcImports = #endif /// Report unresolved references that also weren't consumed by any type providers. member ReportUnresolvedAssemblyReferences: UnresolvedAssemblyReference list -> unit + member SystemRuntimeContainsType: string -> bool member internal Base: TcImports option - static member BuildFrameworkTcImports : CompilationThreadToken * TcConfigProvider * AssemblyResolution list * AssemblyResolution list -> Cancellable - static member BuildNonFrameworkTcImports : CompilationThreadToken * TcConfigProvider * TcGlobals * TcImports * AssemblyResolution list * UnresolvedAssemblyReference list -> Cancellable - static member BuildTcImports : CompilationThreadToken * TcConfigProvider -> Cancellable + static member BuildFrameworkTcImports: + CompilationThreadToken * + TcConfigProvider * + AssemblyResolution list * + AssemblyResolution list + -> Cancellable + + static member BuildNonFrameworkTcImports: + CompilationThreadToken * + TcConfigProvider * + TcGlobals * + TcImports * + AssemblyResolution list * + UnresolvedAssemblyReference list * + DependencyProvider + -> Cancellable + + static member BuildTcImports: + CompilationThreadToken * + TcConfigProvider * + DependencyProvider + -> Cancellable //---------------------------------------------------------------------------- // Special resources in DLLs @@ -703,24 +737,22 @@ val WriteOptimizationData: TcGlobals * filename: string * inMem: bool * CcuThunk // #r and other directives //-------------------------------------------------------------------------- -//---------------------------------------------------------------------------- -// #r and other directives -//-------------------------------------------------------------------------- - /// Process #r in F# Interactive. /// Adds the reference to the tcImports and add the ccu to the type checking environment. val RequireDLL: CompilationThreadToken * TcImports * TcEnv * thisAssemblyName: string * referenceRange: range * file: string -> TcEnv * (ImportedBinary list * ImportedAssembly list) -/// Processing # commands +/// A general routine to process hash directives val ProcessMetaCommandsFromInput : - (('T -> range * string -> 'T) * ('T -> range * string -> 'T) * ('T -> IDependencyManagerProvider * Directive * range * string -> 'T) * ('T -> range * string -> unit)) - -> TcConfigBuilder * ParsedInput * string * 'T - -> 'T + (('T -> range * string -> 'T) * + ('T -> range * string * Directive -> 'T) * + ('T -> range * string -> unit)) + -> TcConfigBuilder * ParsedInput * string * 'T + -> 'T -/// Process all the #r, #I etc. in an input -val ApplyMetaCommandsFromInputToTcConfig: TcConfig * ParsedInput * string -> TcConfig +/// Process all the #r, #I etc. in an input. For non-scripts report warnings about ignored directives. +val ApplyMetaCommandsFromInputToTcConfig: TcConfig * ParsedInput * string * DependencyProvider -> TcConfig -/// Process the #nowarn in an input +/// Process the #nowarn in an input and integrate them into the TcConfig val ApplyNoWarnsToTcConfig: TcConfig * ParsedInput * string -> TcConfig //---------------------------------------------------------------------------- @@ -827,6 +859,9 @@ type LoadClosure = /// The resolved references along with the ranges of the #r positions in each file. References: (string * AssemblyResolution list) list + /// The resolved pacakge references along with the ranges of the #r positions in each file. + PackageReferences: (range * string list)[] + /// The list of references that were not resolved during load closure. UnresolvedReferences: UnresolvedAssemblyReference list @@ -853,8 +888,31 @@ type LoadClosure = // /// A temporary TcConfig is created along the way, is why this routine takes so many arguments. We want to be sure to use exactly the /// same arguments as the rest of the application. - static member ComputeClosureOfScriptText: CompilationThreadToken * legacyReferenceResolver: ReferenceResolver.Resolver * defaultFSharpBinariesDir: string * filename: string * sourceText: ISourceText * implicitDefines:CodeContext * useSimpleResolution: bool * useFsiAuxLib: bool * useSdkRefs: bool * lexResourceManager: Lexhelp.LexResourceManager * applyCompilerOptions: (TcConfigBuilder -> unit) * assumeDotNetFramework: bool * tryGetMetadataSnapshot: ILReaderTryGetMetadataSnapshot * reduceMemoryUsage: ReduceMemoryFlag -> LoadClosure + static member ComputeClosureOfScriptText: + CompilationThreadToken * + legacyReferenceResolver: ReferenceResolver.Resolver * + defaultFSharpBinariesDir: string * + filename: string * + sourceText: ISourceText * + implicitDefines:CodeContext * + useSimpleResolution: bool * + useFsiAuxLib: bool * + useSdkRefs: bool * + lexResourceManager: Lexhelp.LexResourceManager * + applyCompilerOptions: (TcConfigBuilder -> unit) * + assumeDotNetFramework: bool * + tryGetMetadataSnapshot: ILReaderTryGetMetadataSnapshot * + reduceMemoryUsage: ReduceMemoryFlag * + dependencyProvider: DependencyProvider + -> LoadClosure /// Analyze a set of script files and find the closure of their references. The resulting references are then added to the given TcConfig. /// Used from fsi.fs and fsc.fs, for #load and command line. - static member ComputeClosureOfScriptFiles: CompilationThreadToken * tcConfig:TcConfig * (string * range) list * implicitDefines:CodeContext * lexResourceManager: Lexhelp.LexResourceManager -> LoadClosure + static member ComputeClosureOfScriptFiles: + CompilationThreadToken * + tcConfig:TcConfig * + (string * range) list * + implicitDefines:CodeContext * + lexResourceManager: Lexhelp.LexResourceManager * + dependencyProvider: DependencyProvider + -> LoadClosure diff --git a/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs b/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs index 7a8946d1e59..d6c2f2f303d 100644 --- a/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs +++ b/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs @@ -183,11 +183,6 @@ module internal Utilities = p.WaitForExit() - if p.ExitCode <> 0 then - //Write StandardError.txt to err stream - for line in stdOut do Console.Out.WriteLine(line) - for line in stdErr do Console.Error.WriteLine(line) - p.ExitCode = 0, stdOut, stdErr | None -> false, Array.empty, Array.empty @@ -203,7 +198,7 @@ module internal Utilities = | None -> "" let arguments prefix = - sprintf "%s -restore %s %c%s%c /t:InteractivePackageManagement" prefix binLoggingArguments '\"' projectPath '\"' + sprintf "%s -restore %s %c%s%c /nologo /t:InteractivePackageManagement" prefix binLoggingArguments '\"' projectPath '\"' let workingDir = Path.GetDirectoryName projectPath diff --git a/src/fsharp/Microsoft.DotNet.DependencyManager/DependencyProvider.fs b/src/fsharp/Microsoft.DotNet.DependencyManager/DependencyProvider.fs index c3c7046ef29..522be2a40a5 100644 --- a/src/fsharp/Microsoft.DotNet.DependencyManager/DependencyProvider.fs +++ b/src/fsharp/Microsoft.DotNet.DependencyManager/DependencyProvider.fs @@ -68,19 +68,14 @@ module ReflectionHelper = e.InnerException | _ -> e -open ReflectionHelper -open RidHelpers - /// 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 = @@ -268,9 +263,17 @@ type ReflectionDependencyManagerProvider(theType: Type, /// Class is IDisposable type DependencyProvider (assemblyProbingPaths: AssemblyResolutionProbe, nativeProbingRoots: NativeResolutionProbe) = - let dllResolveHandler = new NativeDllResolveHandler(nativeProbingRoots) :> IDisposable + // Note: creating a NativeDllResolveHandler currently installs process-wide handlers + let dllResolveHandler = + match nativeProbingRoots with + | null -> { new IDisposable with member _.Dispose() = () } + | _ -> new NativeDllResolveHandler(nativeProbingRoots) :> IDisposable - let assemblyResolveHandler = new AssemblyResolveHandler(assemblyProbingPaths) :> IDisposable + // Note: creating a AssemblyResolveHandler currently installs process-wide handlers + let assemblyResolveHandler = + match assemblyProbingPaths with + | null -> { new IDisposable with member _.Dispose() = () } + | _ -> new AssemblyResolveHandler(assemblyProbingPaths) :> IDisposable // Resolution Path = Location of FSharp.Compiler.Private.dll let assemblySearchPaths = lazy ( @@ -327,11 +330,11 @@ type DependencyProvider (assemblyProbingPaths: AssemblyResolutionProbe, nativePr None managers - let cache = ConcurrentDictionary<_,IResolveDependenciesResult>(HashIdentity.Structural) + let cache = ConcurrentDictionary<_,Result>(HashIdentity.Structural) - /// Returns a formatted error message for the host to presentconstruct with just nativeProbing handler - new (nativeProbingRoots: NativeResolutionProbe) = - new DependencyProvider(Unchecked.defaultof, nativeProbingRoots) + new (nativeProbingRoots: NativeResolutionProbe) = new DependencyProvider(null, nativeProbingRoots) + + new () = new DependencyProvider(null, null) /// Returns a formatted help messages for registered dependencymanagers for the host to present member _.GetRegisteredDependencyManagerHelpText (compilerTools, outputDir, errorReport) = [| @@ -396,21 +399,25 @@ type DependencyProvider (assemblyProbingPaths: AssemblyResolutionProbe, nativePr let key = (packageManager.Key, scriptExt, Seq.toArray packageManagerTextLines, executionTfm, executionRid, implicitIncludeDir, mainScriptName, fileName) - cache.GetOrAdd(key, System.Func<_,_>(fun _ -> - try - let executionRid = - if isNull executionRid then - RidHelpers.platformRid - else - executionRid - packageManager.ResolveDependencies(implicitIncludeDir, mainScriptName, fileName, scriptExt, packageManagerTextLines, executionTfm, executionRid) - - with e -> - let e = stripTieWrapper e - let err, msg = (DependencyManager.SR.packageManagerError(e.Message)) - reportError.Invoke(ErrorReportType.Error, err, msg) - ReflectionDependencyManagerProvider.MakeResultFromFields(false, arrEmpty, arrEmpty, seqEmpty, seqEmpty, seqEmpty) - )) + let result = + cache.GetOrAdd(key, System.Func<_,_>(fun _ -> + try + let executionRid = + if isNull executionRid then + RidHelpers.platformRid + else + executionRid + Ok (packageManager.ResolveDependencies(implicitIncludeDir, mainScriptName, fileName, scriptExt, packageManagerTextLines, executionTfm, executionRid)) + + with e -> + let e = stripTieWrapper e + Error (DependencyManager.SR.packageManagerError(e.Message)) + )) + match result with + | Ok res -> res + | Error (errorNumber, errorData) -> + reportError.Invoke(ErrorReportType.Error, errorNumber, errorData) + ReflectionDependencyManagerProvider.MakeResultFromFields(false, arrEmpty, arrEmpty, seqEmpty, seqEmpty, seqEmpty) interface IDisposable with diff --git a/src/fsharp/Microsoft.DotNet.DependencyManager/DependencyProvider.fsi b/src/fsharp/Microsoft.DotNet.DependencyManager/DependencyProvider.fsi index 2f16b59fd99..aa9b38f88ba 100644 --- a/src/fsharp/Microsoft.DotNet.DependencyManager/DependencyProvider.fsi +++ b/src/fsharp/Microsoft.DotNet.DependencyManager/DependencyProvider.fsi @@ -56,16 +56,23 @@ type ErrorReportType = type ResolvingErrorReport = delegate of ErrorReportType * int * string -> unit /// Provides DependencyManagement functions. -/// Class is IDisposable +/// +/// The class incrementally collects IDependencyManagerProvider, indexed by key, and +/// queries them. These are found and instantiated with respect to the compilerTools and outputDir +/// provided each time the TryFindDependencyManagerByKey and TryFindDependencyManagerInPath are +/// executed, which are assumed to be invariant over the lifetime of the DependencyProvider. type DependencyProvider = interface System.IDisposable - /// Construct a new DependencyProvider - new: assemblyProbingPaths: AssemblyResolutionProbe * nativeProbingRoots: NativeResolutionProbe -> DependencyProvider + /// Construct a new DependencyProvider with no dynamic load handlers (only for compilation/analysis) + new: unit -> DependencyProvider - /// Construct a new DependencyProvider + /// Construct a new DependencyProvider with only native resolution new: nativeProbingRoots: NativeResolutionProbe -> DependencyProvider + /// Construct a new DependencyProvider with managed and native resolution + new: assemblyProbingPaths: AssemblyResolutionProbe * nativeProbingRoots: NativeResolutionProbe -> DependencyProvider + /// Returns a formatted help messages for registered dependencymanagers for the host to present member GetRegisteredDependencyManagerHelpText: string seq * string * ResolvingErrorReport -> string[] diff --git a/src/fsharp/Microsoft.DotNet.DependencyManager/NativeDllResolveHandler.fs b/src/fsharp/Microsoft.DotNet.DependencyManager/NativeDllResolveHandler.fs index 60b52982cc5..6a4477631c0 100644 --- a/src/fsharp/Microsoft.DotNet.DependencyManager/NativeDllResolveHandler.fs +++ b/src/fsharp/Microsoft.DotNet.DependencyManager/NativeDllResolveHandler.fs @@ -101,8 +101,9 @@ type NativeDllResolveHandlerCoreClr (_nativeProbingRoots: NativeResolutionProbe) let probe = match _nativeProbingRoots with | null -> None - | _ -> _nativeProbingRoots.Invoke() - |> Seq.tryPick(fun root -> + | _ -> + _nativeProbingRoots.Invoke() + |> Seq.tryPick(fun root -> probingFileNames name |> Seq.tryPick(fun name -> let path = Path.Combine(root, name) if File.Exists(path) then diff --git a/src/fsharp/Microsoft.DotNet.DependencyManager/NativeDllResolveHandler.fsi b/src/fsharp/Microsoft.DotNet.DependencyManager/NativeDllResolveHandler.fsi index c98b5f142e7..d1aa22fe49c 100644 --- a/src/fsharp/Microsoft.DotNet.DependencyManager/NativeDllResolveHandler.fsi +++ b/src/fsharp/Microsoft.DotNet.DependencyManager/NativeDllResolveHandler.fsi @@ -18,7 +18,7 @@ type NativeResolutionProbe = delegate of Unit -> seq // Cut down AssemblyLoadContext, for loading native libraries type NativeDllResolveHandler = - /// Construct a new DependencyProvider + /// Construct a new NativeDllResolveHandler new: nativeProbingRoots: NativeResolutionProbe -> NativeDllResolveHandler interface IDisposable diff --git a/src/fsharp/fsc.fs b/src/fsharp/fsc.fs index 4892c4a07fb..f30e913a7a4 100644 --- a/src/fsharp/fsc.fs +++ b/src/fsharp/fsc.fs @@ -50,6 +50,7 @@ open FSharp.Compiler.TypedTreeOps open FSharp.Compiler.TcGlobals open FSharp.Compiler.TypeChecker open FSharp.Compiler.XmlDoc +open Microsoft.DotNet.DependencyManager open FSharp.Compiler.AbstractIL.Internal.StrongNameSign @@ -202,7 +203,7 @@ let TypeCheck (ctok, tcConfig, tcImports, tcGlobals, errorLogger: ErrorLogger, a exiter.Exit 1 /// Check for .fsx and, if present, compute the load closure for of #loaded files. -let AdjustForScriptCompile(ctok, tcConfigB: TcConfigBuilder, commandLineSourceFiles, lexResourceManager) = +let AdjustForScriptCompile(ctok, tcConfigB: TcConfigBuilder, commandLineSourceFiles, lexResourceManager, dependencyProvider) = let combineFilePath file = try @@ -227,7 +228,8 @@ let AdjustForScriptCompile(ctok, tcConfigB: TcConfigBuilder, commandLineSourceFi if IsScript filename then let closure = LoadClosure.ComputeClosureOfScriptFiles - (ctok, tcConfig, [filename, rangeStartup], CodeContext.Compilation, lexResourceManager=lexResourceManager) + (ctok, tcConfig, [filename, rangeStartup], CodeContext.Compilation, + lexResourceManager, dependencyProvider) // Record the references from the analysis of the script. The full resolutions are recorded as the corresponding #I paths used to resolve them // are local to the scripts and not added to the tcConfigB (they are added to localized clones of the tcConfigB). @@ -1763,6 +1765,8 @@ let main0(ctok, argv, legacyReferenceResolver, bannerAlreadyPrinted, // Share intern'd strings across all lexing/parsing let lexResourceManager = new Lexhelp.LexResourceManager() + let dependencyProvider = new DependencyProvider() + // process command line, flags and collect filenames let sourceFiles = @@ -1771,7 +1775,7 @@ let main0(ctok, argv, legacyReferenceResolver, bannerAlreadyPrinted, try let sourceFiles = let files = ProcessCommandLineFlags (tcConfigB, setProcessThreadLocals, lcidFromCodePage, argv) - AdjustForScriptCompile(ctok, tcConfigB, files, lexResourceManager) + AdjustForScriptCompile(ctok, tcConfigB, files, lexResourceManager, dependencyProvider) sourceFiles with e -> @@ -1855,12 +1859,12 @@ let main0(ctok, argv, legacyReferenceResolver, bannerAlreadyPrinted, if tcConfig.printAst then inputs |> List.iter (fun (input, _filename) -> printf "AST:\n"; printfn "%+A" input; printf "\n") - let tcConfig = (tcConfig, inputs) ||> List.fold (fun z (x, m) -> ApplyMetaCommandsFromInputToTcConfig(z, x, m)) + let tcConfig = (tcConfig, inputs) ||> List.fold (fun z (x, m) -> ApplyMetaCommandsFromInputToTcConfig(z, x, m, dependencyProvider)) let tcConfigP = TcConfigProvider.Constant tcConfig // Import other assemblies ReportTime tcConfig "Import non-system references" - let tcImports = TcImports.BuildNonFrameworkTcImports(ctok, tcConfigP, tcGlobals, frameworkTcImports, otherRes, knownUnresolved) |> Cancellable.runWithoutCancellation + let tcImports = TcImports.BuildNonFrameworkTcImports(ctok, tcConfigP, tcGlobals, frameworkTcImports, otherRes, knownUnresolved, dependencyProvider) |> Cancellable.runWithoutCancellation // register tcImports to be disposed in future disposables.Register tcImports @@ -1988,6 +1992,7 @@ let main0OfAst (ctok, legacyReferenceResolver, reduceMemoryUsage, assemblyName, delayForFlagsLogger.ForwardDelayedDiagnostics tcConfigB exiter.Exit 1 + let dependencyProvider = new DependencyProvider() let errorLogger = errorLoggerProvider.CreateErrorLoggerUpToMaxErrors(tcConfigB, exiter) // Install the global error logger and never remove it. This logger does have all command-line flags considered. @@ -2010,12 +2015,12 @@ let main0OfAst (ctok, legacyReferenceResolver, reduceMemoryUsage, assemblyName, use unwindParsePhase = PushThreadBuildPhaseUntilUnwind (BuildPhase.Parse) let meta = Directory.GetCurrentDirectory() - let tcConfig = (tcConfig,inputs) ||> List.fold (fun tcc inp -> ApplyMetaCommandsFromInputToTcConfig (tcc, inp,meta)) + let tcConfig = (tcConfig,inputs) ||> List.fold (fun tcc inp -> ApplyMetaCommandsFromInputToTcConfig (tcc, inp, meta, dependencyProvider)) let tcConfigP = TcConfigProvider.Constant tcConfig // Import other assemblies ReportTime tcConfig "Import non-system references" - let tcImports = TcImports.BuildNonFrameworkTcImports(ctok, tcConfigP, tcGlobals, frameworkTcImports, otherRes, knownUnresolved) |> Cancellable.runWithoutCancellation + let tcImports = TcImports.BuildNonFrameworkTcImports(ctok, tcConfigP, tcGlobals, frameworkTcImports, otherRes, knownUnresolved, dependencyProvider) |> Cancellable.runWithoutCancellation // register tcImports to be disposed in future disposables.Register tcImports diff --git a/src/fsharp/fsi/fsi.fs b/src/fsharp/fsi/fsi.fs index 8e714f71dd1..40850bd7749 100644 --- a/src/fsharp/fsi/fsi.fs +++ b/src/fsharp/fsi/fsi.fs @@ -601,7 +601,11 @@ let internal directoryName (s:string) = //---------------------------------------------------------------------------- /// Process the command line options -type internal FsiCommandLineOptions(fsi: FsiEvaluationSessionHostConfig, argv: string[], tcConfigB, fsiConsoleOutput: FsiConsoleOutput) = +type internal FsiCommandLineOptions(fsi: FsiEvaluationSessionHostConfig, + argv: string[], + tcConfigB, + fsiConsoleOutput: FsiConsoleOutput) = + let mutable enableConsoleKeyProcessing = // Mono on Win32 doesn't implement correct console processing not (runningOnMono && System.Environment.OSVersion.Platform = System.PlatformID.Win32NT) @@ -757,6 +761,9 @@ type internal FsiCommandLineOptions(fsi: FsiEvaluationSessionHostConfig, argv: s stopProcessingRecovery e range0; failwithf "Error creating evaluation session: %A" e inputFilesAcc + // We need a dependency provider with native resolution. Managed resolution is handled by generated `#r` + let dependencyProvider = new DependencyProvider(NativeResolutionProbe(tcConfigB.GetNativeProbingRoots)) + do if tcConfigB.utf8output then let prev = Console.OutputEncoding @@ -797,7 +804,7 @@ type internal FsiCommandLineOptions(fsi: FsiEvaluationSessionHostConfig, argv: s fsiConsoleOutput.uprintfn """ #help;; // %s""" (FSIstrings.SR.fsiIntroTextHashhelpInfo()) if tcConfigB.langVersion.SupportsFeature(LanguageFeature.PackageManagement) then - for msg in tcConfigB.dependencyProvider.GetRegisteredDependencyManagerHelpText(tcConfigB.compilerToolPaths, getOutputDir tcConfigB, reportError m) do + for msg in dependencyProvider.GetRegisteredDependencyManagerHelpText(tcConfigB.compilerToolPaths, getOutputDir tcConfigB, reportError m) do fsiConsoleOutput.uprintfn "%s" msg fsiConsoleOutput.uprintfn """ #quit;; // %s""" (FSIstrings.SR.fsiIntroTextHashquitInfo()) (* last thing you want to do, last thing in the list - stands out more *) @@ -824,6 +831,8 @@ type internal FsiCommandLineOptions(fsi: FsiEvaluationSessionHostConfig, argv: s member __.SourceFiles = sourceFiles member __.Gui = gui + member _.DependencyProvider = dependencyProvider + /// Set the current ui culture for the current thread. let internal SetCurrentUICultureForThread (lcid : int option) = let culture = Thread.CurrentThread.CurrentUICulture @@ -1461,9 +1470,9 @@ type internal FsiDynamicCompiler | { Directive=_; LineStatus=_; Line=_; Range=m } :: _ -> let outputDir = tcConfigB.outputDir |> Option.defaultValue "" - match tcConfigB.dependencyProvider.TryFindDependencyManagerByKey(tcConfigB.compilerToolPaths, getOutputDir tcConfigB, reportError m, packageManagerKey) with + match fsiOptions.DependencyProvider.TryFindDependencyManagerByKey(tcConfigB.compilerToolPaths, getOutputDir tcConfigB, reportError m, packageManagerKey) with | null -> - errorR(Error(tcConfigB.dependencyProvider.CreatePackageManagerUnknownError(tcConfigB.compilerToolPaths, outputDir, packageManagerKey, reportError m), m)) + errorR(Error(fsiOptions.DependencyProvider.CreatePackageManagerUnknownError(tcConfigB.compilerToolPaths, outputDir, packageManagerKey, reportError m), m)) istate | dependencyManager -> let directive d = @@ -1475,13 +1484,10 @@ type internal FsiDynamicCompiler packageManagerLines |> List.map (fun line -> directive line.Directive, line.Line) try - let result = tcConfigB.dependencyProvider.Resolve(dependencyManager, ".fsx", packageManagerTextLines, reportError m, executionTfm, executionRid, tcConfigB.implicitIncludeDir, "stdin.fsx", "stdin.fsx") - match result.Success with - | false -> - tcConfigB.packageManagerLines <- PackageManagerLine.RemoveUnprocessedLines packageManagerKey tcConfigB.packageManagerLines - istate // error already reported - - | true -> + let result = fsiOptions.DependencyProvider.Resolve(dependencyManager, ".fsx", packageManagerTextLines, reportError m, executionTfm, executionRid, tcConfigB.implicitIncludeDir, "stdin.fsx", "stdin.fsx") + if result.Success then + for line in result.StdOut do Console.Out.WriteLine(line) + for line in result.StdError do Console.Error.WriteLine(line) tcConfigB.packageManagerLines <- PackageManagerLine.SetLinesAsProcessed packageManagerKey tcConfigB.packageManagerLines for folder in result.Roots do tcConfigB.AddIncludePath(m, folder, "") @@ -1489,6 +1495,15 @@ type internal FsiDynamicCompiler if not (isNil scripts) then fsiDynamicCompiler.EvalSourceFiles(ctok, istate, m, scripts, lexResourceManager, errorLogger) else istate + else + // Send outputs via diagnostics + if (result.StdOut.Length > 0 || result.StdError.Length > 0) then + for line in Array.append result.StdOut result.StdError do + errorR(Error(FSComp.SR.packageManagerError(line), m)) + //Write outputs in F# Interactive and compiler + tcConfigB.packageManagerLines <- PackageManagerLine.RemoveUnprocessedLines packageManagerKey tcConfigB.packageManagerLines + istate // error already reported + with _ -> // An exception occured during processing, so remove the lines causing the error from the package manager list. @@ -1502,8 +1517,27 @@ type internal FsiDynamicCompiler (fun () -> ProcessMetaCommandsFromInput ((fun st (m,nm) -> tcConfigB.TurnWarningOff(m,nm); st), - (fun st (m,nm) -> snd (fsiDynamicCompiler.EvalRequireReference (ctok, st, m, nm))), - (fun st (packageManagerPrefix, lt, m, nm) -> fsiDynamicCompiler.EvalDependencyManagerTextFragment (packageManagerPrefix, lt, m, nm); st), + (fun st (m, path, directive) -> + + let dm = tcImports.DependencyProvider.TryFindDependencyManagerInPath(tcConfigB.compilerToolPaths, getOutputDir tcConfigB, reportError m, path) + + match dm with + | _, dependencyManager when not(isNull dependencyManager) -> + if tcConfigB.langVersion.SupportsFeature(LanguageFeature.PackageManagement) then + fsiDynamicCompiler.EvalDependencyManagerTextFragment (dependencyManager, directive, m, path) + st + else + errorR(Error(FSComp.SR.packageManagementRequiresVFive(), m)) + st + + | _, _ when directive = Directive.Include -> + errorR(Error(FSComp.SR.poundiNotSupportedByRegisteredDependencyManagers(), m)) + st + + // #r "Assembly" + | path, _ -> + snd (fsiDynamicCompiler.EvalRequireReference (ctok, st, m, path)) + ), (fun _ _ -> ())) (tcConfigB, inp, Path.GetDirectoryName sourceFile, istate)) @@ -1517,7 +1551,11 @@ type internal FsiDynamicCompiler // Close the #load graph on each file and gather the inputs from the scripts. let tcConfig = TcConfig.Create(tcConfigB,validate=false) - let closure = LoadClosure.ComputeClosureOfScriptFiles(ctok, tcConfig, sourceFiles, CodeContext.CompilationAndEvaluation, lexResourceManager=lexResourceManager) + + let closure = + LoadClosure.ComputeClosureOfScriptFiles(ctok, tcConfig, + sourceFiles, CodeContext.CompilationAndEvaluation, + lexResourceManager, fsiOptions.DependencyProvider) // Intent "[Loading %s]\n" (String.concat "\n and " sourceFiles) fsiConsoleOutput.uprintf "[%s " (FSIstrings.SR.fsiLoadingFilesPrefixText()) @@ -2106,7 +2144,7 @@ type internal FsiInteractionProcessor /// Execute a single parsed interaction. Called on the GUI/execute/main thread. let ExecInteraction (ctok, tcConfig:TcConfig, istate, action:ParsedFsiInteraction, errorLogger: ErrorLogger) = let packageManagerDirective directive path m = - let dm = tcConfigB.dependencyProvider.TryFindDependencyManagerInPath(tcConfigB.compilerToolPaths, getOutputDir tcConfigB, reportError m, path) + let dm = fsiOptions.DependencyProvider.TryFindDependencyManagerInPath(tcConfigB.compilerToolPaths, getOutputDir tcConfigB, reportError m, path) match dm with | null, null -> // error already reported @@ -2749,7 +2787,8 @@ type FsiEvaluationSession (fsi: FsiEvaluationSessionHostConfig, argv:string[], i do updateBannerText() // setting the correct banner so that 'fsi -?' display the right thing - let fsiOptions = FsiCommandLineOptions(fsi, argv, tcConfigB, fsiConsoleOutput) + let fsiOptions = FsiCommandLineOptions(fsi, argv, tcConfigB, fsiConsoleOutput) + let fsiConsolePrompt = FsiConsolePrompt(fsiOptions, fsiConsoleOutput) do @@ -2791,7 +2830,7 @@ type FsiEvaluationSession (fsi: FsiEvaluationSessionHostConfig, argv:string[], i let tcImports = try - TcImports.BuildNonFrameworkTcImports(ctokStartup, tcConfigP, tcGlobals, frameworkTcImports, nonFrameworkResolutions, unresolvedReferences) |> Cancellable.runWithoutCancellation + TcImports.BuildNonFrameworkTcImports(ctokStartup, tcConfigP, tcGlobals, frameworkTcImports, nonFrameworkResolutions, unresolvedReferences, fsiOptions.DependencyProvider) |> Cancellable.runWithoutCancellation with e -> stopProcessingRecovery e range0; failwithf "Error creating evaluation session: %A" e diff --git a/src/fsharp/service/FSharpCheckerResults.fs b/src/fsharp/service/FSharpCheckerResults.fs index f3c78473b27..4835516ef03 100644 --- a/src/fsharp/service/FSharpCheckerResults.fs +++ b/src/fsharp/service/FSharpCheckerResults.fs @@ -1051,7 +1051,21 @@ type internal TypeCheckInfo let tip = wordL (TaggedTextOps.tagStringLiteral((resolved.prepareToolTip ()).TrimEnd([|'\n'|]))) FSharpStructuredToolTipText.FSharpToolTipText [FSharpStructuredToolTipElement.Single(tip, FSharpXmlDoc.None)] - | [] -> FSharpStructuredToolTipText.FSharpToolTipText [] + | [] -> + let matches = + match loadClosure with + | None -> None + | Some(loadClosure) -> + loadClosure.PackageReferences + |> Array.tryFind (fun (m, _) -> Range.rangeContainsPos m pos) + match matches with + | None -> FSharpStructuredToolTipText.FSharpToolTipText [] + | Some (_, lines) -> + let lines = lines |> List.filter (fun line -> not (line.StartsWith("//")) && not (String.IsNullOrEmpty line)) + FSharpStructuredToolTipText.FSharpToolTipText + [ for line in lines -> + let tip = wordL (TaggedTextOps.tagStringLiteral line) + FSharpStructuredToolTipElement.Single(tip, FSharpXmlDoc.None)] ErrorScope.Protect Range.range0 dataTipOfReferences @@ -1621,7 +1635,7 @@ module internal ParseAndCheckFile = | None -> // For non-scripts, check for disallow #r and #load. - ApplyMetaCommandsFromInputToTcConfig (tcConfig, parsedMainInput,Path.GetDirectoryName mainInputFileName) |> ignore + ApplyMetaCommandsFromInputToTcConfig (tcConfig, parsedMainInput, Path.GetDirectoryName mainInputFileName, tcImports.DependencyProvider) |> ignore // Type check a single file against an initial context, gleaning both errors and intellisense information. let CheckOneFile @@ -2161,8 +2175,8 @@ type FSharpCheckProjectResults type FsiInteractiveChecker(legacyReferenceResolver, reactorOps: IReactorOperations, tcConfig: TcConfig, - tcGlobals, - tcImports, + tcGlobals: TcGlobals, + tcImports: TcImports, tcState) = let keepAssemblyContents = false @@ -2192,7 +2206,9 @@ type FsiInteractiveChecker(legacyReferenceResolver, tcConfig.useSimpleResolution, tcConfig.useFsiAuxLib, tcConfig.useSdkRefs, new Lexhelp.LexResourceManager(), applyCompilerOptions, assumeDotNetFramework, - tryGetMetadataSnapshot=(fun _ -> None), reduceMemoryUsage=reduceMemoryUsage) + tryGetMetadataSnapshot=(fun _ -> None), + reduceMemoryUsage=reduceMemoryUsage, + dependencyProvider=tcImports.DependencyProvider) let! tcErrors, tcFileInfo = ParseAndCheckFile.CheckOneFile diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index f60d85adfc1..107f381364d 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -26,6 +26,8 @@ open FSharp.Compiler.TypeChecker open FSharp.Compiler.TypedTree open FSharp.Compiler.TypedTreeOps +open Microsoft.DotNet.DependencyManager + open Internal.Utilities.Collections [] @@ -1220,9 +1222,12 @@ type RawFSharpAssemblyDataBackedByLanguageService (tcConfig, tcGlobals, tcState: /// Manages an incremental build graph for the build of a single F# project type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInputs, nonFrameworkResolutions, unresolvedReferences, tcConfig: TcConfig, projectDirectory, outfile, - assemblyName, niceNameGen: NiceNameGenerator, lexResourceManager, - sourceFiles, loadClosureOpt: LoadClosure option, - keepAssemblyContents, keepAllBackgroundResolutions, maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification) = + assemblyName, niceNameGen: NiceNameGenerator, lexResourceManager, + sourceFiles, loadClosureOpt: LoadClosure option, + keepAssemblyContents, keepAllBackgroundResolutions, + maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + dependencyProviderOpt: DependencyProvider option) = let tcConfigP = TcConfigProvider.Constant tcConfig let fileParsed = new Event() @@ -1259,6 +1264,13 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput for (_, f, _) in sourceFiles do yield f |] + // For scripts, the dependency provider is already available. + // For projects create a fresh one for the project. + let dependencyProvider = + match dependencyProviderOpt with + | None -> new DependencyProvider() + | Some dependencyProvider -> dependencyProvider + //---------------------------------------------------- // START OF BUILD TASK FUNCTIONS @@ -1309,7 +1321,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput let! tcImports = cancellable { try - let! tcImports = TcImports.BuildNonFrameworkTcImports(ctok, tcConfigP, tcGlobals, frameworkTcImports, nonFrameworkResolutions, unresolvedReferences) + let! tcImports = TcImports.BuildNonFrameworkTcImports(ctok, tcConfigP, tcGlobals, frameworkTcImports, nonFrameworkResolutions, unresolvedReferences, dependencyProvider) #if !NO_EXTENSIONTYPING tcImports.GetCcusExcludingBase() |> Seq.iter (fun ccu -> // When a CCU reports an invalidation, merge them together and just report a @@ -1383,7 +1395,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput eventually { beforeFileChecked.Trigger filename - ApplyMetaCommandsFromInputToTcConfig (tcConfig, input, Path.GetDirectoryName filename) |> ignore + ApplyMetaCommandsFromInputToTcConfig (tcConfig, input, Path.GetDirectoryName filename, tcAcc.tcImports.DependencyProvider) |> ignore let sink = TcResultsSinkImpl(tcAcc.tcGlobals) let hadParseErrors = not (Array.isEmpty parseErrors) @@ -1740,7 +1752,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput /// CreateIncrementalBuilder (for background type checking). Note that fsc.fs also /// creates an incremental builder used by the command line compiler. - static member TryCreateBackgroundBuilderForProjectOptions + static member TryCreateIncrementalBuilderForProjectOptions (ctok, legacyReferenceResolver, defaultFSharpBinariesDir, frameworkTcImportsCache: FrameworkImportsCache, loadClosureOpt: LoadClosure option, @@ -1749,7 +1761,11 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput projectReferences, projectDirectory, useScriptResolutionRules, keepAssemblyContents, keepAllBackgroundResolutions, maxTimeShareMilliseconds, - tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification) = + tryGetMetadataSnapshot, suggestNamesForErrors, + keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + dependencyProviderOpt) = + let useSimpleResolutionSwitch = "--simpleresolution" cancellable { @@ -1864,14 +1880,16 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput yield Choice2Of2 pr, (fun (cache: TimeStampCache) ctok -> cache.GetProjectReferenceTimeStamp (pr, ctok)) ] let builder = - new IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInputs, nonFrameworkResolutions, unresolvedReferences, - tcConfig, projectDirectory, outfile, assemblyName, niceNameGen, - resourceManager, sourceFilesNew, loadClosureOpt, - keepAssemblyContents=keepAssemblyContents, - keepAllBackgroundResolutions=keepAllBackgroundResolutions, - maxTimeShareMilliseconds=maxTimeShareMilliseconds, - keepAllBackgroundSymbolUses=keepAllBackgroundSymbolUses, - enableBackgroundItemKeyStoreAndSemanticClassification=enableBackgroundItemKeyStoreAndSemanticClassification) + new IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInputs, + nonFrameworkResolutions, unresolvedReferences, + tcConfig, projectDirectory, outfile, assemblyName, niceNameGen, + resourceManager, sourceFilesNew, loadClosureOpt, + keepAssemblyContents, + keepAllBackgroundResolutions, + maxTimeShareMilliseconds, + keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + dependencyProviderOpt) return Some builder with e -> errorRecoveryNoRange e diff --git a/src/fsharp/service/IncrementalBuild.fsi b/src/fsharp/service/IncrementalBuild.fsi index e9f945cd970..c6bfe1e1f5a 100755 --- a/src/fsharp/service/IncrementalBuild.fsi +++ b/src/fsharp/service/IncrementalBuild.fsi @@ -16,6 +16,8 @@ open FSharp.Compiler.SyntaxTree open FSharp.Compiler.TcGlobals open FSharp.Compiler.TypedTree +open Microsoft.DotNet.DependencyManager + /// Lookup the global static cache for building the FrameworkTcImports type internal FrameworkImportsCache = new : size: int -> FrameworkImportsCache @@ -178,9 +180,29 @@ type internal IncrementalBuilder = /// Await the untyped parse results for a particular slot in the vector of parse results. /// /// This may be a marginally long-running operation (parses are relatively quick, only one file needs to be parsed) - member GetParseResultsForFile : CompilationThreadToken * filename:string -> Cancellable - - static member TryCreateBackgroundBuilderForProjectOptions : CompilationThreadToken * ReferenceResolver.Resolver * defaultFSharpBinariesDir: string * FrameworkImportsCache * scriptClosureOptions:LoadClosure option * sourceFiles:string list * commandLineArgs:string list * projectReferences: IProjectReference list * projectDirectory:string * useScriptResolutionRules:bool * keepAssemblyContents: bool * keepAllBackgroundResolutions: bool * maxTimeShareMilliseconds: int64 * tryGetMetadataSnapshot: ILBinaryReader.ILReaderTryGetMetadataSnapshot * suggestNamesForErrors: bool * keepAllBackgroundSymbolUses: bool * enableBackgroundItemKeyStoreAndSemanticClassification: bool -> Cancellable + member GetParseResultsForFile: CompilationThreadToken * filename:string -> Cancellable + + /// Create the incremental builder + static member TryCreateIncrementalBuilderForProjectOptions: + CompilationThreadToken * + ReferenceResolver.Resolver * + defaultFSharpBinariesDir: string * + FrameworkImportsCache * + scriptClosureOptions:LoadClosure option * + sourceFiles:string list * + commandLineArgs:string list * + projectReferences: IProjectReference list * + projectDirectory:string * + useScriptResolutionRules:bool * + keepAssemblyContents: bool * + keepAllBackgroundResolutions: bool * + maxTimeShareMilliseconds: int64 * + tryGetMetadataSnapshot: ILBinaryReader.ILReaderTryGetMetadataSnapshot * + suggestNamesForErrors: bool * + keepAllBackgroundSymbolUses: bool * + enableBackgroundItemKeyStoreAndSemanticClassification: bool * + dependencyProvider: DependencyProvider option + -> Cancellable /// Generalized Incremental Builder. This is exposed only for unit testing purposes. module internal IncrementalBuild = diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index 246f6d7440e..463e57a52d9 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -24,6 +24,8 @@ open FSharp.Compiler.SyntaxTree open FSharp.Compiler.TcGlobals open FSharp.Compiler.Text +open Microsoft.DotNet.DependencyManager + open Internal.Utilities open Internal.Utilities.Collections @@ -274,6 +276,14 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC let scriptClosureCacheLock = Lock() let frameworkTcImportsCache = FrameworkImportsCache(frameworkTcImportsCacheStrongSize) + // We currently share one global dependency provider for all scripts for the FSharpChecker. + // For projects, one is used per project. + // + // Sharing one for all scripts is necessary for good performance from GetProjectOptionsFromScript, + // which requires a dependency provider to process through the project options prior to working out + // if the cached incremental builder can be used for the project. + let dependencyProviderForScripts = new DependencyProvider() + /// CreateOneIncrementalBuilder (for background type checking). Note that fsc.fs also /// creates an incremental builder used by the command line compiler. let CreateOneIncrementalBuilder (ctok, options:FSharpProjectOptions, userOpName) = @@ -301,12 +311,15 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC member x.FileName = nm } ] let loadClosure = scriptClosureCacheLock.AcquireLock (fun ltok -> scriptClosureCache.TryGet (ltok, options)) + let! builderOpt, diagnostics = - IncrementalBuilder.TryCreateBackgroundBuilderForProjectOptions + IncrementalBuilder.TryCreateIncrementalBuilderForProjectOptions (ctok, legacyReferenceResolver, FSharpCheckerResultsSettings.defaultFSharpBinariesDir, frameworkTcImportsCache, loadClosure, Array.toList options.SourceFiles, Array.toList options.OtherOptions, projectReferences, options.ProjectDirectory, options.UseScriptResolutionRules, keepAssemblyContents, keepAllBackgroundResolutions, FSharpCheckerResultsSettings.maxTimeShareMilliseconds, - tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification) + tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + (if options.UseScriptResolutionRules then Some dependencyProviderForScripts else None)) match builderOpt with | None -> () @@ -817,8 +830,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC FSharpCheckerResultsSettings.defaultFSharpBinariesDir, filename, sourceText, CodeContext.Editing, useSimpleResolution, useFsiAuxLib, useSdkRefs, new Lexhelp.LexResourceManager(), applyCompilerOptions, assumeDotNetFramework, - tryGetMetadataSnapshot=tryGetMetadataSnapshot, - reduceMemoryUsage=reduceMemoryUsage) + tryGetMetadataSnapshot, reduceMemoryUsage, dependencyProviderForScripts) let otherFlags = [| yield "--noframework"; yield "--warn:3"; diff --git a/tests/FSharp.Compiler.Private.Scripting.UnitTests/DependencyManagerInteractiveTests.fs b/tests/FSharp.Compiler.Private.Scripting.UnitTests/DependencyManagerInteractiveTests.fs index a62667f01b2..68628e106aa 100644 --- a/tests/FSharp.Compiler.Private.Scripting.UnitTests/DependencyManagerInteractiveTests.fs +++ b/tests/FSharp.Compiler.Private.Scripting.UnitTests/DependencyManagerInteractiveTests.fs @@ -54,10 +54,8 @@ type DependencyManagerInteractiveTests() = #r @"nuget:System.Collections.Immutable.DoesNotExist, version=1.5.0" 0""" use script = new scriptHost() - let opt = script.Eval(text) |> getValue - let value = opt.Value - Assert.Equal(typeof, value.ReflectionType) - Assert.Equal(0, value.ReflectionValue :?> int) + let opt, errors = script.Eval(text) + Assert.Equal(errors.Length, 1) (* [] @@ -232,7 +230,7 @@ type DependencyManagerInteractiveTests() = /// Native dll resolution is not implemented on desktop #if NETCOREAPP - [] + [] member __.``Script using TorchSharp``() = let text = """ #r "nuget:RestoreSources=https://donsyme.pkgs.visualstudio.com/TorchSharp/_packaging/packages2/nuget/v3/index.json" diff --git a/tests/FSharp.Compiler.Private.Scripting.UnitTests/FSharpScriptTests.fs b/tests/FSharp.Compiler.Private.Scripting.UnitTests/FSharpScriptTests.fs index 3ab48c5d46e..151f376ad53 100644 --- a/tests/FSharp.Compiler.Private.Scripting.UnitTests/FSharpScriptTests.fs +++ b/tests/FSharp.Compiler.Private.Scripting.UnitTests/FSharpScriptTests.fs @@ -249,21 +249,26 @@ printfn ""%A"" result if line.Contains("error NU1101:") && line.Contains("FSharp.Really.Not.A.Package") then found <- found + 1 outp.Add(line)) - let _result, _errors = script.Eval("""#r "nuget:FSharp.Really.Not.A.Package" """) - Assert.True( (found = 1), "Expected to see output contains 'error NU1101:' and 'FSharp.Really.Not.A.Package'") + let result, errors = script.Eval("""#r "nuget:FSharp.Really.Not.A.Package" """) + Assert.True( (found = 0), "Did not expect to see output contains 'error NU1101:' and 'FSharp.Really.Not.A.Package'") + Assert.True( errors |> Seq.exists (fun error -> error.Message.Contains("error NU1101:")), "Expect to error containing 'error NU1101:'") + Assert.True( errors |> Seq.exists (fun error -> error.Message.Contains("FSharp.Really.Not.A.Package")), "Expect to error containing 'FSharp.Really.Not.A.Package'") [] member __.``Eval script with invalid PackageName should fail immediately and resolve one time only``() = use output = new RedirectConsoleOutput() use script = new FSharpScript(additionalArgs=[|"/langversion:preview"|]) let mutable foundResolve = 0 - output.OutputProduced.Add (fun line -> if line.Contains("Microsoft (R) Build Engine version") then foundResolve <- foundResolve + 1) - let _result, _errors = + output.OutputProduced.Add (fun line -> if line.Contains("error NU1101:") then foundResolve <- foundResolve + 1) + let result, errors = script.Eval(""" #r "nuget:FSharp.Really.Not.A.Package" #r "nuget:FSharp.Really.Not.Another.Package" """) - Assert.True( (foundResolve = 1), (sprintf "Expected to see 'Microsoft (R) Build Engine version' only once actually resolved %d times" foundResolve)) + Assert.True( (foundResolve = 0), (sprintf "Did not expected to see 'error NU1101:' in output" )) + Assert.Equal(2, (errors |> Seq.filter (fun error -> error.Message.Contains("error NU1101:")) |> Seq.length)) + Assert.Equal(1, (errors |> Seq.filter (fun error -> error.Message.Contains("FSharp.Really.Not.A.Package")) |> Seq.length)) + Assert.Equal(1, (errors |> Seq.filter (fun error -> error.Message.Contains("FSharp.Really.Not.Another.Package")) |> Seq.length)) [] member __.``ML - use assembly with ref dependencies``() = diff --git a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs index 3a26973ad29..8582f212786 100644 --- a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs +++ b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs @@ -42401,6 +42401,7 @@ Microsoft.DotNet.DependencyManager.DependencyProvider: System.Tuple`2[System.Int Microsoft.DotNet.DependencyManager.DependencyProvider: System.Tuple`2[System.String,Microsoft.DotNet.DependencyManager.IDependencyManagerProvider] TryFindDependencyManagerInPath(System.Collections.Generic.IEnumerable`1[System.String], System.String, Microsoft.DotNet.DependencyManager.ResolvingErrorReport, System.String) Microsoft.DotNet.DependencyManager.DependencyProvider: Void .ctor(Microsoft.DotNet.DependencyManager.AssemblyResolutionProbe, Microsoft.DotNet.DependencyManager.NativeResolutionProbe) Microsoft.DotNet.DependencyManager.DependencyProvider: Void .ctor(Microsoft.DotNet.DependencyManager.NativeResolutionProbe) +Microsoft.DotNet.DependencyManager.DependencyProvider: Void .ctor() Microsoft.DotNet.DependencyManager.ErrorReportType+Tags: Int32 Error Microsoft.DotNet.DependencyManager.ErrorReportType+Tags: Int32 Warning Microsoft.DotNet.DependencyManager.ErrorReportType: Boolean Equals(Microsoft.DotNet.DependencyManager.ErrorReportType) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs index 5810202d7af..387caea14d9 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs @@ -105,7 +105,7 @@ type internal FSharpAddOpenCodeFixProvider let! symbol = asyncMaybe { - let! lexerSymbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, context.Span.End, document.FilePath, defines, SymbolLookupKind.Greedy, false) + let! lexerSymbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, context.Span.End, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) return! checkResults.GetSymbolUseAtLocation(Line.fromZ linePos.Line, lexerSymbol.Ident.idRange.EndColumn, line.ToString(), lexerSymbol.FullIsland, userOpName=userOpName) } |> liftAsync diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs index 4f995284b90..2415fa3baca 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs @@ -169,7 +169,7 @@ type internal FSharpImplementInterfaceCodeFixProvider | _ -> Some context.Span.End let! interfaceState = queryInterfaceState appendBracketAt interfacePos tokens parsedInput - let! symbol = Tokenizer.getSymbolAtPosition(context.Document.Id, sourceText, fixupPosition, context.Document.FilePath, defines, SymbolLookupKind.Greedy, false) + let! symbol = Tokenizer.getSymbolAtPosition(context.Document.Id, sourceText, fixupPosition, context.Document.FilePath, defines, SymbolLookupKind.Greedy, false, false) let fcsTextLineNumber = textLine.LineNumber + 1 let lineContents = textLine.ToString() let! options = context.Document.GetOptionsAsync(cancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs index d4f564f75f3..2a185cdbb8e 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs @@ -61,7 +61,7 @@ type internal FSharpRenameUnusedValueCodeFixProvider let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, userOpName=userOpName) let m = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions - let! lexerSymbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, context.Span.Start, document.FilePath, defines, SymbolLookupKind.Greedy, false) + let! lexerSymbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, context.Span.Start, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) let lineText = (sourceText.Lines.GetLineFromPosition context.Span.Start).ToString() let! symbolUse = checkResults.GetSymbolUseAtLocation(m.StartLine, m.EndColumn, lineText, lexerSymbol.FullIsland, userOpName=userOpName) let symbolName = symbolUse.Symbol.DisplayName diff --git a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs index 6ceb996cf1e..ade8eb32a8f 100644 --- a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs +++ b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs @@ -58,7 +58,7 @@ type internal FSharpDocumentHighlightsService [] (checkerP let textLine = sourceText.Lines.GetLineFromPosition(position) let textLinePos = sourceText.Lines.GetLinePosition(position) let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! symbol = Tokenizer.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, SymbolLookupKind.Greedy, false) + let! symbol = Tokenizer.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, SymbolLookupKind.Greedy, false, false) let! _, _, checkFileResults = checker.ParseAndCheckDocument(filePath, textVersionHash, sourceText, options, languageServicePerformanceOptions, userOpName = userOpName) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland, userOpName=userOpName) let! symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol) |> liftAsync diff --git a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs index 9d05c920b8e..15704641c09 100644 --- a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs +++ b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs @@ -155,7 +155,7 @@ type internal InlineRenameService let textLine = sourceText.Lines.GetLineFromPosition(position) let textLinePos = sourceText.Lines.GetLinePosition(position) let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false) + let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, userOpName = userOpName) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.Text.ToString(), symbol.FullIsland, userOpName=userOpName) let! declLoc = symbolUse.GetDeclarationLocation(document) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index 22100b4f158..37635ea5d69 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -27,7 +27,7 @@ module internal SymbolHelpers = let fcsTextLineNumber = Line.fromZ textLinePos.Line let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions - let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false) + let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) let settings = document.FSharpOptions let! _, _, checkFileResults = checker.ParseAndCheckDocument(document.FilePath, textVersionHash, sourceText, projectOptions, settings.LanguageServicePerformance, userOpName = userOpName) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland, userOpName=userOpName) @@ -120,7 +120,7 @@ module internal SymbolHelpers = do! Option.guard (originalText.Length > 0) let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions - let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, symbolSpan.Start, document.FilePath, defines, SymbolLookupKind.Greedy, false) + let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, symbolSpan.Start, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, userOpName = userOpName) let textLine = sourceText.Lines.GetLineFromPosition(symbolSpan.Start) let textLinePos = sourceText.Lines.GetLinePosition(symbolSpan.Start) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs b/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs index 0c48144db02..2ecc21caf0d 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs @@ -33,7 +33,8 @@ type internal LexerSymbolKind = | GenericTypeParameter = 3 | StaticallyResolvedTypeParameter = 4 | ActivePattern = 5 - | Other = 6 + | String = 6 + | Other = 7 type internal LexerSymbol = { Kind: LexerSymbolKind @@ -147,11 +148,14 @@ module internal Tokenizer = | FSharpGlyph.Variable -> Glyph.Local | FSharpGlyph.Error -> Glyph.Error - let GetImageIdForSymbol(symbol:FSharpSymbol, kind:LexerSymbolKind) = + let GetImageIdForSymbol(symbolOpt:FSharpSymbol option, kind:LexerSymbolKind) = let imageId = match kind with | LexerSymbolKind.Operator -> KnownImageIds.Operator | _ -> + match symbolOpt with + | None -> KnownImageIds.Package + | Some symbol -> match symbol with | :? FSharpUnionCase as x -> match Some x.Accessibility with @@ -345,6 +349,7 @@ module internal Tokenizer = member token.IsIdentifier = (token.CharClass = FSharpTokenCharKind.Identifier) member token.IsOperator = (token.ColorClass = FSharpTokenColorKind.Operator) member token.IsPunctuation = (token.ColorClass = FSharpTokenColorKind.Punctuation) + member token.IsString = (token.ColorClass = FSharpTokenColorKind.String) /// This is the information we save for each token in a line for each active document. /// It is a memory-critical data structure - do not make larger. This used to be ~100 bytes class, is now 8-byte struct @@ -375,6 +380,7 @@ module internal Tokenizer = if token.IsOperator then LexerSymbolKind.Operator elif token.IsIdentifier then LexerSymbolKind.Ident elif token.IsPunctuation then LexerSymbolKind.Punctuation + elif token.IsString then LexerSymbolKind.String else LexerSymbolKind.Other Debug.Assert(uint32 token.Tag < 0xFFFFu) Debug.Assert(uint32 kind < 0xFFu) @@ -612,7 +618,8 @@ module internal Tokenizer = linePos: LinePosition, lineStr: string, lookupKind: SymbolLookupKind, - wholeActivePatterns: bool + wholeActivePatterns: bool, + allowStringToken: bool ) : LexerSymbol option = @@ -704,6 +711,7 @@ module internal Tokenizer = | LexerSymbolKind.StaticallyResolvedTypeParameter -> true | _ -> false) |> Option.orElseWith (fun _ -> tokensUnderCursor |> List.tryFind (fun token -> token.Kind = LexerSymbolKind.Operator)) + |> Option.orElseWith (fun _ -> if allowStringToken then tokensUnderCursor |> List.tryFind (fun token -> token.Kind = LexerSymbolKind.String) else None) |> Option.map (fun token -> let partialName = QuickParse.GetPartialLongNameEx(lineStr, token.RightColumn) let identStr = lineStr.Substring(token.LeftColumn, token.MatchedLength) @@ -767,13 +775,14 @@ module internal Tokenizer = fileName: string, defines: string list, lookupKind: SymbolLookupKind, - wholeActivePatterns: bool + wholeActivePatterns: bool, + allowStringToken: bool ) : LexerSymbol option = try let lineData, textLinePos, lineContents = getCachedSourceLineData(documentKey, sourceText, position, fileName, defines) - getSymbolFromSavedTokens(fileName, lineData.SavedTokens, textLinePos, lineContents, lookupKind, wholeActivePatterns) + getSymbolFromSavedTokens(fileName, lineData.SavedTokens, textLinePos, lineContents, lookupKind, wholeActivePatterns, allowStringToken) with | :? System.OperationCanceledException -> reraise() | ex -> diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs index f742a679474..9988f74d6c6 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs @@ -57,7 +57,7 @@ type internal FSharpFindUsagesService let lineNumber = sourceText.Lines.GetLinePosition(position).Line + 1 let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions - let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false) + let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(lineNumber, symbol.Ident.idRange.EndColumn, textLine, symbol.FullIsland, userOpName=userOpName) let! declaration = checkFileResults.GetDeclarationLocation (lineNumber, symbol.Ident.idRange.EndColumn, textLine, symbol.FullIsland, false, userOpName=userOpName) |> liftAsync let tags = FSharpGlyphTags.GetTags(Tokenizer.GetGlyphForSymbol (symbolUse.Symbol, symbol.Kind)) diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs index 4c3c00e6943..18221c0f316 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs @@ -172,7 +172,7 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions let! originTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (sourceText, originRange) let position = originTextSpan.Start - let! lexerSymbol = Tokenizer.getSymbolAtPosition (originDocument.Id, sourceText, position, originDocument.FilePath, defines, SymbolLookupKind.Greedy, false) + let! lexerSymbol = Tokenizer.getSymbolAtPosition (originDocument.Id, sourceText, position, originDocument.FilePath, defines, SymbolLookupKind.Greedy, false, false) let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line @@ -232,7 +232,7 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP let! _, _, checkFileResults = checker.ParseAndCheckDocument (originDocument, projectOptions, sourceText=sourceText, userOpName=userOpName) - let! lexerSymbol = Tokenizer.getSymbolAtPosition (originDocument.Id, sourceText, position,originDocument.FilePath, defines, SymbolLookupKind.Greedy, false) + let! lexerSymbol = Tokenizer.getSymbolAtPosition (originDocument.Id, sourceText, position,originDocument.FilePath, defines, SymbolLookupKind.Greedy, false, false) let idRange = lexerSymbol.Ident.idRange let! declarations = checkFileResults.GetDeclarationLocation (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland, preferSignature, userOpName=userOpName) |> liftAsync diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs index cd5d0fc143e..f5e4b1611f1 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs @@ -26,7 +26,7 @@ open Internal.Utilities.StructuredFormat type internal QuickInfo = { StructuredText: FSharpStructuredToolTipText Span: TextSpan - Symbol: FSharpSymbol + Symbol: FSharpSymbol option SymbolKind: LexerSymbolKind } module internal FSharpQuickInfo = @@ -58,7 +58,7 @@ module internal FSharpQuickInfo = // project options need to be retrieved because the signature file could be in another project let! extParsingOptions, extProjectOptions = projectInfoManager.TryGetOptionsByProject(document.Project, cancellationToken) let extDefines = CompilerEnvironment.GetCompilationDefinesForEditing extParsingOptions - let! extLexerSymbol = Tokenizer.getSymbolAtPosition(extDocId, extSourceText, extSpan.Start, declRange.FileName, extDefines, SymbolLookupKind.Greedy, true) + let! extLexerSymbol = Tokenizer.getSymbolAtPosition(extDocId, extSourceText, extSpan.Start, declRange.FileName, extDefines, SymbolLookupKind.Greedy, true, true) let! _, _, extCheckFileResults = checker.ParseAndCheckDocument(extDocument, extProjectOptions, allowStaleResults=true, sourceText=extSourceText, userOpName = userOpName) let! extQuickInfoText = @@ -75,7 +75,7 @@ module internal FSharpQuickInfo = return { StructuredText = extQuickInfoText Span = span - Symbol = extSymbolUse.Symbol + Symbol = Some extSymbolUse.Symbol SymbolKind = extLexerSymbol.Kind } } @@ -88,26 +88,25 @@ module internal FSharpQuickInfo = position: int, cancellationToken: CancellationToken ) - : Async<(FSharpSymbolUse * QuickInfo option * QuickInfo option) option> = + : Async<(range * QuickInfo option * QuickInfo option) option> = asyncMaybe { let! sourceText = document.GetTextAsync cancellationToken let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions - let! lexerSymbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, true) + let! lexerSymbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, true, true) let idRange = lexerSymbol.Ident.idRange let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, allowStaleResults = true, sourceText=sourceText, userOpName = userOpName) let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() - let! symbolUse = checkFileResults.GetSymbolUseAtLocation (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland, userOpName=userOpName) /// Gets the QuickInfo information for the orignal target - let getTargetSymbolQuickInfo () = + let getTargetSymbolQuickInfo (symbol, tag) = asyncMaybe { let! targetQuickInfo = checkFileResults.GetStructuredToolTipText - (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland, FSharpTokenTag.IDENT, userOpName=userOpName) |> liftAsync + (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland,tag, userOpName=userOpName) |> liftAsync match targetQuickInfo with | FSharpToolTipText [] @@ -116,21 +115,29 @@ module internal FSharpQuickInfo = let! targetTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (sourceText, lexerSymbol.Range) return { StructuredText = targetQuickInfo Span = targetTextSpan - Symbol = symbolUse.Symbol + Symbol = symbol SymbolKind = lexerSymbol.Kind } } + match lexerSymbol.Kind with + | LexerSymbolKind.String -> + let! targetQuickInfo = getTargetSymbolQuickInfo (None, FSharpTokenTag.STRING) + return lexerSymbol.Range, None, Some targetQuickInfo + + | _ -> + let! symbolUse = checkFileResults.GetSymbolUseAtLocation (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland, userOpName=userOpName) + // if the target is in a signature file, adjusting the quick info is unnecessary if isSignatureFile document.FilePath then - let! targetQuickInfo = getTargetSymbolQuickInfo() - return symbolUse, None, Some targetQuickInfo + let! targetQuickInfo = getTargetSymbolQuickInfo (Some symbolUse.Symbol, FSharpTokenTag.IDENT) + return symbolUse.RangeAlternate, None, Some targetQuickInfo else // find the declaration location of the target symbol, with a preference for signature files let! findSigDeclarationResult = checkFileResults.GetDeclarationLocation (idRange.StartLine, idRange.EndColumn, lineText, lexerSymbol.FullIsland, preferFlag=true, userOpName=userOpName) |> liftAsync // it is necessary to retrieve the backup quick info because this acquires // the textSpan designating where we want the quick info to appear. - let! targetQuickInfo = getTargetSymbolQuickInfo() + let! targetQuickInfo = getTargetSymbolQuickInfo (Some symbolUse.Symbol, FSharpTokenTag.IDENT) let! result = match findSigDeclarationResult with @@ -146,15 +153,16 @@ module internal FSharpQuickInfo = match findImplDefinitionResult with | FSharpFindDeclResult.DeclNotFound _ - | FSharpFindDeclResult.ExternalDecl _ -> return symbolUse, Some sigQuickInfo, None + | FSharpFindDeclResult.ExternalDecl _ -> + return symbolUse.RangeAlternate, Some sigQuickInfo, None | FSharpFindDeclResult.DeclFound declRange -> let! implQuickInfo = getQuickInfoFromRange(checker, projectInfoManager, document, declRange, cancellationToken) - return symbolUse, Some sigQuickInfo, Some { implQuickInfo with Span = targetQuickInfo.Span } + return symbolUse.RangeAlternate, Some sigQuickInfo, Some { implQuickInfo with Span = targetQuickInfo.Span } } | _ -> async.Return None |> liftAsync - return result |> Option.defaultValue (symbolUse, None, Some targetQuickInfo) + return result |> Option.defaultValue (symbolUse.RangeAlternate, None, Some targetQuickInfo) } type internal FSharpAsyncQuickInfoSource @@ -180,7 +188,7 @@ type internal FSharpAsyncQuickInfoSource let textLine = sourceText.Lines.GetLineFromPosition position let textLineNumber = textLine.LineNumber + 1 // Roslyn line numbers are zero-based let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions - let! symbol = Tokenizer.getSymbolAtPosition (documentId, sourceText, position, filePath, defines, SymbolLookupKind.Precise, true) + let! symbol = Tokenizer.getSymbolAtPosition (documentId, sourceText, position, filePath, defines, SymbolLookupKind.Precise, true, true) let! res = checkFileResults.GetStructuredToolTipText (textLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland, FSharpTokenTag.IDENT, userOpName=FSharpQuickInfo.userOpName) |> liftAsync match res with | FSharpToolTipText [] @@ -190,7 +198,7 @@ type internal FSharpAsyncQuickInfoSource let! symbolSpan = RoslynHelpers.TryFSharpRangeToTextSpan (sourceText, symbol.Range) return { StructuredText = res Span = symbolSpan - Symbol = symbolUse.Symbol + Symbol = Some symbolUse.Symbol SymbolKind = symbol.Kind } } @@ -211,20 +219,20 @@ type internal FSharpAsyncQuickInfoSource | false -> Task.FromResult(null) | true -> let triggerPoint = triggerPoint.GetValueOrDefault() - let documentationBuilder = XmlDocumentation.CreateDocumentationBuilder(xmlMemberIndexService) asyncMaybe { let document = textBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges() - let! symbolUse, sigQuickInfo, targetQuickInfo = FSharpQuickInfo.getQuickInfo(checkerProvider.Checker, projectInfoManager, document, triggerPoint.Position, cancellationToken) + let! symbolUseRange, sigQuickInfo, targetQuickInfo = FSharpQuickInfo.getQuickInfo(checkerProvider.Checker, projectInfoManager, document, triggerPoint.Position, cancellationToken) let getTrackingSpan (span:TextSpan) = textBuffer.CurrentSnapshot.CreateTrackingSpan(span.Start, span.Length, SpanTrackingMode.EdgeInclusive) + let documentationBuilder = XmlDocumentation.CreateDocumentationBuilder(xmlMemberIndexService) match sigQuickInfo, targetQuickInfo with | None, None -> return null | Some quickInfo, None - | None, Some quickInfo-> + | None, Some quickInfo -> let mainDescription, docs = FSharpAsyncQuickInfoSource.BuildSingleQuickInfoItem documentationBuilder quickInfo let imageId = Tokenizer.GetImageIdForSymbol(quickInfo.Symbol, quickInfo.SymbolKind) - let navigation = QuickInfoNavigation(statusBar, checkerProvider.Checker, projectInfoManager, document, symbolUse.RangeAlternate) + let navigation = QuickInfoNavigation(statusBar, checkerProvider.Checker, projectInfoManager, document, symbolUseRange) let content = QuickInfoViewProvider.provideContent(imageId, mainDescription, docs, navigation) let span = getTrackingSpan quickInfo.Span return QuickInfoItem(span, content) @@ -254,7 +262,7 @@ type internal FSharpAsyncQuickInfoSource ] |> ResizeArray let docs = joinWithLineBreaks [documentation; typeParameterMap; usage; exceptions] let imageId = Tokenizer.GetImageIdForSymbol(targetQuickInfo.Symbol, targetQuickInfo.SymbolKind) - let navigation = QuickInfoNavigation(statusBar, checkerProvider.Checker, projectInfoManager, document, symbolUse.RangeAlternate) + let navigation = QuickInfoNavigation(statusBar, checkerProvider.Checker, projectInfoManager, document, symbolUseRange) let content = QuickInfoViewProvider.provideContent(imageId, mainDescription, docs, navigation) let span = getTrackingSpan targetQuickInfo.Span return QuickInfoItem(span, content) diff --git a/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs b/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs index 755b66ca085..4408b27e5ac 100644 --- a/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs +++ b/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs @@ -52,7 +52,7 @@ module GoToDefinitionServiceTests = let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! lexerSymbol = Tokenizer.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, SymbolLookupKind.Greedy, false) + let! lexerSymbol = Tokenizer.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, SymbolLookupKind.Greedy, false, false) let! _, _, checkFileResults = checker.ParseAndCheckDocument (filePath, textVersionHash, sourceText, options, LanguageServicePerformanceOptions.Default, userOpName=userOpName) |> Async.RunSynchronously let declarations = checkFileResults.GetDeclarationLocation (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland, false, userOpName=userOpName) |> Async.RunSynchronously