Skip to content

Commit d95e8f3

Browse files
authored
Fix finding references to interface methods (#15951)
1 parent 635772a commit d95e8f3

File tree

4 files changed

+172
-57
lines changed

4 files changed

+172
-57
lines changed

src/Compiler/Service/IncrementalBuild.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ type BoundModel private (
335335
if enableBackgroundItemKeyStoreAndSemanticClassification then
336336
use _ = Activity.start "IncrementalBuild.CreateItemKeyStoreAndSemanticClassification" [|Activity.Tags.fileName, fileName|]
337337
let sResolutions = sink.GetResolutions()
338-
let builder = ItemKeyStoreBuilder()
338+
let builder = ItemKeyStoreBuilder(tcGlobals)
339339
let preventDuplicates = HashSet({ new IEqualityComparer<struct(pos * pos)> with
340340
member _.Equals((s1, e1): struct(pos * pos), (s2, e2): struct(pos * pos)) = Position.posEq s1 s2 && Position.posEq e1 e2
341341
member _.GetHashCode o = o.GetHashCode() })

src/Compiler/Service/ItemKey.fs

Lines changed: 121 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ open FSharp.Compiler.Text.Range
1616
open FSharp.Compiler.TypedTree
1717
open FSharp.Compiler.TypedTreeBasics
1818
open FSharp.Compiler.Syntax.PrettyNaming
19+
open FSharp.Compiler.TypedTreeOps
20+
open FSharp.Compiler.TcGlobals
1921

2022
#nowarn "9"
2123
#nowarn "51"
@@ -96,8 +98,82 @@ module ItemKeyTags =
9698
[<Literal>]
9799
let parameters = "p$p$"
98100

101+
[<AutoOpen>]
102+
module DebugKeyStore =
103+
104+
/// A debugging tool to show what's being written into the ItemKeyStore in a more human readable way in the debugger.
105+
type DebugKeyStore() =
106+
107+
let mutable debugCurrentItem = ResizeArray()
108+
109+
member val Items = ResizeArray()
110+
111+
member _.WriteRange(m: range) = debugCurrentItem.Add("range", $"{m}")
112+
113+
member _.WriteEntityRef(eref: EntityRef) =
114+
debugCurrentItem.Add("EntityRef", $"{eref}")
115+
116+
member _.WriteILType(ilTy: ILType) =
117+
debugCurrentItem.Add("ILType", $"%A{ilTy}")
118+
119+
member _.WriteType isStandalone (ty: TType) =
120+
debugCurrentItem.Add("Type", $"{isStandalone} %A{ty}")
121+
122+
member _.WriteMeasure isStandalone (ms: Measure) =
123+
debugCurrentItem.Add("Measure", $"{isStandalone} %A{ms}")
124+
125+
member _.WriteTypar (isStandalone: bool) (typar: Typar) =
126+
debugCurrentItem.Add("Typar", $"{isStandalone} %A{typar}")
127+
128+
member _.WriteValRef(vref: ValRef) =
129+
debugCurrentItem.Add("ValRef", $"{vref}")
130+
131+
member _.WriteValue(vref: ValRef) =
132+
debugCurrentItem.Add("Value", $"{vref}")
133+
134+
member _.WriteActivePatternCase (apInfo: ActivePatternInfo) index =
135+
debugCurrentItem.Add("ActivePatternCase", $"{apInfo} {index}")
136+
137+
member this.FinishItem(item, length) =
138+
debugCurrentItem.Add("length", $"{length}")
139+
this.Items.Add(item, debugCurrentItem)
140+
let itemCount = this.Items.Count
141+
assert (itemCount > 0)
142+
debugCurrentItem <- ResizeArray()
143+
144+
member _.New() = DebugKeyStore()
145+
146+
/// A replacement for DebugKeyStore for when we're not debugging.
147+
type _DebugKeyStoreNoop() =
148+
149+
member inline _.Items = Unchecked.defaultof<_>
150+
151+
member inline _.WriteRange(_m: range) = ()
152+
153+
member inline _.WriteEntityRef(_eref: EntityRef) = ()
154+
155+
member inline _.WriteILType(_ilTy: ILType) = ()
156+
157+
member inline _.WriteType _isStandalone (_ty: TType) = ()
158+
159+
member inline _.WriteMeasure _isStandalone (_ms: Measure) = ()
160+
161+
member inline _.WriteTypar (_isStandalone: bool) (_typar: Typar) = ()
162+
163+
member inline _.WriteValRef(_vref: ValRef) = ()
164+
165+
member inline _.WriteValue(_vref: ValRef) = ()
166+
167+
member inline _.WriteActivePatternCase (_apInfo: ActivePatternInfo) _index = ()
168+
169+
member inline _.FinishItem(_item, _length) = ()
170+
171+
member inline this.New() = this
172+
173+
let DebugKeyStoreNoop = _DebugKeyStoreNoop ()
174+
99175
[<Sealed>]
100-
type ItemKeyStore(mmf: MemoryMappedFile, length) =
176+
type ItemKeyStore(mmf: MemoryMappedFile, length, tcGlobals, debugStore) =
101177

102178
let rangeBuffer = Array.zeroCreate<byte> sizeof<range>
103179

@@ -107,6 +183,8 @@ type ItemKeyStore(mmf: MemoryMappedFile, length) =
107183
if isDisposed then
108184
raise (ObjectDisposedException("ItemKeyStore"))
109185

186+
member _.DebugStore = debugStore
187+
110188
member _.ReadRange(reader: byref<BlobReader>) =
111189
reader.ReadBytes(sizeof<range>, rangeBuffer, 0)
112190
MemoryMarshal.Cast<byte, range>(Span(rangeBuffer)).[0]
@@ -133,7 +211,7 @@ type ItemKeyStore(mmf: MemoryMappedFile, length) =
133211
member this.FindAll(item: Item) =
134212
checkDispose ()
135213

136-
let builder = ItemKeyStoreBuilder()
214+
let builder = ItemKeyStoreBuilder(tcGlobals)
137215
builder.Write(range0, item)
138216

139217
match builder.TryBuildAndReset() with
@@ -166,10 +244,13 @@ type ItemKeyStore(mmf: MemoryMappedFile, length) =
166244
isDisposed <- true
167245
mmf.Dispose()
168246

169-
and [<Sealed>] ItemKeyStoreBuilder() =
247+
and [<Sealed>] ItemKeyStoreBuilder(tcGlobals: TcGlobals) =
170248

171249
let b = BlobBuilder()
172250

251+
// Change this to DebugKeyStore() for debugging (DebugStore will be available on ItemKeyStore)
252+
let mutable debug = DebugKeyStoreNoop
253+
173254
let writeChar (c: char) = b.WriteUInt16(uint16 c)
174255

175256
let writeUInt16 (i: uint16) = b.WriteUInt16 i
@@ -181,16 +262,20 @@ and [<Sealed>] ItemKeyStoreBuilder() =
181262
let writeString (str: string) = b.WriteUTF16 str
182263

183264
let writeRange (m: range) =
265+
debug.WriteRange m
184266
let mutable m = m
185267
let ptr = &&m |> NativePtr.toNativeInt |> NativePtr.ofNativeInt<byte>
186268
b.WriteBytes(ptr, sizeof<range>)
187269

188270
let writeEntityRef (eref: EntityRef) =
271+
debug.WriteEntityRef eref
189272
writeString ItemKeyTags.entityRef
190273
writeString eref.CompiledName
191274
eref.CompilationPath.MangledPath |> List.iter (fun str -> writeString str)
192275

193276
let rec writeILType (ilTy: ILType) =
277+
debug.WriteILType ilTy
278+
194279
match ilTy with
195280
| ILType.TypeVar n ->
196281
writeString "!"
@@ -231,6 +316,8 @@ and [<Sealed>] ItemKeyStoreBuilder() =
231316
writeILType mref.ReturnType
232317

233318
let rec writeType isStandalone (ty: TType) =
319+
debug.WriteType isStandalone ty
320+
234321
match stripTyparEqns ty with
235322
| TType_forall (_, ty) -> writeType false ty
236323

@@ -268,6 +355,8 @@ and [<Sealed>] ItemKeyStoreBuilder() =
268355
writeString nm
269356

270357
and writeMeasure isStandalone (ms: Measure) =
358+
debug.WriteMeasure isStandalone ms
359+
271360
match ms with
272361
| Measure.Var typar ->
273362
writeString ItemKeyTags.typeMeasureVar
@@ -278,20 +367,38 @@ and [<Sealed>] ItemKeyStoreBuilder() =
278367
| _ -> ()
279368

280369
and writeTypar (isStandalone: bool) (typar: Typar) =
370+
debug.WriteTypar isStandalone typar
371+
281372
match typar.Solution with
282373
| Some ty -> writeType isStandalone ty
283374
| _ ->
284375
if isStandalone then
285376
writeInt64 typar.Stamp
286377

287378
let writeValRef (vref: ValRef) =
379+
debug.WriteValRef vref
380+
288381
match vref.MemberInfo with
289382
| Some memberInfo ->
290383
writeString ItemKeyTags.itemValueMember
291-
writeEntityRef memberInfo.ApparentEnclosingEntity
384+
385+
match vref.IsOverrideOrExplicitImpl, vref.MemberInfo with
386+
| true,
387+
Some {
388+
ImplementedSlotSigs = slotSig :: _tail
389+
} -> slotSig.DeclaringType |> writeType false
390+
| _ -> writeEntityRef memberInfo.ApparentEnclosingEntity
391+
292392
writeString vref.LogicalName
293393
writeString ItemKeyTags.parameters
294-
writeType false vref.Type
394+
395+
match vref.IsInstanceMember, tryDestFunTy tcGlobals vref.Type with
396+
// In case of an instance member, we will skip the type of "this" because it will differ
397+
// between the definition and overrides. Also it's not needed to uniquely identify the reference.
398+
| true, ValueSome (_thisTy, funTy) -> funTy
399+
| _ -> vref.Type
400+
|> writeType false
401+
295402
| _ ->
296403
writeString ItemKeyTags.itemValue
297404
writeString vref.LogicalName
@@ -307,6 +414,8 @@ and [<Sealed>] ItemKeyStoreBuilder() =
307414
| Parent eref -> writeEntityRef eref
308415

309416
let writeValue (vref: ValRef) =
417+
debug.WriteValue vref
418+
310419
if vref.IsPropertyGetterMethod || vref.IsPropertySetterMethod then
311420
writeString ItemKeyTags.itemProperty
312421
writeString vref.PropertyName
@@ -322,6 +431,8 @@ and [<Sealed>] ItemKeyStoreBuilder() =
322431
writeValRef vref
323432

324433
let writeActivePatternCase (apInfo: ActivePatternInfo) index =
434+
debug.WriteActivePatternCase apInfo index
435+
325436
writeString ItemKeyTags.itemActivePattern
326437

327438
match apInfo.ActiveTagsWithRanges with
@@ -474,6 +585,7 @@ and [<Sealed>] ItemKeyStoreBuilder() =
474585
let postCount = b.Count
475586

476587
fixup.WriteInt32(postCount - preCount)
588+
debug.FinishItem(item, postCount - preCount)
477589

478590
member _.TryBuildAndReset() =
479591
if b.Count > 0 then
@@ -495,7 +607,10 @@ and [<Sealed>] ItemKeyStoreBuilder() =
495607

496608
b.Clear()
497609

498-
Some(new ItemKeyStore(mmf, length))
610+
let result = Some(new ItemKeyStore(mmf, length, tcGlobals, debug.Items))
611+
debug <- debug.New()
612+
result
499613
else
500614
b.Clear()
615+
debug <- debug.New()
501616
None

src/Compiler/Service/ItemKey.fsi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ namespace FSharp.Compiler.CodeAnalysis
55
open System
66
open FSharp.Compiler.NameResolution
77
open FSharp.Compiler.Text
8+
open FSharp.Compiler.TcGlobals
89

910
/// Stores a list of item key strings and their ranges in a memory mapped file.
1011
[<Sealed>]
@@ -17,7 +18,7 @@ type internal ItemKeyStore =
1718
[<Sealed>]
1819
type internal ItemKeyStoreBuilder =
1920

20-
new: unit -> ItemKeyStoreBuilder
21+
new: TcGlobals -> ItemKeyStoreBuilder
2122

2223
member Write: range * Item -> unit
2324

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

Lines changed: 48 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -533,71 +533,70 @@ match 2 with | Even -> () | Odd -> ()
533533

534534
module Interfaces =
535535

536-
[<Fact>]
537-
let ``We find all references to interface methods`` () =
538-
539-
let source = """
536+
let project() =
537+
let source1 = """
540538
type IInterface1 =
541-
abstract member Method1 : int
539+
abstract member Property1 : int
540+
abstract member Method1: unit -> int
541+
abstract member Method1: string -> int
542542
543543
type IInterface2 =
544-
abstract member Method2 : int
545-
544+
abstract member Property2 : int
545+
"""
546+
let source2 = """
547+
open ModuleFirst
546548
type internal SomeType() =
547549
548550
interface IInterface1 with
549-
member _.Method1 =
550-
42
551+
member _.Property1 = 42
552+
member _.Method1() = 43
553+
member _.Method1(foo) = 43
551554
552555
interface IInterface2 with
553-
member this.Method2 =
554-
(this :> IInterface1).Method1
556+
member this.Property2 =
557+
(this :> IInterface1).Property1
555558
"""
556559

557-
SyntheticProject.Create( { sourceFile "Program" [] with Source = source } ).Workflow {
558-
placeCursor "Program" "Method1"
559-
findAllReferences (expectToFind [
560-
"FileProgram.fs", 4, 20, 27
561-
"FileProgram.fs", 12, 17, 24
562-
"FileProgram.fs", 17, 12, 41 // Not sure why we get the whole range here, but it seems to work fine.
563-
])
564-
}
560+
SyntheticProject.Create(
561+
{ sourceFile "First" [] with Source = source1 },
562+
{ sourceFile "Second" [] with Source = source2 } )
565563

566-
[<Fact>]
567-
let ``We find all references to interface methods starting from implementation`` () =
568-
569-
let source1 = """
570-
type IInterface1 =
571-
abstract member Method1 : int
572-
573-
type IInterface2 =
574-
abstract member Method2 : int
575-
"""
564+
let property1Locations() = [
565+
"FileFirst.fs", 4, 20, 29
566+
"FileSecond.fs", 7, 17, 26
567+
"FileSecond.fs", 13, 12, 43 // Not sure why we get the whole range here, but it seems to work fine.
568+
]
576569

577-
let source2 = """
578-
open ModuleFirst
570+
let method1Locations() = [
571+
"FileFirst.fs", 5, 20, 27
572+
"FileSecond.fs", 8, 17, 24
573+
]
579574

580-
type internal SomeType() =
575+
[<Fact>]
576+
let ``We find all references to interface properties`` () =
577+
project().Workflow {
578+
placeCursor "First" "Property1"
579+
findAllReferences (expectToFind <| property1Locations())
580+
}
581581

582-
interface IInterface1 with
583-
member _.Method1 =
584-
42
582+
[<Fact>]
583+
let ``We find all references to interface properties starting from implementation`` () =
584+
project().Workflow {
585+
placeCursor "Second" "Property1"
586+
findAllReferences (expectToFind <| property1Locations())
587+
}
585588

586-
interface IInterface2 with
587-
member this.Method2 =
588-
(this :> IInterface1).Method1
589-
"""
589+
[<Fact>]
590+
let ``We find all references to interface methods`` () =
591+
project().Workflow {
592+
placeCursor "First" "Method1"
593+
findAllReferences (expectToFind <| method1Locations())
594+
}
590595

591-
SyntheticProject.Create(
592-
{ sourceFile "First" [] with Source = source1 },
593-
{ sourceFile "Second" [] with Source = source2 }
594-
).Workflow {
596+
[<Fact>]
597+
let ``We find all references to interface methods starting from implementation`` () =
598+
project().Workflow {
595599
placeCursor "Second" "Method1"
596-
findAllReferences (expectToFind [
597-
"FileFirst.fs", 4, 20, 27
598-
"FileSecond.fs", 8, 17, 24
599-
"FileSecond.fs", 13, 12, 41 // Not sure why we get the whole range here, but it seems to work fine.
600-
])
600+
findAllReferences (expectToFind <| method1Locations())
601601
}
602602

603-

0 commit comments

Comments
 (0)