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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 47 additions & 15 deletions src/Compiler/Service/IncrementalBuild.fs
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,8 @@ module ValueOption =
| ValueSome x -> Some x
| _ -> None

type private TypeCheck = TcInfo * TcResultsSinkImpl * CheckedImplFile option * string
type private SingleFileDiagnostics = (PhasedDiagnostic * FSharpDiagnosticSeverity) array
type private TypeCheck = TcInfo * TcResultsSinkImpl * CheckedImplFile option * string * SingleFileDiagnostics

/// Bound model of an underlying syntax and typed tree.
type BoundModel private (
Expand Down Expand Up @@ -299,7 +300,7 @@ type BoundModel private (
| _ ->
None
}
return tcInfo, sink, implFile, fileName
return tcInfo, sink, implFile, fileName, newErrors
}

let skippedImplemetationTypeCheck =
Expand All @@ -322,13 +323,13 @@ type BoundModel private (

let getTcInfo (typeCheck: GraphNode<TypeCheck>) =
node {
let! tcInfo , _, _, _ = typeCheck.GetOrComputeValue()
let! tcInfo , _, _, _, _ = typeCheck.GetOrComputeValue()
return tcInfo
} |> GraphNode

let getTcInfoExtras (typeCheck: GraphNode<TypeCheck>) =
node {
let! _ , sink, implFile, fileName = typeCheck.GetOrComputeValue()
let! _ , sink, implFile, fileName, _ = typeCheck.GetOrComputeValue()
// Build symbol keys
let itemKeyStore, semanticClassification =
if enableBackgroundItemKeyStoreAndSemanticClassification then
Expand Down Expand Up @@ -365,22 +366,36 @@ type BoundModel private (
}
} |> GraphNode

let defaultTypeCheck = node { return prevTcInfo, TcResultsSinkImpl(tcGlobals), None, "default typecheck - no syntaxTree", [||] }
let typeCheckNode = syntaxTreeOpt |> Option.map getTypeCheck |> Option.defaultValue defaultTypeCheck |> GraphNode
let tcInfoExtras = getTcInfoExtras typeCheckNode
let diagnostics =
node {
let! _, _, _, _, diags = typeCheckNode.GetOrComputeValue()
return diags
} |> GraphNode

let startComputingFullTypeCheck =
node {
let! _ = tcInfoExtras.GetOrComputeValue()
return! diagnostics.GetOrComputeValue()
}

let tcInfo, tcInfoExtras =
let defaultTypeCheck = node { return prevTcInfo, TcResultsSinkImpl(tcGlobals), None, "default typecheck - no syntaxTree" }
let typeCheckNode = syntaxTreeOpt |> Option.map getTypeCheck |> Option.defaultValue defaultTypeCheck |> GraphNode
match tcStateOpt with
| Some tcState -> tcState
| _ ->
match skippedImplemetationTypeCheck with
| Some info ->
| Some tcInfo ->
// For skipped implementation sources do full type check only when requested.
GraphNode.FromResult info, getTcInfoExtras typeCheckNode
GraphNode.FromResult tcInfo, tcInfoExtras
| _ ->
let tcInfoExtras = getTcInfoExtras typeCheckNode
// start computing extras, so that typeCheckNode can be GC'd quickly
tcInfoExtras.GetOrComputeValue() |> Async.AwaitNodeCode |> Async.Ignore |> Async.Start
startComputingFullTypeCheck |> Async.AwaitNodeCode |> Async.Ignore |> Async.Start
getTcInfo typeCheckNode, tcInfoExtras

member val Diagnostics = diagnostics

member val TcInfo = tcInfo

member val TcInfoExtras = tcInfoExtras
Expand Down Expand Up @@ -633,7 +648,7 @@ module IncrementalBuilderHelpers =
#if !NO_TYPEPROVIDERS
,importsInvalidatedByTypeProvider: Event<unit>
#endif
) : NodeCode<BoundModel> =
) : NodeCode<BoundModel * SingleFileDiagnostics> =

node {
let diagnosticsLogger = CompilationDiagnosticLogger("CombineImportedAssembliesTask", tcConfig.diagnosticsOptions)
Expand Down Expand Up @@ -706,11 +721,11 @@ module IncrementalBuilderHelpers =
fileChecked,
tcInfo,
None
)
), initialErrors
}

/// Finish up the typechecking to produce outputs for the rest of the compilation process
let FinalizeTypeCheckTask (tcConfig: TcConfig) tcGlobals partialCheck assemblyName outfile (boundModels: GraphNode<BoundModel> seq) =
let FinalizeTypeCheckTask (tcConfig: TcConfig) tcGlobals partialCheck assemblyName outfile (initialErrors: SingleFileDiagnostics) (boundModels: GraphNode<BoundModel> seq) =
node {
let diagnosticsLogger = CompilationDiagnosticLogger("FinalizeTypeCheckTask", tcConfig.diagnosticsOptions)
use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.TypeCheck)
Expand Down Expand Up @@ -796,7 +811,18 @@ module IncrementalBuilderHelpers =
mkSimpleAssemblyRef assemblyName, ProjectAssemblyDataResult.Unavailable true, None

let finalBoundModel = Array.last computedBoundModels
let diagnostics = diagnosticsLogger.GetDiagnostics() :: finalInfo.tcDiagnosticsRev

// Collect diagnostics. This will type check in parallel any implementation files skipped so far.
let! partialDiagnostics =
computedBoundModels
|> Seq.map (fun m -> m.Diagnostics.GetOrComputeValue())
|> NodeCode.Parallel
let diagnostics = [
diagnosticsLogger.GetDiagnostics()
yield! partialDiagnostics |> Seq.rev
initialErrors
]

let! finalBoundModelWithErrors = finalBoundModel.Finish(diagnostics, Some topAttrs)
return ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt, finalBoundModelWithErrors
}
Expand All @@ -805,6 +831,7 @@ module IncrementalBuilderHelpers =
type IncrementalBuilderInitialState =
{
initialBoundModel: BoundModel
initialErrors: SingleFileDiagnostics
tcGlobals: TcGlobals
referencedAssemblies: ImmutableArray<Choice<string, IProjectReference> * (TimeStampCache -> DateTime)>
tcConfig: TcConfig
Expand All @@ -830,6 +857,7 @@ type IncrementalBuilderInitialState =
static member Create
(
initialBoundModel: BoundModel,
initialErrors: SingleFileDiagnostics,
tcGlobals,
nonFrameworkAssemblyInputs,
tcConfig: TcConfig,
Expand All @@ -852,6 +880,7 @@ type IncrementalBuilderInitialState =
let initialState =
{
initialBoundModel = initialBoundModel
initialErrors = initialErrors
tcGlobals = tcGlobals
referencedAssemblies = nonFrameworkAssemblyInputs |> ImmutableArray.ofSeq
tcConfig = tcConfig
Expand Down Expand Up @@ -920,13 +949,15 @@ module IncrementalBuilderStateHelpers =
let createFinalizeBoundModelGraphNode (initialState: IncrementalBuilderInitialState) (boundModels: GraphNode<BoundModel> seq) =
GraphNode(node {
use _ = Activity.start "GetCheckResultsAndImplementationsForProject" [|Activity.Tags.project, initialState.outfile|]
let! initialErrors = initialState.initialBoundModel.Diagnostics.GetOrComputeValue()
let! result =
FinalizeTypeCheckTask
initialState.tcConfig
initialState.tcGlobals
initialState.enablePartialTypeChecking
initialState.assemblyName
initialState.outfile
initialErrors
boundModels
return result, DateTime.UtcNow
})
Expand Down Expand Up @@ -1532,7 +1563,7 @@ type IncrementalBuilder(initialState: IncrementalBuilderInitialState, state: Inc

let defaultTimeStamp = DateTime.UtcNow

let! initialBoundModel =
let! initialBoundModel, initialErrors =
CombineImportedAssembliesTask(
assemblyName,
tcConfig,
Expand Down Expand Up @@ -1572,6 +1603,7 @@ type IncrementalBuilder(initialState: IncrementalBuilderInitialState, state: Inc
let initialState =
IncrementalBuilderInitialState.Create(
initialBoundModel,
initialErrors,
tcGlobals,
nonFrameworkAssemblyInputs,
tcConfig,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@
<Compile Include="Signatures\TypeTests.fs" />
<Compile Include="Signatures\SigGenerationRoundTripTests.fs" />
<Compile Include="Signatures\NestedTypeTests.fs" />
<Compile Include="Signatures\MissingDiagnostic.fs" />
<Compile Include="StaticLinking\StaticLinking.fs" />
<Compile Include="FSharpChecker\CommonWorkflows.fs" />
<Compile Include="FSharpChecker\SymbolUse.fs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
module FSharp.Compiler.ComponentTests.Signatures.MissingDiagnostic

open Xunit
open FSharp.Test
open FSharp.Test.Compiler

let implementation = """
module Foo

let a (b: int) : int = 'x'
"""

let signature = """
module Foo

val a: b: int -> int
"""

[<Fact>]
let ``Compile gives errors`` () =
Fsi signature
|> withAdditionalSourceFile (FsSource implementation)
|> compile
|> shouldFail
|> withSingleDiagnostic (Error 1, Line 4, Col 24, Line 4,Col 27, "This expression was expected to have type
'int'
but here has type
'char' ")

[<Fact>]
let ``Type check project with signature file doesn't get the diagnostic`` () =
Fsi signature
|> withAdditionalSourceFile (FsSource implementation)
|> typecheckProject false
|> fun projectResults ->
projectResults.Diagnostics |> ignore
Assert.False (projectResults.Diagnostics |> Array.isEmpty)

[<Fact>]
let ``Type check project without signature file does get the diagnostic`` () =
Fs implementation
|> typecheckProject false
|> fun projectResults ->
projectResults.Diagnostics |> ignore
Assert.False (projectResults.Diagnostics |> Array.isEmpty)

[<Fact>]
let ``Enabling enablePartialTypeChecking = true doesn't change the problem`` () =
Fsi signature
|> withAdditionalSourceFile (FsSource implementation)
|> typecheckProject true
|> fun projectResults ->
projectResults.Diagnostics |> ignore
Assert.False (projectResults.Diagnostics |> Array.isEmpty)
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ let private getGenericParametersNamesFor
(additionalFile: SourceCodeFileKind)
: string array =
let typeCheckResult =
cUnit |> withAdditionalSourceFile additionalFile |> typecheckProject
cUnit |> withAdditionalSourceFile additionalFile |> typecheckProject false

assert (Array.isEmpty typeCheckResult.Diagnostics)

Expand Down
4 changes: 2 additions & 2 deletions tests/FSharp.Test.Utilities/Compiler.fs
Original file line number Diff line number Diff line change
Expand Up @@ -917,7 +917,7 @@ module rec Compiler =
CompilerAssert.TypeCheck(options, fileName, source)
| _ -> failwith "Typecheck only supports F#"

let typecheckProject (cUnit: CompilationUnit) : FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults =
let typecheckProject enablePartialTypeChecking (cUnit: CompilationUnit) : FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults =
match cUnit with
| FS fsSource ->
let options = fsSource.Options |> Array.ofList
Expand All @@ -935,7 +935,7 @@ module rec Compiler =
|> async.Return

let sourceFiles = Array.map fst sourceFiles
CompilerAssert.TypeCheckProject(options, sourceFiles, getSourceText)
CompilerAssert.TypeCheckProject(options, sourceFiles, getSourceText, enablePartialTypeChecking)
| _ -> failwith "Typecheck only supports F#"

let run (result: CompilationResult) : CompilationResult =
Expand Down
4 changes: 2 additions & 2 deletions tests/FSharp.Test.Utilities/CompilerAssert.fs
Original file line number Diff line number Diff line change
Expand Up @@ -858,8 +858,8 @@ Updated automatically, please check diffs in your pull request, changes must be
static member TypeCheckSingleError (source: string) (expectedSeverity: FSharpDiagnosticSeverity) (expectedErrorNumber: int) (expectedErrorRange: int * int * int * int) (expectedErrorMsg: string) =
CompilerAssert.TypeCheckWithErrors source [| expectedSeverity, expectedErrorNumber, expectedErrorRange, expectedErrorMsg |]

static member TypeCheckProject(options: string array, sourceFiles: string array, getSourceText) : FSharpCheckProjectResults =
let checker = FSharpChecker.Create(documentSource = DocumentSource.Custom getSourceText)
static member TypeCheckProject(options: string array, sourceFiles: string array, getSourceText, enablePartialTypeChecking) : FSharpCheckProjectResults =
let checker = FSharpChecker.Create(documentSource = DocumentSource.Custom getSourceText, enablePartialTypeChecking = enablePartialTypeChecking)
let defaultOptions = defaultProjectOptions TargetFramework.Current
let projectOptions = { defaultOptions with OtherOptions = Array.append options defaultOptions.OtherOptions; SourceFiles = sourceFiles }

Expand Down