From b8c135e4b7db9ebbb7806d9ec1239d9f7263cde5 Mon Sep 17 00:00:00 2001 From: nojaf Date: Wed, 15 Feb 2023 17:55:52 +0100 Subject: [PATCH] Avoid duplicate nested modules in Trie. --- .../Driver/GraphChecking/TrieMapping.fs | 6 +- .../TypeChecks/Graph/TrieMappingTests.fs | 185 ++++++++++++++++++ 2 files changed, 188 insertions(+), 3 deletions(-) diff --git a/src/Compiler/Driver/GraphChecking/TrieMapping.fs b/src/Compiler/Driver/GraphChecking/TrieMapping.fs index 2435cfc167f..4ab2e64dc58 100644 --- a/src/Compiler/Driver/GraphChecking/TrieMapping.fs +++ b/src/Compiler/Driver/GraphChecking/TrieMapping.fs @@ -100,7 +100,8 @@ let mkDictFromKeyValuePairs (items: KeyValuePair<'TKey, 'TValue> list) = let dict = Dictionary(Seq.length items) for KeyValue (k, v) in items do - dict.Add(k, v) + if not (dict.ContainsKey(k)) then + dict.Add(k, v) dict @@ -188,8 +189,7 @@ let processSynModuleOrNamespace<'Decl> mkSingletonDict name { Current = current; Children = node } |> continuation) tail - if List.isEmpty name then - // This can happen for a namespace global. + if kind = SynModuleOrNamespaceKind.AnonModule then // We collect the child nodes from the decls decls |> List.choose (mkTrieForDeclaration idx) |> mkDictFromKeyValuePairs else diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/TrieMappingTests.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/TrieMappingTests.fs index 838f9846a5c..a3f7508ffe7 100644 --- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/TrieMappingTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/TrieMappingTests.fs @@ -313,3 +313,188 @@ let _ = () Assert.AreEqual(1, trie.Children.Count) let aNode = trie.Children.["A"] Assert.AreEqual(set [| 0 |], aNode.Files) + +[] +let ``Two nested modules with the same name, in named namespace`` () = + let trie = + TrieMapping.mkTrie + [| + { + Idx = 0 + FileName = "A.fs" + ParsedInput = + parseSourceCode ( + "A.fs", + """ +namespace N + +[] +module ``module`` = + let f x = x + 1 +module ``module`` = + let g x = x + 1 +""" ) + } + |] + + Assert.AreEqual(1, trie.Children.Count) + let node = trie.Children.["N"] + Assert.AreEqual(1, node.Children.Count) + +[] +let ``Two nested modules with the same name, in namespace global`` () = + let trie = + TrieMapping.mkTrie + [| + { + Idx = 0 + FileName = "A.fs" + ParsedInput = + parseSourceCode ( + "A.fs", + """ +namespace global + +[] +module ``module`` = + let f x = x + 1 +module ``module`` = + let g x = x + 1 +""" ) + } + |] + + // namespace global leads to a Root entry, no further processing will be done. + Assert.AreEqual(set [| 0 |], trie.Files) + +[] +let ``Two nested modules with the same name, in anonymous module`` () = + let trie = + TrieMapping.mkTrie + [| + { + Idx = 1 + FileName = "Program.fs" + ParsedInput = + parseSourceCode ( + "Program.fs", + """ +[] +module ``module`` = + let f x = x + 1 +module ``module`` = + let g x = x + 1 +""" ) + } + |] + + Assert.AreEqual(1, trie.Children.Count) + Assert.True(trie.Children.ContainsKey("module")) + +[] +let ``Two nested modules with the same name, in nested module`` () = + let trie = + TrieMapping.mkTrie + [| + { + Idx = 0 + FileName = "A.fs" + ParsedInput = + parseSourceCode ( + "A.fs", + """ +namespace A + +module B = + + [] + module ``module`` = + let f x = x + 1 + module ``module`` = + let g x = x + 1 +""" ) + } + |] + + let bNode = trie.Children["A"].Children["B"] + Assert.AreEqual(1, bNode.Children.Count) + Assert.True(bNode.Children.ContainsKey("module")) + +[] +let ``Two nested modules with the same name, in nested module in signature file`` () = + let trie = + TrieMapping.mkTrie + [| + { + Idx = 0 + FileName = "A.fsi" + ParsedInput = + parseSourceCode ( + "A.fsi", + """ +namespace A + +module B = + + [] + module ``module`` = begin end + module ``module`` = begin end +""" ) + } + |] + + let bNode = trie.Children["A"].Children["B"] + Assert.AreEqual(1, bNode.Children.Count) + Assert.True(bNode.Children.ContainsKey("module")) + +[] +let ``Two namespaces with the same name in the same implementation file`` () = + let trie = + TrieMapping.mkTrie + [| + { + Idx = 0 + FileName = "A.fs" + ParsedInput = + parseSourceCode ( + "A.fs", + """ +namespace A + +module B = begin end + +namespace A + +module C = begin end +""" ) + } + |] + + let aNode = trie.Children["A"] + Assert.AreEqual(2, aNode.Children.Count) + +[] +let ``Two namespaces with the same name in the same signature file`` () = + let trie = + TrieMapping.mkTrie + [| + { + Idx = 0 + FileName = "A.fsi" + ParsedInput = + parseSourceCode ( + "A.fsi", + """ +namespace A + +module B = begin end + +namespace A + +module C = begin end +""" ) + } + |] + + let aNode = trie.Children["A"] + Assert.AreEqual(2, aNode.Children.Count)