Skip to content

Commit c6350ce

Browse files
authored
Aligning constructor and type for Rename + FindAllReferences (#14350)
* Issue investigation && repro * Abs filepath remapping for test DSL * itemKeyStore enabled for test workflow * Changing behaviour for FindAllReferences and constructors * tests for strange static init * Making tests pass in net70 * Remove debugging code * Reflect VS-realistic FsharpChecker settings in ProjectWorkflowBuilder (for tests) * Fantomas applied * PR feedback
1 parent 14a1308 commit c6350ce

File tree

4 files changed

+143
-3
lines changed

4 files changed

+143
-3
lines changed

src/Compiler/Service/ItemKey.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,7 @@ and [<Sealed>] ItemKeyStoreBuilder() =
390390
| Item.MethodGroup (_, [ info ], _)
391391
| Item.CtorGroup (_, [ info ]) ->
392392
match info with
393+
| FSMeth (_, ty, vref, _) when vref.IsConstructor -> writeType true ty
393394
| FSMeth (_, _, vref, _) -> writeValRef vref
394395
| ILMeth (_, info, _) ->
395396
info.ILMethodRef.ArgTypes |> List.iter writeILType

tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@
210210
<Compile Include="Signatures\SigGenerationRoundTripTests.fs" />
211211
<Compile Include="FSharpChecker\CommonWorkflows.fs" />
212212
<Compile Include="FSharpChecker\SymbolUse.fs" />
213+
<Compile Include="FSharpChecker\FindReferences.fs" />
213214
<None Include="**\*.cs;**\*.fs;**\*.fsx;**\*.fsi" Exclude="@(Compile)">
214215
<Link>%(RelativeDir)\TestSource\%(Filename)%(Extension)</Link>
215216
</None>
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
module FSharp.Compiler.ComponentTests.FSharpChecker.FindReferences
2+
3+
open FSharp.Compiler.CodeAnalysis
4+
open Xunit
5+
open FSharp.Test.ProjectGeneration
6+
7+
type Occurence = Definition | InType | Use
8+
9+
let deriveOccurence (su:FSharpSymbolUse) =
10+
if su.IsFromDefinition
11+
then Definition
12+
elif su.IsFromType
13+
then InType
14+
elif su.IsFromUse
15+
then Use
16+
else failwith $"Unexpected type of occurence (for this test), symbolUse = {su}"
17+
18+
/// https://github.com/dotnet/fsharp/issues/13199
19+
let reproSourceCode = """
20+
type MyType() =
21+
member x.DoNothing(d:MyType) = ()
22+
23+
let a = MyType()
24+
let b = new MyType()
25+
a.DoNothing(b)
26+
"""
27+
let impFile() = { sourceFile "First" [] with ExtraSource = reproSourceCode }
28+
let createProject() = SyntheticProject.Create(impFile())
29+
30+
[<Fact>]
31+
let ``Finding usage of type via GetUsesOfSymbolInFile should also find it's constructors`` () =
32+
createProject().Workflow
33+
{
34+
checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) ->
35+
36+
let symbolUse = typeCheckResult.GetSymbolUseAtLocation(7, 11, "type MyType() =", ["MyType"]).Value
37+
let references =
38+
typeCheckResult.GetUsesOfSymbolInFile(symbolUse.Symbol)
39+
|> Array.sortBy (fun su -> su.Range.StartLine)
40+
|> Array.map (fun su -> su.Range.StartLine, su.Range.StartColumn, su.Range.EndColumn, deriveOccurence su)
41+
42+
Assert.Equal<(int*int*int*Occurence)>(
43+
[| 7,5,11,Definition
44+
8,25,31,InType
45+
10,8,14,Use
46+
11,12,18,Use
47+
|],references) )
48+
}
49+
50+
51+
[<Fact>]
52+
let ``Finding usage of type via FindReference should also find it's constructors`` () =
53+
createProject().Workflow
54+
{
55+
placeCursor "First" 7 11 "type MyType() =" ["MyType"]
56+
findAllReferences "First" (fun (ranges:list<FSharp.Compiler.Text.range>) ->
57+
let ranges =
58+
ranges
59+
|> List.sortBy (fun r -> r.StartLine)
60+
|> List.map (fun r -> r.StartLine, r.StartColumn, r.EndColumn)
61+
|> Array.ofSeq
62+
63+
Assert.Equal<(int*int*int)>(
64+
[| 7,5,11 // Typedef itself
65+
8,25,31 // Usage within type
66+
10,8,14 // "a= ..." constructor
67+
11,12,18 // "b= ..." constructor
68+
|],ranges) )
69+
70+
}
71+
72+
[<Fact>]
73+
let ``Finding usage of type via FindReference works across files`` () =
74+
let secondFile = { sourceFile "Second" ["First"] with ExtraSource = """
75+
open ModuleFirst
76+
let secondA = MyType()
77+
let secondB = new MyType()
78+
secondA.DoNothing(secondB)
79+
"""}
80+
let original = createProject()
81+
let project = {original with SourceFiles = original.SourceFiles @ [secondFile]}
82+
project.Workflow
83+
{
84+
placeCursor "First" 7 11 "type MyType() =" ["MyType"]
85+
findAllReferences "Second" (fun (ranges:list<FSharp.Compiler.Text.range>) ->
86+
let ranges =
87+
ranges
88+
|> List.sortBy (fun r -> r.StartLine)
89+
|> List.map (fun r -> r.StartLine, r.StartColumn, r.EndColumn)
90+
|> Array.ofSeq
91+
92+
Assert.Equal<(int*int*int)>(
93+
[| 9,14,20 // "secondA = ..." constructor
94+
10,18,24 // "secondB = ..." constructor
95+
|],ranges) )
96+
97+
}
98+

tests/FSharp.Test.Utilities/ProjectGeneration.fs

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -356,11 +356,19 @@ module ProjectOperations =
356356

357357
type WorkflowContext =
358358
{ Project: SyntheticProject
359-
Signatures: Map<string, string> }
359+
Signatures: Map<string, string>
360+
Cursor : FSharp.Compiler.CodeAnalysis.FSharpSymbolUse option }
360361

361362
type ProjectWorkflowBuilder(initialProject: SyntheticProject, ?checker: FSharpChecker) =
362363

363-
let checker = defaultArg checker (FSharpChecker.Create())
364+
let checker =
365+
defaultArg
366+
checker
367+
(FSharpChecker.Create(
368+
keepAllBackgroundSymbolUses = false,
369+
enableBackgroundItemKeyStoreAndSemanticClassification = true,
370+
enablePartialTypeChecking = true
371+
))
364372

365373
let mapProject f workflow =
366374
async {
@@ -392,7 +400,8 @@ type ProjectWorkflowBuilder(initialProject: SyntheticProject, ?checker: FSharpCh
392400

393401
return
394402
{ Project = initialProject
395-
Signatures = Map signatures }
403+
Signatures = Map signatures
404+
Cursor = None }
396405
}
397406

398407
member this.Run(workflow: Async<WorkflowContext>) =
@@ -442,6 +451,37 @@ type ProjectWorkflowBuilder(initialProject: SyntheticProject, ?checker: FSharpCh
442451
return { ctx with Signatures = ctx.Signatures.Add(fileId, newSignature) }
443452
}
444453

454+
/// Find a symbol using the provided range, mimicing placing a cursor on it in IDE scenarios
455+
[<CustomOperation "placeCursor">]
456+
member this.PlaceCursor(workflow: Async<WorkflowContext>, fileId, line, colAtEndOfNames, fullLine, symbolNames) =
457+
async {
458+
let! ctx = workflow
459+
let! results = checkFile fileId ctx.Project checker
460+
let typeCheckResults = getTypeCheckResult results
461+
462+
let su = typeCheckResults.GetSymbolUseAtLocation(line,colAtEndOfNames,fullLine,symbolNames)
463+
464+
return {ctx with Cursor = su}
465+
}
466+
467+
468+
469+
/// Find all references within a single file, results are provided to the 'processResults' function
470+
[<CustomOperation "findAllReferences">]
471+
member this.FindAllReferences(workflow: Async<WorkflowContext>, fileId: string, processResults) =
472+
async{
473+
let! ctx = workflow
474+
let po = ctx.Project.GetProjectOptions checker
475+
let s = ctx.Cursor |> Option.defaultWith (fun () -> failwith $"Please place cursor at a valid location via {nameof(this.PlaceCursor)} first")
476+
let file = ctx.Project.Find fileId
477+
let absFileName = ctx.Project.ProjectDir ++ file.FileName
478+
let! results = checker.FindBackgroundReferencesInFile(absFileName,po, s.Symbol)
479+
480+
processResults (results |> Seq.toList)
481+
482+
return ctx
483+
}
484+
445485
/// Parse and type check given file and process the results using `processResults` function.
446486
[<CustomOperation "checkFile">]
447487
member this.CheckFile(workflow: Async<WorkflowContext>, fileId: string, processResults) =

0 commit comments

Comments
 (0)