未验证 提交 c6350ce3 编写于 作者: T Tomas Grosup 提交者: GitHub

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
上级 14a1308b
......@@ -390,6 +390,7 @@ and [<Sealed>] ItemKeyStoreBuilder() =
| Item.MethodGroup (_, [ info ], _)
| Item.CtorGroup (_, [ info ]) ->
match info with
| FSMeth (_, ty, vref, _) when vref.IsConstructor -> writeType true ty
| FSMeth (_, _, vref, _) -> writeValRef vref
| ILMeth (_, info, _) ->
info.ILMethodRef.ArgTypes |> List.iter writeILType
......
......@@ -210,6 +210,7 @@
<Compile Include="Signatures\SigGenerationRoundTripTests.fs" />
<Compile Include="FSharpChecker\CommonWorkflows.fs" />
<Compile Include="FSharpChecker\SymbolUse.fs" />
<Compile Include="FSharpChecker\FindReferences.fs" />
<None Include="**\*.cs;**\*.fs;**\*.fsx;**\*.fsi" Exclude="@(Compile)">
<Link>%(RelativeDir)\TestSource\%(Filename)%(Extension)</Link>
</None>
......
module FSharp.Compiler.ComponentTests.FSharpChecker.FindReferences
open FSharp.Compiler.CodeAnalysis
open Xunit
open FSharp.Test.ProjectGeneration
type Occurence = Definition | InType | Use
let deriveOccurence (su:FSharpSymbolUse) =
if su.IsFromDefinition
then Definition
elif su.IsFromType
then InType
elif su.IsFromUse
then Use
else failwith $"Unexpected type of occurence (for this test), symbolUse = {su}"
/// https://github.com/dotnet/fsharp/issues/13199
let reproSourceCode = """
type MyType() =
member x.DoNothing(d:MyType) = ()
let a = MyType()
let b = new MyType()
a.DoNothing(b)
"""
let impFile() = { sourceFile "First" [] with ExtraSource = reproSourceCode }
let createProject() = SyntheticProject.Create(impFile())
[<Fact>]
let ``Finding usage of type via GetUsesOfSymbolInFile should also find it's constructors`` () =
createProject().Workflow
{
checkFile "First" (fun (typeCheckResult: FSharpCheckFileResults) ->
let symbolUse = typeCheckResult.GetSymbolUseAtLocation(7, 11, "type MyType() =", ["MyType"]).Value
let references =
typeCheckResult.GetUsesOfSymbolInFile(symbolUse.Symbol)
|> Array.sortBy (fun su -> su.Range.StartLine)
|> Array.map (fun su -> su.Range.StartLine, su.Range.StartColumn, su.Range.EndColumn, deriveOccurence su)
Assert.Equal<(int*int*int*Occurence)>(
[| 7,5,11,Definition
8,25,31,InType
10,8,14,Use
11,12,18,Use
|],references) )
}
[<Fact>]
let ``Finding usage of type via FindReference should also find it's constructors`` () =
createProject().Workflow
{
placeCursor "First" 7 11 "type MyType() =" ["MyType"]
findAllReferences "First" (fun (ranges:list<FSharp.Compiler.Text.range>) ->
let ranges =
ranges
|> List.sortBy (fun r -> r.StartLine)
|> List.map (fun r -> r.StartLine, r.StartColumn, r.EndColumn)
|> Array.ofSeq
Assert.Equal<(int*int*int)>(
[| 7,5,11 // Typedef itself
8,25,31 // Usage within type
10,8,14 // "a= ..." constructor
11,12,18 // "b= ..." constructor
|],ranges) )
}
[<Fact>]
let ``Finding usage of type via FindReference works across files`` () =
let secondFile = { sourceFile "Second" ["First"] with ExtraSource = """
open ModuleFirst
let secondA = MyType()
let secondB = new MyType()
secondA.DoNothing(secondB)
"""}
let original = createProject()
let project = {original with SourceFiles = original.SourceFiles @ [secondFile]}
project.Workflow
{
placeCursor "First" 7 11 "type MyType() =" ["MyType"]
findAllReferences "Second" (fun (ranges:list<FSharp.Compiler.Text.range>) ->
let ranges =
ranges
|> List.sortBy (fun r -> r.StartLine)
|> List.map (fun r -> r.StartLine, r.StartColumn, r.EndColumn)
|> Array.ofSeq
Assert.Equal<(int*int*int)>(
[| 9,14,20 // "secondA = ..." constructor
10,18,24 // "secondB = ..." constructor
|],ranges) )
}
......@@ -356,11 +356,19 @@ module ProjectOperations =
type WorkflowContext =
{ Project: SyntheticProject
Signatures: Map<string, string> }
Signatures: Map<string, string>
Cursor : FSharp.Compiler.CodeAnalysis.FSharpSymbolUse option }
type ProjectWorkflowBuilder(initialProject: SyntheticProject, ?checker: FSharpChecker) =
let checker = defaultArg checker (FSharpChecker.Create())
let checker =
defaultArg
checker
(FSharpChecker.Create(
keepAllBackgroundSymbolUses = false,
enableBackgroundItemKeyStoreAndSemanticClassification = true,
enablePartialTypeChecking = true
))
let mapProject f workflow =
async {
......@@ -392,7 +400,8 @@ type ProjectWorkflowBuilder(initialProject: SyntheticProject, ?checker: FSharpCh
return
{ Project = initialProject
Signatures = Map signatures }
Signatures = Map signatures
Cursor = None }
}
member this.Run(workflow: Async<WorkflowContext>) =
......@@ -442,6 +451,37 @@ type ProjectWorkflowBuilder(initialProject: SyntheticProject, ?checker: FSharpCh
return { ctx with Signatures = ctx.Signatures.Add(fileId, newSignature) }
}
/// Find a symbol using the provided range, mimicing placing a cursor on it in IDE scenarios
[<CustomOperation "placeCursor">]
member this.PlaceCursor(workflow: Async<WorkflowContext>, fileId, line, colAtEndOfNames, fullLine, symbolNames) =
async {
let! ctx = workflow
let! results = checkFile fileId ctx.Project checker
let typeCheckResults = getTypeCheckResult results
let su = typeCheckResults.GetSymbolUseAtLocation(line,colAtEndOfNames,fullLine,symbolNames)
return {ctx with Cursor = su}
}
/// Find all references within a single file, results are provided to the 'processResults' function
[<CustomOperation "findAllReferences">]
member this.FindAllReferences(workflow: Async<WorkflowContext>, fileId: string, processResults) =
async{
let! ctx = workflow
let po = ctx.Project.GetProjectOptions checker
let s = ctx.Cursor |> Option.defaultWith (fun () -> failwith $"Please place cursor at a valid location via {nameof(this.PlaceCursor)} first")
let file = ctx.Project.Find fileId
let absFileName = ctx.Project.ProjectDir ++ file.FileName
let! results = checker.FindBackgroundReferencesInFile(absFileName,po, s.Symbol)
processResults (results |> Seq.toList)
return ctx
}
/// Parse and type check given file and process the results using `processResults` function.
[<CustomOperation "checkFile">]
member this.CheckFile(workflow: Async<WorkflowContext>, fileId: string, processResults) =
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册