diff --git a/src/Compiler/Driver/GraphChecking/DependencyResolution.fs b/src/Compiler/Driver/GraphChecking/DependencyResolution.fs
index 33e5b6450d5..c3206efbdfb 100644
--- a/src/Compiler/Driver/GraphChecking/DependencyResolution.fs
+++ b/src/Compiler/Driver/GraphChecking/DependencyResolution.fs
@@ -17,29 +17,33 @@ let queryTriePartial (trie: TrieNode) (path: LongIdentifier) : TrieNode option =
visit trie path
-let mapNodeToQueryResult (node: TrieNode option) : QueryTrieNodeResult =
+let mapNodeToQueryResult (currentFileIndex: FileIndex) (node: TrieNode option) : QueryTrieNodeResult =
match node with
| Some finalNode ->
- if Set.isEmpty finalNode.Files then
+ if
+ Set.isEmpty finalNode.Files
+ // If this node exposes files which the current index cannot see, we consider it not to have data at all.
+ || Set.forall (fun idx -> idx >= currentFileIndex) finalNode.Files
+ then
QueryTrieNodeResult.NodeDoesNotExposeData
else
QueryTrieNodeResult.NodeExposesData(finalNode.Files)
| None -> QueryTrieNodeResult.NodeDoesNotExist
/// Find a path in the Trie.
-let queryTrie (trie: TrieNode) (path: LongIdentifier) : QueryTrieNodeResult =
- queryTriePartial trie path |> mapNodeToQueryResult
+let queryTrie (currentFileIndex: FileIndex) (trie: TrieNode) (path: LongIdentifier) : QueryTrieNodeResult =
+ queryTriePartial trie path |> mapNodeToQueryResult currentFileIndex
/// Same as 'queryTrie' but allows passing in a path combined from two parts, avoiding list allocation.
-let queryTrieDual (trie: TrieNode) (path1: LongIdentifier) (path2: LongIdentifier) : QueryTrieNodeResult =
+let queryTrieDual (currentFileIndex: FileIndex) (trie: TrieNode) (path1: LongIdentifier) (path2: LongIdentifier) : QueryTrieNodeResult =
match queryTriePartial trie path1 with
| Some intermediateNode -> queryTriePartial intermediateNode path2
| None -> None
- |> mapNodeToQueryResult
+ |> mapNodeToQueryResult currentFileIndex
/// Process namespace declaration.
let processNamespaceDeclaration (trie: TrieNode) (path: LongIdentifier) (state: FileContentQueryState) : FileContentQueryState =
- let queryResult = queryTrie trie path
+ let queryResult = queryTrie state.CurrentFile trie path
match queryResult with
| QueryTrieNodeResult.NodeDoesNotExist -> state
@@ -49,7 +53,7 @@ let processNamespaceDeclaration (trie: TrieNode) (path: LongIdentifier) (state:
/// Process an "open" statement.
/// The statement could link to files and/or should be tracked as an open namespace.
let processOpenPath (trie: TrieNode) (path: LongIdentifier) (state: FileContentQueryState) : FileContentQueryState =
- let queryResult = queryTrie trie path
+ let queryResult = queryTrie state.CurrentFile trie path
match queryResult with
| QueryTrieNodeResult.NodeDoesNotExist -> state
@@ -99,12 +103,13 @@ let rec processStateEntry (trie: TrieNode) (state: FileContentQueryState) (entry
||> Array.fold (fun state takeParts ->
let path = List.take takeParts path
// process the name was if it were a FQN
- let stateAfterFullIdentifier = processIdentifier (queryTrieDual trie [] path) state
+ let stateAfterFullIdentifier =
+ processIdentifier (queryTrieDual state.CurrentFile trie [] path) state
// Process the name in combination with the existing open namespaces
(stateAfterFullIdentifier, state.OpenNamespaces)
||> Set.fold (fun acc openNS ->
- let queryResult = queryTrieDual trie openNS path
+ let queryResult = queryTrieDual state.CurrentFile trie openNS path
processIdentifier queryResult acc))
| FileContentEntry.NestedModule (nestedContent = nestedContent) ->
@@ -137,7 +142,7 @@ let collectGhostDependencies (fileIndex: FileIndex) (trie: TrieNode) (result: Fi
// For each opened namespace, if none of already resolved dependencies define it, return the top-most file that defines it.
Set.toArray result.OpenedNamespaces
|> Array.choose (fun path ->
- match queryTrie trie path with
+ match queryTrie fileIndex trie path with
| QueryTrieNodeResult.NodeExposesData _
| QueryTrieNodeResult.NodeDoesNotExist -> None
| QueryTrieNodeResult.NodeDoesNotExposeData ->
diff --git a/src/Compiler/Driver/GraphChecking/DependencyResolution.fsi b/src/Compiler/Driver/GraphChecking/DependencyResolution.fsi
index 3ceac318ec7..a2c52adcea9 100644
--- a/src/Compiler/Driver/GraphChecking/DependencyResolution.fsi
+++ b/src/Compiler/Driver/GraphChecking/DependencyResolution.fsi
@@ -1,9 +1,12 @@
/// Logic for constructing a file dependency graph for the purposes of parallel type-checking.
module internal FSharp.Compiler.GraphChecking.DependencyResolution
-/// Query a TrieNode to find a certain path.
+///
+/// Query a TrieNode to find a certain path.
+/// The result will take the current file index into account to determine if the result node contains data.
+///
/// This code is only used directly in unit tests.
-val queryTrie: trie: TrieNode -> path: LongIdentifier -> QueryTrieNodeResult
+val queryTrie: currentFileIndex: FileIndex -> trie: TrieNode -> path: LongIdentifier -> QueryTrieNodeResult
/// Process an open path (found in the ParsedInput) with a given FileContentQueryState.
/// This code is only used directly in unit tests.
diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/QueryTrieTests.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/QueryTrieTests.fs
index 1c06fe9dee2..b82014b178a 100644
--- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/QueryTrieTests.fs
+++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/QueryTrieTests.fs
@@ -762,7 +762,7 @@ let private fantomasCoreTrie: TrieNode =
[]
let ``Query non existing node in trie`` () =
let result =
- queryTrie fantomasCoreTrie [ "System"; "System"; "Runtime"; "CompilerServices" ]
+ queryTrie 7 fantomasCoreTrie [ "System"; "System"; "Runtime"; "CompilerServices" ]
match result with
| QueryTrieNodeResult.NodeDoesNotExist -> Assert.Pass()
@@ -770,7 +770,7 @@ let ``Query non existing node in trie`` () =
[]
let ``Query node that does not expose data in trie`` () =
- let result = queryTrie fantomasCoreTrie [ "Fantomas"; "Core" ]
+ let result = queryTrie 7 fantomasCoreTrie [ "Fantomas"; "Core" ]
match result with
| QueryTrieNodeResult.NodeDoesNotExposeData -> Assert.Pass()
@@ -779,7 +779,7 @@ let ``Query node that does not expose data in trie`` () =
[]
let ``Query module node that exposes one file`` () =
let result =
- queryTrie fantomasCoreTrie [ "Fantomas"; "Core"; "ISourceTextExtensions" ]
+ queryTrie 7 fantomasCoreTrie [ "Fantomas"; "Core"; "ISourceTextExtensions" ]
match result with
| QueryTrieNodeResult.NodeExposesData file ->
diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/Scenarios.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/Scenarios.fs
index a60e8e7c116..1211e6b19f3 100644
--- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/Scenarios.fs
+++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/Scenarios.fs
@@ -747,6 +747,28 @@ module S
let main _ =
F.br ()
0
+"""
+ (set [| 0 |])
+ ]
+ scenario
+ "Ghost dependency via top-level namespace"
+ [
+ sourceFile
+ "Graph.fs"
+ """
+namespace Graphoscope.Graph
+
+type UndirectedGraph = obj
+"""
+ Set.empty
+ sourceFile
+ "DiGraph.fs"
+ """
+namespace Graphoscope
+
+open Graphoscope
+
+type DiGraph = obj
"""
(set [| 0 |])
]