Skip to content

Commit b3848a3

Browse files
committed
Improve top level auto open detection.
1 parent 89d072d commit b3848a3

File tree

5 files changed

+241
-125
lines changed

5 files changed

+241
-125
lines changed

tests/ParallelTypeCheckingTests/Code/AlwaysLinkDetection.fs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@ let doesFileHasAutoOpenBehavior (ast: ParsedInput) : bool =
3535
match ast with
3636
| ParsedInput.SigFile (ParsedSigFileInput (contents = contents)) ->
3737
List.exists
38-
(fun (SynModuleOrNamespaceSig (attribs = attribs; kind = kind)) ->
39-
isAnyAttributeAutoOpen attribs
38+
(fun (SynModuleOrNamespaceSig (attribs = attribs; longId = longId; kind = kind)) ->
39+
(isAnyAttributeAutoOpen attribs && longId.Length < 2)
4040
|| kind = SynModuleOrNamespaceKind.GlobalNamespace)
4141
contents
4242
| ParsedInput.ImplFile (ParsedImplFileInput (contents = contents)) ->
4343
List.exists
44-
(fun (SynModuleOrNamespace (attribs = attribs; kind = kind)) ->
45-
isAnyAttributeAutoOpen attribs
44+
(fun (SynModuleOrNamespace (attribs = attribs; longId = longId; kind = kind)) ->
45+
(isAnyAttributeAutoOpen attribs && longId.Length < 2)
4646
|| kind = SynModuleOrNamespaceKind.GlobalNamespace)
4747
contents

tests/ParallelTypeCheckingTests/Code/TrieMapping.fs

Lines changed: 138 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -52,146 +52,164 @@ let rec mkTrieNodeFor (file: FileWithAST) : TrieNode =
5252
match file.AST with
5353
| ParsedInput.SigFile (ParsedSigFileInput (contents = contents)) ->
5454
contents
55-
|> List.choose (fun (SynModuleOrNamespaceSig (longId = longId; kind = kind; decls = decls; accessibility = _accessibility)) ->
56-
let hasTypesOrAutoOpenNestedModules =
57-
List.exists
58-
(function
59-
| SynModuleSigDecl.Types _ -> true
60-
| SynModuleSigDecl.NestedModule(moduleInfo = SynComponentInfo (attributes = attributes)) ->
61-
AlwaysLinkDetection.isAnyAttributeAutoOpen attributes
62-
| _ -> false)
63-
decls
64-
65-
let isNamespace =
66-
match kind with
67-
| SynModuleOrNamespaceKind.AnonModule
68-
| SynModuleOrNamespaceKind.NamedModule -> false
69-
| SynModuleOrNamespaceKind.DeclaredNamespace
70-
| SynModuleOrNamespaceKind.GlobalNamespace -> true
71-
72-
match longId with
73-
| [] -> None
74-
| _ ->
75-
let rootNode =
76-
let rec visit continuation (xs: LongIdent) =
77-
match xs with
78-
| [] -> failwith "should even empty"
79-
| [ finalPart ] ->
80-
let name = finalPart.idText
81-
82-
let current =
83-
if isNamespace then
84-
TrieNodeInfo.Namespace(
85-
name,
86-
(if hasTypesOrAutoOpenNestedModules then
87-
hs idx
88-
else
89-
emptyHS ())
90-
)
91-
else
92-
TrieNodeInfo.Module(name, idx)
93-
94-
let children = List.choose (mkTrieForNestedSigModule idx) decls
95-
96-
continuation (
97-
Dictionary<_, _>(
98-
Seq.singleton (
99-
KeyValuePair(
55+
|> List.choose
56+
(fun (SynModuleOrNamespaceSig (longId = longId; kind = kind; attribs = attribs; decls = decls; accessibility = _accessibility)) ->
57+
let hasTypesOrAutoOpenNestedModules =
58+
List.exists
59+
(function
60+
| SynModuleSigDecl.Types _ -> true
61+
| SynModuleSigDecl.NestedModule(moduleInfo = SynComponentInfo (attributes = attributes)) ->
62+
AlwaysLinkDetection.isAnyAttributeAutoOpen attributes
63+
| _ -> false)
64+
decls
65+
66+
let isNamespace =
67+
match kind with
68+
| SynModuleOrNamespaceKind.AnonModule
69+
| SynModuleOrNamespaceKind.NamedModule -> false
70+
| SynModuleOrNamespaceKind.DeclaredNamespace
71+
| SynModuleOrNamespaceKind.GlobalNamespace -> true
72+
73+
let topLevelModuleOrNamespaceHasAutoOpen =
74+
AlwaysLinkDetection.isAnyAttributeAutoOpen attribs
75+
76+
match longId with
77+
| [] -> None
78+
| _ ->
79+
let rootNode =
80+
let rec visit continuation (xs: LongIdent) =
81+
match xs with
82+
| [] -> failwith "should even empty"
83+
| [ finalPart ] ->
84+
let name = finalPart.idText
85+
86+
let current =
87+
if isNamespace then
88+
TrieNodeInfo.Namespace(
10089
name,
101-
{
102-
Current = current
103-
Children = Dictionary(children)
104-
}
90+
(if hasTypesOrAutoOpenNestedModules then
91+
hs idx
92+
else
93+
emptyHS ())
94+
)
95+
else
96+
TrieNodeInfo.Module(name, idx)
97+
98+
let children = List.choose (mkTrieForNestedSigModule idx) decls
99+
100+
continuation (
101+
Dictionary<_, _>(
102+
Seq.singleton (
103+
KeyValuePair(
104+
name,
105+
{
106+
Current = current
107+
Children = Dictionary(children)
108+
}
109+
)
105110
)
106111
)
107112
)
108-
)
109-
| head :: tail ->
110-
let name = head.idText
113+
| head :: tail ->
114+
let name = head.idText
115+
116+
visit
117+
(fun node ->
118+
let files =
119+
match tail with
120+
| [ _ ] when topLevelModuleOrNamespaceHasAutoOpen && not isNamespace -> hs idx
121+
| _ -> emptyHS ()
111122

112-
visit
113-
(fun node ->
114-
let current = TrieNodeInfo.Namespace(name, emptyHS ())
123+
let current = TrieNodeInfo.Namespace(name, files)
115124

116-
Dictionary<_, _>(Seq.singleton (KeyValuePair(name, { Current = current; Children = node })))
117-
|> continuation)
118-
tail
125+
Dictionary<_, _>(Seq.singleton (KeyValuePair(name, { Current = current; Children = node })))
126+
|> continuation)
127+
tail
119128

120-
visit id longId
129+
visit id longId
121130

122-
Some { Current = Root; Children = rootNode })
131+
Some { Current = Root; Children = rootNode })
123132
|> List.toArray
124133
|> mergeTrieNodes contents.Length
125134
| ParsedInput.ImplFile (ParsedImplFileInput (contents = contents)) ->
126135
contents
127-
|> List.choose (fun (SynModuleOrNamespace (longId = longId; kind = kind; decls = decls; accessibility = _accessibility)) ->
128-
let hasTypesOrAutoOpenNestedModules =
129-
List.exists
130-
(function
131-
| SynModuleDecl.Types _ -> true
132-
| SynModuleDecl.NestedModule(moduleInfo = SynComponentInfo (attributes = attributes)) ->
133-
AlwaysLinkDetection.isAnyAttributeAutoOpen attributes
134-
| _ -> false)
135-
decls
136-
137-
let isNamespace =
138-
match kind with
139-
| SynModuleOrNamespaceKind.AnonModule
140-
| SynModuleOrNamespaceKind.NamedModule -> false
141-
| SynModuleOrNamespaceKind.DeclaredNamespace
142-
| SynModuleOrNamespaceKind.GlobalNamespace -> true
143-
144-
match longId with
145-
| [] -> None
146-
| _ ->
147-
let rootNode =
148-
let rec visit continuation (xs: LongIdent) =
149-
match xs with
150-
| [] -> failwith "should even empty"
151-
| [ finalPart ] ->
152-
let name = finalPart.idText
153-
154-
let current =
155-
if isNamespace then
156-
TrieNodeInfo.Namespace(
157-
name,
158-
(if hasTypesOrAutoOpenNestedModules then
159-
hs idx
160-
else
161-
emptyHS ())
162-
)
163-
else
164-
TrieNodeInfo.Module(name, idx)
165-
166-
let children = List.choose (mkTrieForSynModuleDecl idx) decls
167-
168-
continuation (
169-
Dictionary<_, _>(
170-
Seq.singleton (
171-
KeyValuePair(
136+
|> List.choose
137+
(fun (SynModuleOrNamespace (longId = longId; attribs = attribs; kind = kind; decls = decls; accessibility = _accessibility)) ->
138+
let hasTypesOrAutoOpenNestedModules =
139+
List.exists
140+
(function
141+
| SynModuleDecl.Types _ -> true
142+
| SynModuleDecl.NestedModule(moduleInfo = SynComponentInfo (attributes = attributes)) ->
143+
AlwaysLinkDetection.isAnyAttributeAutoOpen attributes
144+
| _ -> false)
145+
decls
146+
147+
let isNamespace =
148+
match kind with
149+
| SynModuleOrNamespaceKind.AnonModule
150+
| SynModuleOrNamespaceKind.NamedModule -> false
151+
| SynModuleOrNamespaceKind.DeclaredNamespace
152+
| SynModuleOrNamespaceKind.GlobalNamespace -> true
153+
154+
let topLevelModuleOrNamespaceHasAutoOpen =
155+
AlwaysLinkDetection.isAnyAttributeAutoOpen attribs
156+
157+
match longId with
158+
| [] -> None
159+
| _ ->
160+
let rootNode =
161+
let rec visit continuation (xs: LongIdent) =
162+
match xs with
163+
| [] -> failwith "should even empty"
164+
| [ finalPart ] ->
165+
let name = finalPart.idText
166+
167+
let current =
168+
if isNamespace then
169+
TrieNodeInfo.Namespace(
172170
name,
173-
{
174-
Current = current
175-
Children = Dictionary(children)
176-
}
171+
(if hasTypesOrAutoOpenNestedModules then
172+
hs idx
173+
else
174+
emptyHS ())
175+
)
176+
else
177+
TrieNodeInfo.Module(name, idx)
178+
179+
let children = List.choose (mkTrieForSynModuleDecl idx) decls
180+
181+
continuation (
182+
Dictionary<_, _>(
183+
Seq.singleton (
184+
KeyValuePair(
185+
name,
186+
{
187+
Current = current
188+
Children = Dictionary(children)
189+
}
190+
)
177191
)
178192
)
179193
)
180-
)
181-
| head :: tail ->
182-
let name = head.idText
194+
| head :: tail ->
195+
let name = head.idText
196+
197+
visit
198+
(fun node ->
199+
let files =
200+
match tail with
201+
| [ _ ] when topLevelModuleOrNamespaceHasAutoOpen && not isNamespace -> hs idx
202+
| _ -> emptyHS ()
183203

184-
visit
185-
(fun node ->
186-
let current = TrieNodeInfo.Namespace(name, emptyHS ())
204+
let current = TrieNodeInfo.Namespace(name, files)
187205

188-
Dictionary<_, _>(Seq.singleton (KeyValuePair(name, { Current = current; Children = node })))
189-
|> continuation)
190-
tail
206+
Dictionary<_, _>(Seq.singleton (KeyValuePair(name, { Current = current; Children = node })))
207+
|> continuation)
208+
tail
191209

192-
visit id longId
210+
visit id longId
193211

194-
Some { Current = Root; Children = rootNode })
212+
Some { Current = Root; Children = rootNode })
195213
|> List.toArray
196214
|> mergeTrieNodes contents.Length
197215

tests/ParallelTypeCheckingTests/Tests/AlwaysLinkDetectionTests.fs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ let ``Detect top level auto open`` () =
99
let fileContent =
1010
"""
1111
[<AutoOpen>]
12-
module internal Internal.Utilities.Library.Block
12+
module internal Block
1313
1414
open System.Collections.Immutable
1515
@@ -41,3 +41,15 @@ type X = { Y: int }
4141

4242
let ast = parseSourceCode ("Global.fsi", fileContent)
4343
Assert.True(doesFileHasAutoOpenBehavior ast)
44+
45+
[<Test>]
46+
let ``Top level auto open module with prefixed namespace should not be consider as always linked`` () =
47+
let fileContent =
48+
"""
49+
[<AutoOpen>]
50+
module A.B
51+
let a = 0
52+
"""
53+
54+
let ast = parseSourceCode ("Global.fsi", fileContent)
55+
Assert.False(doesFileHasAutoOpenBehavior ast)

tests/ParallelTypeCheckingTests/Tests/Scenarios.fs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,4 +471,34 @@ module Tc = CheckExpressions
471471
"""
472472
(set [| 0 |])
473473
]
474+
scenario
475+
"Top level module with auto open and namespace prefix"
476+
[
477+
// This file is added to make ensure that B.fs links to A.fs because of the contents of A.
478+
// If A didn't have the AutoOpen attribute, as a last resort it would be linked to A anyway because of the ghost dependency mechanism.
479+
sourceFile
480+
"Ghost.fs"
481+
"""
482+
namespace A
483+
"""
484+
Set.empty
485+
sourceFile
486+
"A.fs"
487+
"""
488+
[<AutoOpen>]
489+
module A.B
490+
491+
let a = 0
492+
"""
493+
Set.empty
494+
sourceFile
495+
"B.fs"
496+
"""
497+
module Library
498+
499+
open A
500+
let b = a + 1
501+
"""
502+
(set [| 1 |])
503+
]
474504
]

0 commit comments

Comments
 (0)