Show C# and VB uses of an F# symbol in Find All References - #20463
Show C# and VB uses of an F# symbol in Find All References#20463xperiandri wants to merge 8 commits into
Conversation
❗ Release notes requiredYou can open this PR in browser to add release notes: open in github.dev
|
This comment has been minimized.
This comment has been minimized.
T-Gro
left a comment
There was a problem hiding this comment.
🤖 🕵️ AI review — verify independently.
| match! project.GetCompilationAsync cancellationToken with | ||
| | null -> return Seq.empty | ||
| | compilation -> | ||
| match DocumentationCommentId.GetFirstSymbolForDeclarationId(docId, compilation) with |
There was a problem hiding this comment.
🤖 🕵️ [P2] Find All References reports the unrelated C# method and misses the F# call. With the F# reference aliased as FSLib, the editor search returns Other()'s Value span instead of Real()'s. Resolve the ID within the declaring assembly rather than taking the compilation-wide first match.
// F# library
namespace Collision
type Widget() =
static member Value() = 1// Reference the F# library with alias FSLib.
extern alias FSLib;
namespace Collision {
public class Widget { public static int Value() => 2; }
}
class Consumer {
int Real() => FSLib::Collision.Widget.Value(); // missed
int Other() => Collision.Widget.Value(); // incorrectly reported
}| | docSig when value.LiteralValue.IsSome && docSig.StartsWith("P:", StringComparison.Ordinal) -> $"F:{docSig.Substring 2}" | ||
| | docSig -> docSig | ||
| | :? FSharpEntity as entity -> entity.XmlDocSig | ||
| | :? FSharpField as field -> field.XmlDocSig |
There was a problem hiding this comment.
🤖🕵️ Enum IDs do not resolve: P:N.Color.Red returns null, but F:N.Color.Red resolves the field.
namespace N
type Color = Red = 0 | Blue = 1Map enum-field IDs to F: and cover C#/VB callers.
…r tests Test helpers so far put every synthetic file into one Roslyn project. CreateMultiProjectSolution creates one project per synthetic project with project references, the way VS wires project-to-project references; CreateMultiTargetSolution creates one project per target instance sharing the project path and the document paths, the way VS loads a multi-targeted project. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The IFSharpFindUsagesContext stub of FindReferencesTests moves to RoslynTestHelpers.CreateFindUsagesContext so other test files can collect the definitions and references a search reports. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The test host served language services for F# only. It now creates them for any language from the same export provider, imports the C# workspace parts, accepts .cs documents, and gains two helpers: CompileToAssembly builds a synthetic project into its OutputFilename with the checker's options, and AddCSharpProject adds a C# library referencing the framework of the F# options and given assemblies. The assemblies are added after AdhocWorkspace.AddProject, which would otherwise rewrite a reference to a project's output into a project reference; VS keeps a C# → F# reference as metadata. The smoke tests check that the C# compilation resolves the documentation comment id of an F# function, that SymbolFinder finds its call site, and that ProjectFiltering sees the C# project as a consumer of the F# assembly. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Find All References searched F# documents only: C# and VB projects were never in scope, so call sites of F# functions from C# were missing. After the F# uses are reported, the symbol's documentation comment id (its XmlDocSig, the compiled form Roslyn resolves) is looked up in the compilation of every C# or VB project whose metadata references include the assembly of a project declaring the symbol, and SymbolFinder.FindReferencesAsync reports the locations under the F# definition item. Multi-targeted consumers report each file span once. Only Find All References does this: Find Implementations and Rename keep their F#-only scope, and symbols internal to their project or declared in external assemblies are skipped. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
An F# library compiled to its assembly and a C# consumer calling its function: Find All References on the F# declaration reports the C# call site, Find Implementations does not, and DocumentationCommentId gives the compiled form for a module, a function and nothing for a local. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
FCS names a module literal `P:` like any module value, but it compiles to a const field, so `DocumentationCommentId.GetFirstSymbolForDeclarationId` found nothing and Find All References showed no C# uses of it. The id now starts with `F:` for a literal. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The cross-language search ran after the F# one and visited the consumers one by one, so on a solution where the F# search takes minutes the C# call sites were the last thing to appear. The search now starts before the F# one and runs a few consumers at a time, each search building a compilation; the results are still reported after the F# uses, each file span once, so the order in the window is unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
8b67667 to
450d60b
Compare
This comment has been minimized.
This comment has been minimized.
|
🔍 Tooling Safety Check — Affects-Build-Infra, Affects-Design-Time, Affects-Restore
|
Description
Find All References on an F# symbol never listed the call sites in C# or VB projects. Roslyn hands F# entirely to
IFSharpFindUsagesService, and the F# search only ever visited F# documents: no C# project was in scope andProject.FindFSharpReferencesAsyncfilters byisFSharpSourceFile. This is not related to[<CompiledName>]— the compiled name is what the cross-language lookup already uses.Change (
Symbols.fs,FindUsagesService.fs): after the F# uses have been reported,FSharpSymbol.DocumentationCommentId— the symbol'sXmlDocSig, the compiled form Roslyn resolves — is looked up withDocumentationCommentId.GetFirstSymbolForDeclarationIdin the compilation of every C# or VB project whose metadata references include the built assembly of a project declaring the symbol (ProjectFiltering.getProjectsReferencingAssemblyonOutputFilePath; Visual Studio keeps a C# → F# project reference as aPortableExecutableReference, seeProjectSystemProjectFactory.CanConvertMetadataReferenceToProjectReference).SymbolFinder.FindReferencesAsyncis limited to that project's documents, the locations are reported asFSharpSourceReferenceItems under the F# definition item, and each file span is reported once across the target-framework instances of a consumer. The consumers are searched a few at a time (each search may build a compilation) and concurrently with the F# projects, since the id and the definition items are known before either search starts; the C# locations are reported after the F# uses, so the window keeps its order. Only Find All References does this; Find Implementations and Rename keep their F#-only scope, and symbols internal to their project or declared in external assemblies are skipped. Expected failures arenullresults (no compilation, unresolved id, unbuilt or stale assembly) and simply yield no C# results.Known limits: a union case resolves to its nested type, so
U.NewCase(…)calls are not found; conversion operators need~retin the id; active patterns have no compiled name Roslyn can parse.Test infrastructure: the test host now serves language services for any language from the same export provider and imports
Microsoft.CodeAnalysis.CSharp.Workspaces(added toeng/Packages.propsat the Roslyn version the tests already use).RoslynTestHelpers.CompileToAssemblybuilds aSyntheticProjectinto itsOutputFilenamewith the checker's options;AddCSharpProjectadds a C# library referencing the framework of those options and the given assemblies — added afterAdhocWorkspace.AddProject, which otherwise rewrites a reference to a project's output into a project reference, unlike Visual Studio. The first two commits are shared with #20462 and #20464.Tests (
FindReferencesFromCSharpTests): the C# compilation resolves the doc comment id of the F# function andSymbolFinderfinds its call site;ProjectFilteringsees the C# project as a consumer of the F# assembly;DocumentationCommentIdgivesT:/M:ids for a module and a function and nothing for a local; Find All References on the F# declaration reports exactly the C#twicespan; Find Implementations reports nothing.Checklist
Test cases added
Performance benchmarks added in case of performance changes
Release notes entry updated:
🤖 Generated with Claude Code