diff --git a/fcs/FSharp.Compiler.Service.netstandard/FSharp.Compiler.Service.netstandard.fsproj b/fcs/FSharp.Compiler.Service.netstandard/FSharp.Compiler.Service.netstandard.fsproj index 535a4a590be..5b035b3c9fc 100644 --- a/fcs/FSharp.Compiler.Service.netstandard/FSharp.Compiler.Service.netstandard.fsproj +++ b/fcs/FSharp.Compiler.Service.netstandard/FSharp.Compiler.Service.netstandard.fsproj @@ -81,6 +81,7 @@ FSStrings.resx + FSStrings.resources --module Microsoft.FSharp.Compiler.AbstractIL.Internal.AsciiParser --open Microsoft.FSharp.Compiler.AbstractIL --internal --lexlib Internal.Utilities.Text.Lexing --parslib Internal.Utilities.Text.Parsing diff --git a/src/fsharp/import.fs b/src/fsharp/import.fs index dab3e6eec93..6db631a0318 100644 --- a/src/fsharp/import.fs +++ b/src/fsharp/import.fs @@ -4,6 +4,7 @@ module internal Microsoft.FSharp.Compiler.Import open System.Reflection +open System.Collections.Concurrent open System.Collections.Generic open Microsoft.FSharp.Compiler.AbstractIL.IL @@ -52,7 +53,7 @@ type AssemblyLoader = /// serves as an interface through to the tables stored in the primary TcImports structures defined in CompileOps.fs. [] type ImportMap(g:TcGlobals,assemblyLoader:AssemblyLoader) = - let typeRefToTyconRefCache = new System.Collections.Generic.Dictionary() + let typeRefToTyconRefCache = ConcurrentDictionary() member this.g = g member this.assemblyLoader = assemblyLoader member this.ILTypeRefToTyconRefCache = typeRefToTyconRefCache diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 0028b767202..3fd19dacfe5 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1698,14 +1698,16 @@ 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 (ctok, legacyReferenceResolver, defaultFSharpBinariesDir, frameworkTcImportsCache: FrameworkImportsCache, loadClosureOpt:LoadClosure option, sourceFiles:string list, commandLineArgs:string list, projectReferences, projectDirectory, useScriptResolutionRules, keepAssemblyContents, keepAllBackgroundResolutions, maxTimeShareMilliseconds) = - let targetProfileSwitch = "--targetprofile:" let useSimpleResolutionSwitch = "--simpleresolution" cancellable { // Trap and report warnings and errors from creation. - use errorScope = new ErrorScope() - let! builderOpt = + let delayedLogger = CapturingErrorLogger("IncrementalBuilderCreation") + use _unwindEL = PushErrorLoggerPhaseUntilUnwind (fun _ -> delayedLogger) + use _unwindBP = PushThreadBuildPhaseUntilUnwind BuildPhase.Parameter + + let! builderOpt = cancellable { try @@ -1741,22 +1743,12 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput #else tcConfigB.useSimpleResolution <- (getSwitchValue useSimpleResolutionSwitch) |> Option.isSome #endif - match (getSwitchValue targetProfileSwitch) with - | Some v -> - let _s = v - CompileOptions.SetTargetProfile tcConfigB v - | None -> () - // Apply command-line arguments and collect more source files if they are in the arguments let sourceFilesNew = ApplyCommandLineArgs(tcConfigB, sourceFiles, commandLineArgs) // Never open PDB files for the language service, even if --standalone is specified tcConfigB.openDebugInformationForLaterStaticLinking <- false - match commandLineArgs |> Seq.tryFind(fun s -> s.StartsWith(targetProfileSwitch)) with - | Some arg -> - let profile = arg.Substring(targetProfileSwitch.Length) - CompileOptions.SetTargetProfile tcConfigB profile - | _ -> () + tcConfigB, sourceFilesNew match loadClosureOpt with @@ -1825,7 +1817,18 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput return None } - return builderOpt, errorScope.Diagnostics + let diagnostics = + match builderOpt with + | Some builder -> + let errorSeverityOptions = builder.TcConfig.errorSeverityOptions + let errorLogger = CompilationErrorLogger("IncrementalBuilderCreation", errorSeverityOptions) + delayedLogger.CommitDelayedDiagnostics(errorLogger) + errorLogger.GetErrors() |> List.map (fun (d, severity) -> d, severity = FSharpErrorSeverity.Error) + | _ -> + delayedLogger.Diagnostics + |> List.map (fun (d, isError) -> FSharpErrorInfo.CreateFromException(d, isError, range.Zero)) + + return builderOpt, diagnostics } static member KeepBuilderAlive (builderOpt: IncrementalBuilder option) = match builderOpt with diff --git a/src/fsharp/symbols/SymbolHelpers.fs b/src/fsharp/symbols/SymbolHelpers.fs index 935fbf25476..c13731306dd 100644 --- a/src/fsharp/symbols/SymbolHelpers.fs +++ b/src/fsharp/symbols/SymbolHelpers.fs @@ -150,15 +150,14 @@ type internal CompilationErrorLogger (debugName: string, options: FSharpErrorSev override x.DiagnosticSink(exn, isError) = if isError || ReportWarningAsError options exn then - diagnostics.Add(exn, isError) + diagnostics.Add(exn, FSharpErrorSeverity.Error) errorCount <- errorCount + 1 else if ReportWarning options exn then - diagnostics.Add(exn, isError) + diagnostics.Add(exn, FSharpErrorSeverity.Warning) override x.ErrorCount = errorCount - member x.GetErrors() = - [ for (e, isError) in diagnostics -> e, (if isError then FSharpErrorSeverity.Error else FSharpErrorSeverity.Warning) ] + member x.GetErrors() = List.ofSeq diagnostics /// This represents the global state established as each task function runs as part of the build. diff --git a/tests/service/Common.fs b/tests/service/Common.fs index 8b76fa107c7..d038ff178a1 100644 --- a/tests/service/Common.fs +++ b/tests/service/Common.fs @@ -160,6 +160,23 @@ let mkProjectCommandLineArgsForScript (dllName, fileNames) = |] #endif +let mkTestFileAndOptions source additionalArgs = + let fileName = Path.ChangeExtension(Path.GetTempFileName(), ".fs") + let project = Path.GetTempFileName() + let dllName = Path.ChangeExtension(project, ".dll") + let projFileName = Path.ChangeExtension(project, ".fsproj") + let fileSource1 = "module M" + File.WriteAllText(fileName, fileSource1) + + let args = Array.append (mkProjectCommandLineArgs (dllName, [fileName])) additionalArgs + let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + fileName, options + +let parseAndCheckFile fileName source options = + match checker.ParseAndCheckFileInProject(fileName, 0, source, options) |> Async.RunSynchronously with + | parseResults, FSharpCheckFileAnswer.Succeeded(checkResults) -> parseResults, checkResults + | _ -> failwithf "Parsing aborted unexpectedly..." + let parseAndCheckScript (file, input) = #if DOTNETCORE diff --git a/tests/service/ProjectAnalysisTests.fs b/tests/service/ProjectAnalysisTests.fs index 6cf1795c1cf..93463d6a203 100644 --- a/tests/service/ProjectAnalysisTests.fs +++ b/tests/service/ProjectAnalysisTests.fs @@ -5195,3 +5195,16 @@ type A(i:int) = | Some decl -> failwithf "unexpected declaration %A" decl | None -> failwith "declaration list is empty" + + +[] +[] +[] +[] +[] +let ``#4030, Incremental builder creation warnings`` (args, errorSeverities) = + let source = "module M" + let fileName, options = mkTestFileAndOptions source args + + let _, checkResults = parseAndCheckFile fileName source options + checkResults.Errors |> Array.map (fun e -> e.Severity = FSharpErrorSeverity.Error) |> shouldEqual errorSeverities diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index ee47b94e085..b5da2f6bb24 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -85,10 +85,9 @@ type internal FSharpCompletionProvider else let triggerPosition = caretPosition - 1 let triggerChar = sourceText.[triggerPosition] - let prevChar = sourceText.[triggerPosition - 1] - + // do not trigger completion if it's not single dot, i.e. range expression - if not Settings.IntelliSense.ShowAfterCharIsTyped && prevChar = '.' then + if not Settings.IntelliSense.ShowAfterCharIsTyped && triggerPosition > 0 && sourceText.[triggerPosition - 1] = '.' then false else let documentId, filePath, defines = getInfo() diff --git a/vsintegration/tests/unittests/CompletionProviderTests.fs b/vsintegration/tests/unittests/CompletionProviderTests.fs index 6a6c64f44b1..5e0212f62bd 100644 --- a/vsintegration/tests/unittests/CompletionProviderTests.fs +++ b/vsintegration/tests/unittests/CompletionProviderTests.fs @@ -273,6 +273,18 @@ xVal**y let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo) Assert.IsTrue(triggered, "Completion should trigger after typing an identifier that follows a mathematical operation") +[] +let ShouldTriggerCompletionAtStartOfFileWithInsertion = + let fileContents = """ +l""" + + let marker = "l" + let caretPosition = fileContents.IndexOf(marker) + marker.Length + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let getInfo() = documentId, filePath, [] + let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo) + Assert.IsTrue(triggered, "Completion should trigger after typing an Insertion character at the top of the file, e.g. a function definition in a new script file.") + [] let ShouldDisplayTypeMembers() = let fileContents = """