Record the parameter and type parameter counts of a navigable item - #20531
xperiandri wants to merge 2 commits into
Conversation
❗ Release notes requiredYou can open this PR in browser to add release notes: open in github.dev Caution No release notes found for the changed paths (see table below). Please make sure to add an entry with an informative description of the change as well as link to this pull request, issue and language suggestion if applicable. Release notes for this repository are based on Keep A Changelog format. The following format is recommended for this repository: `* . (PR #XXXXX)`
If you believe that release notes are not necessary for this PR, please add NO_RELEASE_NOTES label to the pull request.
|
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.
| | SynType.LongIdent(SynLongIdent([ id ], _, _)) -> id.idText = "unit" | ||
| | SynType.Paren(innerType, _) | ||
| | SynType.WithGlobalConstraints(innerType, _, _) -> isUnitType innerType | ||
| | _ -> false |
There was a problem hiding this comment.
🤖 🕵️ Named unit parameter counted as 1 in the signature but 0 in the implementation — different Navigate To sort keys for the same zero-argument method.
// NamedUnit.fsi
module NamedUnit
val Search : u: unit -> int
// NamedUnit.fs
module NamedUnit
let Search () = 1|
|
||
| /// The parameters of the compiled method: the parser leaves a solitary unit argument of a signature in its arity, | ||
| /// where a binding has already dropped it. | ||
| let private parameterCountOfSignature (SynValInfo(curriedArgInfos, _)) (synType: SynType) = |
There was a problem hiding this comment.
I am afraid this might alltogether operate on a wrong layer, considering things like:
- unit
- byrefs
- units of measure
This should follow existing functions that know how a function is represented in IL.
There was a problem hiding this comment.
The layer is forced by what the caller is: NavigateTo.GetNavigableItems runs on a parse tree alone. Navigate To has to answer for every document in the solution, and it has to answer while the solution is still loading, so no checking has happened and there is no Val, no TType, nothing the arity functions (GetTopValTypeInFSharpForm, ArgInfosOfMember) could be handed. Reaching for them here would mean checking every document in the solution to fill in a sort tiebreak.
That is also the layer the numbers are compared against. They exist for NavigateToSearchResultHelpers.ComputeSecondarySort, which orders results that already matched the pattern equally well, and C# and Visual Basic fill them the same way — CSharpDeclaredSymbolInfoFactoryService takes ParameterList.Parameters.Count and TypeParameterList?.Parameters.Count straight off the declaration syntax. A wrong count moves one same-named result past another in that list; it is never asked whether two members are the same member.
On the three you named:
- byrefs — a
byrefparameter is still one parameter in IL, so the count is unaffected; - units of measure — a measure annotates the type of a parameter and adds none, so likewise;
- unit — this one really does differ, and it is the only one that does. A solitary
unitargument is dropped by the compiler but left in the arity the parser produces for a signature, soval f : unit -> intandlet f () = 1disagreed. That is handled.
The remaining hole is the named form the bot found in the other thread, val Search : u: unit -> int: the check looks through SynType.Paren and SynType.WithGlobalConstraints but not through SynType.SignatureParameter, so a named unit still counts as one. Fixing that, and adding both spellings to the tests.
If the team would rather the two numbers be IL-accurate, then they cannot come from this walker at all and the tiebreak needs a different source — happy to take that route instead, but it is a different change from this one.
b0f52c9 to
188278c
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Navigate To orders matches that are otherwise equal by these counts for C# and VB. NavigableItem now carries them: every curried and tupled argument of the method a declaration compiles to, without the instance and without a solitary unit argument, and its explicitly declared type parameters. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
188278c to
f1b560c
Compare
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Two correctness issues remain in parameter-count computation, and the release note is in the wrong version file.
Review effort: Lite
Findings: 1
What changed in this PR
Adds parameter and type-parameter counts to F# NavigableItem results for consistent Navigate To ordering.
Changes:
- Computes counts from implementation and signature syntax.
- Extends the public API and surface-area baseline.
- Adds comprehensive tests and release notes.
| File | Summary |
|---|---|
tests/FSharp.Compiler.Service.Tests/NavigateToTests.fs |
Adds coverage for declaration count behavior. |
tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj |
Includes the updated tests. |
tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl |
Updates the API baseline. |
src/Compiler/Service/ServiceNavigation.fsi |
Exposes and documents the new fields. |
src/Compiler/Service/ServiceNavigation.fs |
Computes declaration counts; labeled unit signatures and property setters require corrections (moderate, 3 and 1 votes). |
docs/release-notes/.FSharp.Compiler.Service/11.0.100.md |
Adds release notes, but the entry should move to the current 11.0.200 file (nit, 1 vote). |
| | SynType.LongIdent(SynLongIdent([ id ], _, _)) -> id.idText = "unit" | ||
| | SynType.Paren(innerType, _) | ||
| | SynType.WithGlobalConstraints(innerType, _, _) -> isUnitType innerType | ||
| | _ -> false |
|
🔍 Tooling Safety Check — Affects-Design-Time
|

Description
Navigate To orders matches of the same kind by a secondary sort. For C# and VB that key holds each declaration's parameter and type parameter counts, taken from Roslyn's declaration index, after the folder distance to the file being edited. dotnet/roslyn#85280 computes the same key for F# results, and needs F# to supply the two counts.
NavigateTo.GetNavigableItemsis where F# builds its declaration list from the parse tree, soNavigableItemnow carries:ParameterCount: every curried and tupled argument of the method the declaration compiles to. An instance member's self argument is not counted, and neither is a solitary unit argument:let f () = …andval f: unit -> intcount 0,let f () x = …counts 2. Bindings take it from the arity the parser inferred. For signatures, the parser leaves a solitary unit argument in the arity, so it is recognised from the type.TypeParameterCount: the explicitly declared type parameters of a type, binding or signature.This changes the public surface:
NavigableItemis a record, so code that constructs one must supply the two fields.Checklist
NavigateToTestscovers values, curried, tupled and unit functions, generic functions, types, instance and static members and properties, in implementation and signature files.🤖 Generated with Claude Code