diff --git a/src/Compiler/Checking/NameResolution.fs b/src/Compiler/Checking/NameResolution.fs index 447c45cf524..6fa70c26fc7 100644 --- a/src/Compiler/Checking/NameResolution.fs +++ b/src/Compiler/Checking/NameResolution.fs @@ -2078,7 +2078,13 @@ type TcResultsSinkImpl(tcGlobals, ?sourceText: ISourceText) = let keyOpt = match item with - | Item.Value vref -> Some (endPos, vref.DisplayName) + | Item.Value vref -> + if (vref.IsPropertyGetterMethod || vref.IsPropertySetterMethod) + && not (System.String.IsNullOrWhiteSpace vref.Id.idText) then + // We don't want to skip a symbol if both getter and setter are present. + Some (endPos, vref.Id.idText) + else + Some (endPos, vref.DisplayName) | Item.OtherName (ident = Some id) -> Some (endPos, id.idText) | _ -> None diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index fabb4d8f497..070185bdc16 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -837,6 +837,7 @@ type internal TypeCheckInfo || #endif nameMatchesResidue tcref.DisplayName) + | Item.Value v when (v.IsPropertyGetterMethod || v.IsPropertySetterMethod) -> residue = v.Id.idText || residue = n1 | _ -> residue = n1) /// Post-filter items to make sure they have precisely the right name diff --git a/tests/service/ProjectAnalysisTests.fs b/tests/service/ProjectAnalysisTests.fs index 715ec77870b..0ee58b1898f 100644 --- a/tests/service/ProjectAnalysisTests.fs +++ b/tests/service/ProjectAnalysisTests.fs @@ -3443,16 +3443,20 @@ let ``Test Project24 all symbols`` () = ("``.ctor``", "file1", ((4, 5), (4, 23)), ["defn"], ["member"; "ctor"]); ("NameGetSet", "file1", ((5, 13), (5, 23)), ["defn"], ["member"; "getter"]); ("int", "file1", ((7, 20), (7, 23)), ["type"], ["abbrev"]); + ("NameGetSet", "file1", ((5, 13), (5, 23)), ["defn"], ["member"; "setter"]); ("NameGet", "file1", ((9, 13), (9, 20)), ["defn"], ["member"; "getter"]); ("int", "file1", ((11, 20), (11, 23)), ["type"], ["abbrev"]); + ("NameGet", "file1", ((9, 13), (9, 20)), ["defn"], ["member"; "setter"]); ("int", "file1", ((14, 21), (14, 24)), ["type"], ["abbrev"]); ("NameSet", "file1", ((13, 13), (13, 20)), ["defn"], ["member"; "setter"]); ("StaticNameGetSet", "file1", ((16, 18), (16, 34)), ["defn"], ["member"; "getter"]); - ("int", "file1", ((18, 20), (18, 23)), ["type"], ["abbrev"]); + ("int", "file1", ((18, 20), (18, 23)), ["type"], ["abbrev"]) + ("StaticNameGetSet", "file1", ((16, 18), (16, 34)), ["defn"], ["member"; "setter"]); ("StaticNameGet", "file1", ((20, 18), (20, 31)), ["defn"], ["member"; "getter"]); - ("int", "file1", ((22, 20), (22, 23)), ["type"], ["abbrev"]); + ("int", "file1", ((22, 20), (22, 23)), ["type"], ["abbrev"]) + ("StaticNameGet", "file1", ((20, 18), (20, 31)), ["defn"], ["member"; "setter"]); ("int", "file1", ((25, 21), (25, 24)), ["type"], ["abbrev"]); ("StaticNameSet", "file1", ((24, 18), (24, 31)), ["defn"], ["member"; "setter"]); @@ -3460,10 +3464,12 @@ let ``Test Project24 all symbols`` () = ["member"; "getter"]); ("AutoPropGetSet", "file1", ((28, 15), (28, 29)), ["defn"], ["member"; "getter"]); + ("AutoPropGetSet", "file1", ((28, 15), (28, 29)), ["defn"], ["member"; "setter"]) ("StaticAutoPropGet", "file1", ((30, 22), (30, 39)), ["defn"], ["member"; "getter"]); ("StaticAutoPropGetSet", "file1", ((31, 22), (31, 42)), ["defn"], - ["member"; "getter"]); + ["member"; "getter"]) + ("StaticAutoPropGetSet", "file1", ((31, 22), (31, 42)), ["defn"], ["member"; "setter"]); ("x", "file1", ((5, 11), (5, 12)), ["defn"], []); ("int", "file1", ((7, 20), (7, 23)), ["type"], ["abbrev"]); ("v", "file1", ((7, 17), (7, 18)), ["defn"], []); @@ -3549,22 +3555,28 @@ let ``Test symbol uses of properties with both getters and setters`` () = [|("TypeWithProperties", "file1", ((4, 5), (4, 23)), ["class"]); ("``.ctor``", "file1", ((4, 5), (4, 23)), ["member"; "ctor"]); ("NameGetSet", "file1", ((5, 13), (5, 23)), ["member"; "getter"]); - ("int", "file1", ((7, 20), (7, 23)), ["abbrev"]); + ("int", "file1", ((7, 20), (7, 23)), ["abbrev"]) + ("NameGetSet", "file1", ((5, 13), (5, 23)), ["member"; "setter"]); ("NameGet", "file1", ((9, 13), (9, 20)), ["member"; "getter"]); - ("int", "file1", ((11, 20), (11, 23)), ["abbrev"]); + ("int", "file1", ((11, 20), (11, 23)), ["abbrev"]) + ("NameGet", "file1", ((9, 13), (9, 20)), ["member"; "setter"]); ("int", "file1", ((14, 21), (14, 24)), ["abbrev"]); ("NameSet", "file1", ((13, 13), (13, 20)), ["member"; "setter"]); ("StaticNameGetSet", "file1", ((16, 18), (16, 34)), ["member"; "getter"]); - ("int", "file1", ((18, 20), (18, 23)), ["abbrev"]); + ("int", "file1", ((18, 20), (18, 23)), ["abbrev"]) + ("StaticNameGetSet", "file1", ((16, 18), (16, 34)), ["member"; "setter"]); ("StaticNameGet", "file1", ((20, 18), (20, 31)), ["member"; "getter"]); - ("int", "file1", ((22, 20), (22, 23)), ["abbrev"]); + ("int", "file1", ((22, 20), (22, 23)), ["abbrev"]) + ("StaticNameGet", "file1", ((20, 18), (20, 31)), ["member"; "setter"]); ("int", "file1", ((25, 21), (25, 24)), ["abbrev"]); ("StaticNameSet", "file1", ((24, 18), (24, 31)), ["member"; "setter"]); ("AutoPropGet", "file1", ((27, 15), (27, 26)), ["member"; "getter"]); - ("AutoPropGetSet", "file1", ((28, 15), (28, 29)), ["member"; "getter"]); + ("AutoPropGetSet", "file1", ((28, 15), (28, 29)), ["member"; "getter"]) + ("AutoPropGetSet", "file1", ((28, 15), (28, 29)), ["member"; "setter"]); ("StaticAutoPropGet", "file1", ((30, 22), (30, 39)), ["member"; "getter"]); ("StaticAutoPropGetSet", "file1", ((31, 22), (31, 42)), - ["member"; "getter"]); + ["member"; "getter"]) + ("StaticAutoPropGetSet", "file1", ((31, 22), (31, 42)), ["member"; "setter"]) ("x", "file1", ((5, 11), (5, 12)), []); ("int", "file1", ((7, 20), (7, 23)), ["abbrev"]); ("v", "file1", ((7, 17), (7, 18)), []); @@ -3626,7 +3638,7 @@ let ``Test symbol uses of properties with both getters and setters`` () = ("PropertyTest", "file1", ((2, 7), (2, 19)), ["module"])|] let getSampleSymbolUseOpt = - backgroundTypedParse1.GetSymbolUseAtLocation(9,20,"",["NameGet"]) + backgroundTypedParse1.GetSymbolUseAtLocation(9,20,"",["get_NameGet"]) let getSampleSymbol = getSampleSymbolUseOpt.Value.Symbol diff --git a/tests/service/Symbols.fs b/tests/service/Symbols.fs index deacc4b66fb..9c6bdca3c0b 100644 --- a/tests/service/Symbols.fs +++ b/tests/service/Symbols.fs @@ -427,3 +427,50 @@ let f2 b1 b2 b3 b4 b5 = mfv.FullType.AllInterfaces.Count |> should equal 0 | _ -> () | _ -> () + +module FSharpMemberOrFunctionOrValue = + [] + let ``Both Set and Get symbols are present`` () = + let _, checkResults = getParseAndCheckResults """ +namespace Foo + +type Foo = + member _.X + with get (y: int) : string = "" + and set (a: int) (b: float) = () +""" + + // "X" resolves a symbol but it will either be the get or set symbol. + // Use get_ or set_ to differentiate. + let xSymbol = checkResults.GetSymbolUseAtLocation(5, 14, " member _.X", [ "X" ]) + Assert.True xSymbol.IsSome + + let getSymbol = findSymbolUseByName "get_X" checkResults + match getSymbol.Symbol with + | :? FSharpMemberOrFunctionOrValue as mfv -> + Assert.AreEqual(1, mfv.CurriedParameterGroups.[0].Count) + | symbol -> Assert.Fail $"Expected {symbol} to be FSharpMemberOrFunctionOrValue" + + let setSymbol = findSymbolUseByName "set_X" checkResults + match setSymbol.Symbol with + | :? FSharpMemberOrFunctionOrValue as mfv -> + Assert.AreEqual(2, mfv.CurriedParameterGroups.[0].Count) + | symbol -> Assert.Fail $"Expected {symbol} to be FSharpMemberOrFunctionOrValue" + + [] + let ``AutoProperty with get,set has two symbols`` () = + let _, checkResults = getParseAndCheckResults """ +namespace Foo + +type Foo = + member val AutoPropGetSet = 0 with get, set +""" + + let getSymbol = findSymbolUseByName "get_AutoPropGetSet" checkResults + let setSymbol = findSymbolUseByName "set_AutoPropGetSet" checkResults + + match getSymbol.Symbol, setSymbol.Symbol with + | :? FSharpMemberOrFunctionOrValue as getMfv, + (:? FSharpMemberOrFunctionOrValue as setMfv) -> + Assert.AreNotEqual(getMfv.CurriedParameterGroups, setMfv.CurriedParameterGroups) + | _ -> Assert.Fail "Expected symbols to be FSharpMemberOrFunctionOrValue"