Skip to content

Commit 70bd177

Browse files
authored
Fix finding references to a type when there's a module with the same name (#16081)
This change can cause different ordering of symbols in GetAllUsesOfAllSymbolsInFile. Also some invalid long identifiers might me missing there.
1 parent a47975f commit 70bd177

File tree

7 files changed

+99
-30
lines changed

7 files changed

+99
-30
lines changed

src/Compiler/Checking/CheckDeclarations.fs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ let ImplicitlyOpenOwnNamespace tcSink g amap scopem enclosingNamespacePath (env:
363363
match enclosingNamespacePathToOpen with
364364
| id :: rest ->
365365
let ad = env.AccessRights
366-
match ResolveLongIdentAsModuleOrNamespace tcSink amap scopem true OpenQualified env.eNameResEnv ad id rest true with
366+
match ResolveLongIdentAsModuleOrNamespace tcSink amap scopem true OpenQualified env.eNameResEnv ad id rest true ShouldNotifySink.Yes with
367367
| Result modrefs ->
368368
let modrefs = List.map p23 modrefs
369369
let lid = SynLongIdent(enclosingNamespacePathToOpen, [] , [])
@@ -639,7 +639,7 @@ let TcOpenLidAndPermitAutoResolve tcSink (env: TcEnv) amap (longId : Ident list)
639639
| [] -> []
640640
| id :: rest ->
641641
let m = longId |> List.map (fun id -> id.idRange) |> List.reduce unionRanges
642-
match ResolveLongIdentAsModuleOrNamespace tcSink amap m true OpenQualified env.NameEnv ad id rest true with
642+
match ResolveLongIdentAsModuleOrNamespace tcSink amap m true OpenQualified env.NameEnv ad id rest true ShouldNotifySink.Yes with
643643
| Result res -> res
644644
| Exception err ->
645645
errorR(err); []
@@ -1531,7 +1531,7 @@ module MutRecBindingChecking =
15311531
let resolved =
15321532
match p with
15331533
| [] -> Result []
1534-
| id :: rest -> ResolveLongIdentAsModuleOrNamespace cenv.tcSink cenv.amap m true OpenQualified env.NameEnv ad id rest false
1534+
| id :: rest -> ResolveLongIdentAsModuleOrNamespace cenv.tcSink cenv.amap m true OpenQualified env.NameEnv ad id rest false ShouldNotifySink.Yes
15351535

15361536
let mvvs = ForceRaise resolved
15371537

@@ -4738,7 +4738,7 @@ let rec TcSignatureElementNonMutRec (cenv: cenv) parent typeNames endm (env: TcE
47384738
let resolved =
47394739
match p with
47404740
| [] -> Result []
4741-
| id :: rest -> ResolveLongIdentAsModuleOrNamespace cenv.tcSink cenv.amap m true OpenQualified env.NameEnv ad id rest false
4741+
| id :: rest -> ResolveLongIdentAsModuleOrNamespace cenv.tcSink cenv.amap m true OpenQualified env.NameEnv ad id rest false ShouldNotifySink.Yes
47424742
let mvvs = ForceRaise resolved
47434743
let scopem = unionRanges m endm
47444744
let unfilteredModrefs = mvvs |> List.map p23

src/Compiler/Checking/CheckExpressions.fs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7334,7 +7334,7 @@ and TcConstExpr cenv (overallTy: OverallTy) env m tpenv c =
73347334
let expr =
73357335
let modName = "NumericLiteral" + suffix
73367336
let ad = env.eAccessRights
7337-
match ResolveLongIdentAsModuleOrNamespace cenv.tcSink cenv.amap m true OpenQualified env.eNameResEnv ad (ident (modName, m)) [] false with
7337+
match ResolveLongIdentAsModuleOrNamespace cenv.tcSink cenv.amap m true OpenQualified env.eNameResEnv ad (ident (modName, m)) [] false ShouldNotifySink.Yes with
73387338
| Result []
73397339
| Exception _ -> error(Error(FSComp.SR.tcNumericLiteralRequiresModule modName, m))
73407340
| Result ((_, mref, _) :: _) ->
@@ -8121,7 +8121,7 @@ and TcNameOfExpr (cenv: cenv) env tpenv (synArg: SynExpr) =
81218121
let resolvedToModuleOrNamespaceName =
81228122
if delayed.IsEmpty then
81238123
let id,rest = List.headAndTail longId
8124-
match ResolveLongIdentAsModuleOrNamespace cenv.tcSink cenv.amap m true OpenQualified env.eNameResEnv ad id rest true with
8124+
match ResolveLongIdentAsModuleOrNamespace cenv.tcSink cenv.amap m true OpenQualified env.eNameResEnv ad id rest true ShouldNotifySink.Yes with
81258125
| Result modref when delayed.IsEmpty && modref |> List.exists (p23 >> IsEntityAccessible cenv.amap m ad) ->
81268126
true // resolved to a module or namespace, done with checks
81278127
| _ ->

src/Compiler/Checking/NameResolution.fs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2364,14 +2364,19 @@ let CheckForTypeLegitimacyAndMultipleGenericTypeAmbiguities
23642364
// Consume ids that refer to a namespace, module, or type
23652365
//-------------------------------------------------------------------------
23662366

2367+
[<RequireQualifiedAccess>]
2368+
type ShouldNotifySink =
2369+
| Yes
2370+
| No
2371+
23672372
/// Perform name resolution for an identifier which must resolve to be a module or namespace.
2368-
let rec ResolveLongIdentAsModuleOrNamespace sink (amap: Import.ImportMap) m first fullyQualified (nenv: NameResolutionEnv) ad (id:Ident) (rest: Ident list) isOpenDecl =
2373+
let rec ResolveLongIdentAsModuleOrNamespace sink (amap: Import.ImportMap) m first fullyQualified (nenv: NameResolutionEnv) ad (id:Ident) (rest: Ident list) isOpenDecl notifySink =
23692374
if first && id.idText = MangledGlobalName then
23702375
match rest with
23712376
| [] ->
23722377
error (Error(FSComp.SR.nrGlobalUsedOnlyAsFirstName(), id.idRange))
23732378
| id2 :: rest2 ->
2374-
ResolveLongIdentAsModuleOrNamespace sink amap m false FullyQualified nenv ad id2 rest2 isOpenDecl
2379+
ResolveLongIdentAsModuleOrNamespace sink amap m false FullyQualified nenv ad id2 rest2 isOpenDecl notifySink
23752380
else
23762381
let notFoundAux (id: Ident) depth error (tcrefs: TyconRef seq) =
23772382
let suggestNames (addToBuffer: string -> unit) =
@@ -2432,16 +2437,17 @@ let rec ResolveLongIdentAsModuleOrNamespace sink (amap: Import.ImportMap) m firs
24322437
modrefs
24332438
|> List.map (fun modref ->
24342439
if IsEntityAccessible amap m ad modref then
2435-
notifyNameResolution modref id.idRange
2440+
if notifySink = ShouldNotifySink.Yes then
2441+
notifyNameResolution modref id.idRange
24362442
look 1 modref rest
24372443
else
24382444
raze (namespaceOrModuleNotFound.Force()))
24392445
|> List.reduce AddResults
24402446
| _ -> raze (namespaceOrModuleNotFound.Force())
24412447

24422448
// Note - 'rest' is annotated due to a bug currently in Unity (see: https://github.com/dotnet/fsharp/pull/7427)
2443-
let ResolveLongIdentAsModuleOrNamespaceThen sink atMostOne amap m fullyQualified (nenv: NameResolutionEnv) ad id (rest: Ident list) isOpenDecl f =
2444-
match ResolveLongIdentAsModuleOrNamespace sink amap m true fullyQualified nenv ad id [] isOpenDecl with
2449+
let ResolveLongIdentAsModuleOrNamespaceThen sink atMostOne amap m fullyQualified (nenv: NameResolutionEnv) ad id (rest: Ident list) isOpenDecl notifySink f =
2450+
match ResolveLongIdentAsModuleOrNamespace sink amap m true fullyQualified nenv ad id [] isOpenDecl notifySink with
24452451
| Result modrefs ->
24462452
match rest with
24472453
| [] -> error(Error(FSComp.SR.nrUnexpectedEmptyLongId(), id.idRange))
@@ -3067,7 +3073,7 @@ let rec ResolveExprLongIdentPrim sink (ncenv: NameResolver) first fullyQualified
30673073
// Otherwise modules are searched first. REVIEW: modules and types should be searched together.
30683074
// For each module referenced by 'id', search the module as if it were an F# module and/or a .NET namespace.
30693075
let moduleSearch ad () =
3070-
ResolveLongIdentAsModuleOrNamespaceThen sink ResultCollectionSettings.AtMostOneResult ncenv.amap m fullyQualified nenv ad id rest isOpenDecl
3076+
ResolveLongIdentAsModuleOrNamespaceThen sink ResultCollectionSettings.AtMostOneResult ncenv.amap m fullyQualified nenv ad id rest isOpenDecl ShouldNotifySink.No
30713077
(ResolveExprLongIdentInModuleOrNamespace ncenv nenv typeNameResInfo ad)
30723078

30733079
// REVIEW: somewhat surprisingly, this shows up on performance traces, with tcrefs non-nil.
@@ -3258,7 +3264,7 @@ let rec ResolvePatternLongIdentPrim sink (ncenv: NameResolver) fullyQualified wa
32583264
ResolutionInfo.SendEntityPathToSink (sink, ncenv, nenv, ItemOccurence.Pattern, ad, res, ResultTyparChecker(fun () -> true))
32593265
Item.Types (id.idText, [ mkAppTy tcref [] ])
32603266
| _ ->
3261-
match ResolveLongIdentAsModuleOrNamespace sink ncenv.amap id.idRange true fullyQualified nenv ad id [] false with
3267+
match ResolveLongIdentAsModuleOrNamespace sink ncenv.amap id.idRange true fullyQualified nenv ad id [] false ShouldNotifySink.Yes with
32623268
| Result ((_, mref, _) :: _) ->
32633269
let res = ResolutionInfo.Empty.AddEntity (id.idRange, mref)
32643270
ResolutionInfo.SendEntityPathToSink (sink, ncenv, nenv, ItemOccurence.Pattern, ad, res, ResultTyparChecker(fun () -> true))
@@ -3271,7 +3277,7 @@ let rec ResolvePatternLongIdentPrim sink (ncenv: NameResolver) fullyQualified wa
32713277
// Long identifiers in patterns
32723278
else
32733279
let moduleSearch ad () =
3274-
ResolveLongIdentAsModuleOrNamespaceThen sink ResultCollectionSettings.AtMostOneResult ncenv.amap m fullyQualified nenv ad id rest false
3280+
ResolveLongIdentAsModuleOrNamespaceThen sink ResultCollectionSettings.AtMostOneResult ncenv.amap m fullyQualified nenv ad id rest false ShouldNotifySink.Yes
32753281
(ResolvePatternLongIdentInModuleOrNamespace ncenv nenv numTyArgsOpt ad)
32763282

32773283
let tyconSearch ad =
@@ -3494,12 +3500,12 @@ let rec ResolveTypeLongIdentPrim sink (ncenv: NameResolver) occurence first full
34943500
NoResultsOrUsefulErrors
34953501

34963502
let modulSearch =
3497-
ResolveLongIdentAsModuleOrNamespaceThen sink ResultCollectionSettings.AllResults ncenv.amap m2 fullyQualified nenv ad id rest false
3503+
ResolveLongIdentAsModuleOrNamespaceThen sink ResultCollectionSettings.AllResults ncenv.amap m2 fullyQualified nenv ad id rest false ShouldNotifySink.Yes
34983504
(ResolveTypeLongIdentInModuleOrNamespace sink nenv ncenv typeNameResInfo ad genOk)
34993505
|?> List.concat
35003506

35013507
let modulSearchFailed() =
3502-
ResolveLongIdentAsModuleOrNamespaceThen sink ResultCollectionSettings.AllResults ncenv.amap m2 fullyQualified nenv AccessibleFromSomeFSharpCode id rest false
3508+
ResolveLongIdentAsModuleOrNamespaceThen sink ResultCollectionSettings.AllResults ncenv.amap m2 fullyQualified nenv AccessibleFromSomeFSharpCode id rest false ShouldNotifySink.Yes
35033509
(ResolveTypeLongIdentInModuleOrNamespace sink nenv ncenv typeNameResInfo.DropStaticArgsInfo AccessibleFromSomeFSharpCode genOk)
35043510
|?> List.concat
35053511

@@ -3710,7 +3716,7 @@ let ResolveFieldPrim sink (ncenv: NameResolver) nenv ad ty (mp, id: Ident) allFi
37103716
match lid with
37113717
| [] -> NoResultsOrUsefulErrors
37123718
| id2 :: rest2 ->
3713-
ResolveLongIdentAsModuleOrNamespaceThen sink ResultCollectionSettings.AtMostOneResult ncenv.amap m OpenQualified nenv ad id2 rest2 false
3719+
ResolveLongIdentAsModuleOrNamespaceThen sink ResultCollectionSettings.AtMostOneResult ncenv.amap m OpenQualified nenv ad id2 rest2 false ShouldNotifySink.Yes
37143720
(ResolveFieldInModuleOrNamespace ncenv nenv ad)
37153721

37163722
let resInfo, item, rest =
@@ -3823,7 +3829,7 @@ let ResolveNestedField sink (ncenv: NameResolver) nenv ad recdTy lid =
38233829
match lid with
38243830
| [] -> NoResultsOrUsefulErrors
38253831
| modOrNsId :: rest ->
3826-
ResolveLongIdentAsModuleOrNamespaceThen sink ResultCollectionSettings.AtMostOneResult ncenv.amap modOrNsId.idRange OpenQualified nenv ad modOrNsId rest false (ResolveFieldInModuleOrNamespace ncenv nenv ad)
3832+
ResolveLongIdentAsModuleOrNamespaceThen sink ResultCollectionSettings.AtMostOneResult ncenv.amap modOrNsId.idRange OpenQualified nenv ad modOrNsId rest false ShouldNotifySink.Yes (ResolveFieldInModuleOrNamespace ncenv nenv ad)
38273833
|?> List.map (fun (_, FieldResolution(rfinfo, _), restAfterField) ->
38283834
let fieldId = rest.[ rest.Length - restAfterField.Length - 1 ]
38293835
fieldId, Item.RecdField rfinfo, restAfterField)

src/Compiler/Checking/NameResolution.fsi

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,12 @@ type AfterResolution =
598598
(MethInfo * PropInfo option * TyparInstantiation -> unit) *
599599
(unit -> unit)
600600

601+
/// Indicates whether we want to report found items to the name resolution sink
602+
[<RequireQualifiedAccess>]
603+
type ShouldNotifySink =
604+
| Yes
605+
| No
606+
601607
/// Temporarily redirect reporting of name resolution and type checking results
602608
val internal WithNewTypecheckResultsSink: ITypecheckResultsSink * TcResultsSink -> System.IDisposable
603609

@@ -684,6 +690,7 @@ val internal ResolveLongIdentAsModuleOrNamespace:
684690
id: Ident ->
685691
rest: Ident list ->
686692
isOpenDecl: bool ->
693+
notifySink: ShouldNotifySink ->
687694
ResultOrException<(int * ModuleOrNamespaceRef * ModuleOrNamespaceType) list>
688695

689696
/// Resolve a long identifier to an object constructor.

tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,3 +600,57 @@ type internal SomeType() =
600600
findAllReferences (expectToFind <| method1Locations())
601601
}
602602

603+
[<Fact>]
604+
let ``Module with the same name as type`` () =
605+
let source = """
606+
module Foo
607+
608+
type MyType =
609+
static member Two = 1
610+
611+
let x = MyType.Two
612+
613+
module MyType = do () // <-- Extra module with the same name as the type
614+
615+
let y = MyType.Two
616+
"""
617+
618+
let fileName, options, checker = singleFileChecker source
619+
620+
let symbolUse = getSymbolUse fileName source "MyType" options checker |> Async.RunSynchronously
621+
622+
checker.FindBackgroundReferencesInFile(fileName, options, symbolUse.Symbol, fastCheck = true)
623+
|> Async.RunSynchronously
624+
|> expectToFind [
625+
fileName, 4, 5, 11
626+
fileName, 7, 8, 14
627+
fileName, 11, 8, 14
628+
]
629+
630+
[<Fact>]
631+
let ``Module with the same name as type part 2`` () =
632+
let source = """
633+
module Foo
634+
635+
module MyType =
636+
637+
let Three = 7
638+
639+
type MyType =
640+
static member Two = 1
641+
642+
let x = MyType.Two
643+
644+
let y = MyType.Three
645+
"""
646+
647+
let fileName, options, checker = singleFileChecker source
648+
649+
let symbolUse = getSymbolUse fileName source "MyType" options checker |> Async.RunSynchronously
650+
651+
checker.FindBackgroundReferencesInFile(fileName, options, symbolUse.Symbol, fastCheck = true)
652+
|> Async.RunSynchronously
653+
|> expectToFind [
654+
fileName, 4, 7, 13
655+
fileName, 13, 8, 14
656+
]

tests/service/EditorTests.fs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1574,15 +1574,14 @@ let _ = Threading.Buzz = null
15741574
su.Symbol.ToString(), (r.StartLine, r.StartColumn, r.EndLine, r.EndColumn))
15751575
|> Array.distinct
15761576
|> shouldEqual
1577-
// note: these "System" sysbol uses are not duplications because each of them corresponts to different namespaces
1577+
// note: these "System" and "Threading" symbol uses are not duplications because each of them corresponds to different namespaces
15781578
[|("System", (2, 5, 2, 11))
15791579
("Threading", (2, 12, 2, 21))
15801580
("System", (3, 5, 3, 11))
15811581
("System", (5, 7, 5, 13))
15821582
("Threading", (5, 14, 5, 23))
15831583
("Tasks", (5, 24, 5, 29))
15841584
("val op_Equality", (6, 23, 6, 24))
1585-
("Threading", (6, 8, 6, 17))
15861585
("Test", (1, 0, 1, 0))|]
15871586

15881587
[<Test>]

tests/service/ProjectAnalysisTests.fs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1482,10 +1482,11 @@ let ``Test project 5 all symbols`` () =
14821482
let allUsesOfAllSymbols =
14831483
wholeProjectResults.GetAllUsesOfAllSymbols()
14841484

1485-
|> Array.map (fun su -> su.Symbol.ToString(), su.Symbol.FullName, Project5.cleanFileName su.FileName, tupsZ su.Range, attribsOfSymbolUse su)
1485+
|> Seq.map (fun su -> su.Symbol.ToString(), su.Symbol.FullName, Project5.cleanFileName su.FileName, tupsZ su.Range, attribsOfSymbolUse su)
1486+
|> Set
14861487

14871488
allUsesOfAllSymbols |> shouldEqual
1488-
[|("symbol Even", "Even", "file1", ((4, 6), (4, 10)), ["defn"]);
1489+
(Set [("symbol Even", "Even", "file1", ((4, 6), (4, 10)), ["defn"]);
14891490
("symbol Odd", "Odd", "file1", ((4, 11), (4, 14)), ["defn"]);
14901491
("val input", "input", "file1", ((4, 17), (4, 22)), ["defn"]);
14911492
("val op_Equality", "Microsoft.FSharp.Core.Operators.(=)", "file1",
@@ -1539,7 +1540,7 @@ let ``Test project 5 all symbols`` () =
15391540
("val str", "str", "file1", ((22, 38), (22, 41)), []);
15401541
("val parseNumeric", "ActivePatterns.parseNumeric", "file1",
15411542
((19, 4), (19, 16)), ["defn"]);
1542-
("ActivePatterns", "ActivePatterns", "file1", ((1, 7), (1, 21)), ["defn"])|]
1543+
("ActivePatterns", "ActivePatterns", "file1", ((1, 7), (1, 21)), ["defn"])])
15431544

15441545
[<Test>]
15451546
let ``Test complete active patterns' exact ranges from uses of symbols`` () =
@@ -2764,11 +2765,12 @@ let ``Test Project17 all symbols`` () =
27642765
let allUsesOfAllSymbols =
27652766
wholeProjectResults.GetAllUsesOfAllSymbols()
27662767

2767-
|> Array.map (fun su -> su.Symbol.ToString(), su.Symbol.DisplayName, Project17.cleanFileName su.FileName, tups su.Range, attribsOfSymbolUse su, attribsOfSymbol su.Symbol)
2768+
|> Seq.map (fun su -> su.Symbol.ToString(), su.Symbol.DisplayName, Project17.cleanFileName su.FileName, tups su.Range, attribsOfSymbolUse su, attribsOfSymbol su.Symbol)
2769+
|> Set
27682770

27692771
allUsesOfAllSymbols
2770-
|> shouldEqual
2771-
[|("Microsoft", "Microsoft", "file1", ((4, 8), (4, 17)), [], ["namespace"]);
2772+
|> shouldEqual (Set
2773+
[("Microsoft", "Microsoft", "file1", ((4, 8), (4, 17)), [], ["namespace"]);
27722774
("Collections", "Collections", "file1", ((4, 25), (4, 36)), [], ["namespace"]);
27732775
("FSharp", "FSharp", "file1", ((4, 18), (4, 24)), [], ["namespace"]);
27742776
("FSharpList`1", "List", "file1", ((4, 8), (4, 41)), [], ["union"]);
@@ -2808,7 +2810,7 @@ let ``Test Project17 all symbols`` () =
28082810
("property HelpLink", "HelpLink", "file1", ((10, 31), (10, 41)), [],
28092811
["slot"; "member"; "prop"]);
28102812
("val f3", "f3", "file1", ((10, 4), (10, 6)), ["defn"], ["val"]);
2811-
("Impl", "Impl", "file1", ((2, 7), (2, 11)), ["defn"], ["module"])|]
2813+
("Impl", "Impl", "file1", ((2, 7), (2, 11)), ["defn"], ["module"])])
28122814

28132815

28142816
//-----------------------------------------------------------------------------------------
@@ -2906,10 +2908,11 @@ let ``Test Project19 all symbols`` () =
29062908
let allUsesOfAllSymbols =
29072909
wholeProjectResults.GetAllUsesOfAllSymbols()
29082910

2909-
|> Array.map (fun su -> su.Symbol.ToString(), su.Symbol.DisplayName, Project19.cleanFileName su.FileName, tups su.Range, attribsOfSymbolUse su, attribsOfSymbol su.Symbol)
2911+
|> Seq.map (fun su -> su.Symbol.ToString(), su.Symbol.DisplayName, Project19.cleanFileName su.FileName, tups su.Range, attribsOfSymbolUse su, attribsOfSymbol su.Symbol)
2912+
|> Set
29102913

29112914
allUsesOfAllSymbols |> shouldEqual
2912-
[|("field EnumCase1", "EnumCase1", "file1", ((4, 14), (4, 23)), ["defn"],
2915+
(Set [("field EnumCase1", "EnumCase1", "file1", ((4, 14), (4, 23)), ["defn"],
29132916
["field"; "static"; "1"]);
29142917
("field EnumCase2", "EnumCase2", "file1", ((4, 30), (4, 39)), ["defn"],
29152918
["field"; "static"; "2"]);
@@ -2935,7 +2938,7 @@ let ``Test Project19 all symbols`` () =
29352938
("field Monday", "Monday", "file1", ((10, 8), (10, 31)), [],
29362939
["field"; "static"; "1"]);
29372940
("val s", "s", "file1", ((10, 4), (10, 5)), ["defn"], ["val"]);
2938-
("Impl", "Impl", "file1", ((2, 7), (2, 11)), ["defn"], ["module"])|]
2941+
("Impl", "Impl", "file1", ((2, 7), (2, 11)), ["defn"], ["module"])])
29392942

29402943

29412944

0 commit comments

Comments
 (0)