-
Notifications
You must be signed in to change notification settings - Fork 880
Record the parameter and type parameter counts of a navigable item #20531
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
5a8cc99
f1b560c
bfb2486
86cfab8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -764,10 +764,39 @@ type NavigableItem = | |
| IsSignature: bool | ||
| Kind: NavigableItemKind | ||
| Container: NavigableContainer | ||
| ParameterCount: int | ||
| TypeParameterCount: int | ||
| } | ||
|
|
||
| [<RequireQualifiedAccess>] | ||
| module NavigateTo = | ||
| let private typeParameterCountOf (typars: SynTyparDecls option) = | ||
| match typars with | ||
| | Some typars -> typars.TyparDecls.Length | ||
| | None -> 0 | ||
|
|
||
| let rec private isUnitType synType = | ||
| match synType with | ||
| | SynType.LongIdent(SynLongIdent([ id ], _, _)) -> id.idText = "unit" | ||
| | SynType.Paren(innerType, _) | ||
| | SynType.WithGlobalConstraints(innerType, _, _) | ||
| // `val f: u: unit -> int` names the argument the compiler drops, so the name must not hide it. | ||
| | SynType.SignatureParameter(usedType = innerType) -> isUnitType innerType | ||
| | _ -> false | ||
|
|
||
| /// 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) = | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am afraid this might alltogether operate on a wrong layer, considering things like:
This should follow existing functions that know how a function is represented in IL.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The layer is forced by what the caller is: That is also the layer the numbers are compared against. They exist for On the three you named:
The remaining hole is the named form the bot found in the other thread, 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. |
||
| match curriedArgInfos, synType with | ||
| | [ [ _ ] ], SynType.Fun(argType = argType) when isUnitType argType -> 0 | ||
| | [ [ _ ] ], SynType.WithGlobalConstraints(SynType.Fun(argType = argType), _, _) when isUnitType argType -> 0 | ||
| | _ -> List.sumBy List.length curriedArgInfos | ||
|
|
||
| let private parameterCountOfBinding (SynValData(memberFlags = memberFlags; valInfo = SynValInfo(curriedArgInfos, _))) = | ||
| match memberFlags, curriedArgInfos with | ||
| | Some memberFlags, _self :: argInfos when memberFlags.IsInstance -> List.sumBy List.length argInfos | ||
| | _ -> List.sumBy List.length curriedArgInfos | ||
|
|
||
| let GetNavigableItems (parsedInput: ParsedInput) : NavigableItem[] = | ||
|
|
||
| let convertToDisplayName name = | ||
|
|
@@ -778,7 +807,7 @@ module NavigateTo = | |
|
|
||
| let result = ResizeArray() | ||
|
|
||
| let addLongIdent kind (lid: LongIdent) (isSignature: bool) (container: NavigableContainer) = | ||
| let addLongIdent kind (lid: LongIdent) (isSignature: bool) (container: NavigableContainer) typeParameterCount = | ||
| if not lid.IsEmpty then | ||
| let name = textOfLid lid | ||
|
|
||
|
|
@@ -789,10 +818,12 @@ module NavigateTo = | |
| IsSignature = isSignature | ||
| Kind = kind | ||
| Container = container | ||
| ParameterCount = 0 | ||
| TypeParameterCount = typeParameterCount | ||
| } | ||
| |> result.Add | ||
|
|
||
| let addIdent kind (id: Ident) (isSignature: bool) (container: NavigableContainer) = | ||
| let addIdentWithArity kind (id: Ident) (isSignature: bool) (container: NavigableContainer) parameterCount typeParameterCount = | ||
| if not (String.IsNullOrEmpty id.idText) then | ||
| let name = convertToDisplayName id.idText | ||
|
|
||
|
|
@@ -803,11 +834,16 @@ module NavigateTo = | |
| IsSignature = isSignature | ||
| Kind = kind | ||
| Container = container | ||
| ParameterCount = parameterCount | ||
| TypeParameterCount = typeParameterCount | ||
| } | ||
| |> result.Add | ||
|
|
||
| let addIdent kind id isSignature container = | ||
| addIdentWithArity kind id isSignature container 0 0 | ||
|
|
||
| let addModule lid isSig container = | ||
| addLongIdent NavigableItemKind.Module lid isSig container | ||
| addLongIdent NavigableItemKind.Module lid isSig container 0 | ||
|
|
||
| let addModuleAbbreviation (id: Ident) isSig container = | ||
| addIdent NavigableItemKind.ModuleAbbreviation id isSig container | ||
|
|
@@ -818,14 +854,17 @@ module NavigateTo = | |
| NavigableContainer.Container(NavigableContainerType.Exception, [ id.idText ], container) | ||
|
|
||
| let addComponentInfo containerType kind (info: SynComponentInfo) isSig container = | ||
| let (SynComponentInfo(typeParams = typeParams)) = info | ||
| let lid = info.LongIdent | ||
| addLongIdent kind lid isSig container | ||
| addLongIdent kind lid isSig container (typeParameterCountOf typeParams) | ||
|
|
||
| NavigableContainer.Container(containerType, pathOfLid lid, container) | ||
|
|
||
| let addValSig kind synValSig isSig container = | ||
| let (SynValSig(ident = SynIdent(id, _))) = synValSig | ||
| addIdent kind id isSig container | ||
| let (SynValSig(ident = SynIdent(id, _); explicitTypeParams = SynValTyparDecls(typars, _); synType = synType; arity = arity)) = | ||
| synValSig | ||
|
|
||
| addIdentWithArity kind id isSig container (parameterCountOfSignature arity synType) (typeParameterCountOf typars) | ||
|
|
||
| let addField synField isSig container = | ||
| let (SynField(idOpt = id)) = synField | ||
|
|
@@ -863,17 +902,25 @@ module NavigateTo = | |
| | Some mf -> mapMemberKind mf.MemberKind | ||
| | _ -> NavigableItemKind.ModuleValue | ||
|
|
||
| let typeParameterCount = | ||
| match headPat with | ||
| | SynPat.LongIdent(typarDecls = Some(SynValTyparDecls(typars, _))) -> typeParameterCountOf typars | ||
| | _ -> 0 | ||
|
|
||
| let addBindingIdent id = | ||
| addIdentWithArity kind id false container (parameterCountOfBinding valData) typeParameterCount | ||
|
|
||
| match headPat with | ||
| | SynPat.LongIdent(longDotId = SynLongIdent([ _; id ], _, _)) -> | ||
| // instance members | ||
| addIdent kind id false container | ||
| addBindingIdent id | ||
| | SynPat.LongIdent(longDotId = SynLongIdent([ id ], _, _)) -> | ||
| // functions | ||
| addIdent kind id false container | ||
| addBindingIdent id | ||
| | SynPat.Named(SynIdent(id, _), _, _, _) | ||
| | SynPat.As(_, SynPat.Named(SynIdent(id, _), _, _, _), _) -> | ||
| // values | ||
| addIdent kind id false container | ||
| addBindingIdent id | ||
| | _ -> () | ||
|
|
||
| let addMember valSig (memberFlags: SynMemberFlags) isSig container = | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| module FSharp.Compiler.Service.Tests.NavigateToTests | ||
|
|
||
| open FSharp.Compiler.EditorServices | ||
| open FSharp.Compiler.Service.Tests.Common | ||
| open FSharp.Compiler.Syntax | ||
| open Xunit | ||
|
|
||
| let private implementation = | ||
| """ | ||
| let value = 1 | ||
| let curried a b = a + b | ||
| let tupledAndCurried (a, b) c = a + b + c | ||
| let takesUnit () = 1 | ||
| let generic<'T> (x: 'T) = x | ||
|
|
||
| type C<'T, 'U>() = | ||
| member _.Method(a: int, b: int) = a + b | ||
| member _.Property = 1 | ||
| static member Static x y = x + y | ||
| """ | ||
|
|
||
| let private signature = | ||
| """ | ||
| module M | ||
|
|
||
| val curried: int -> int -> int | ||
| val takesUnit: unit -> int | ||
| val takesNamedUnit: u: unit -> int | ||
| val generic<'T> : 'T * 'T -> 'T | ||
|
|
||
| type C<'T> = | ||
| member Method: a: int * b: int -> int | ||
| abstract Abstract: unit -> unit | ||
| """ | ||
|
|
||
| let private arityOf (parseTree: ParsedInput) name = | ||
| let item = | ||
| NavigateTo.GetNavigableItems parseTree | ||
| |> Array.find (fun item -> item.Name = name) | ||
|
|
||
| item.ParameterCount, item.TypeParameterCount | ||
|
|
||
| [<Theory>] | ||
| [<InlineData("value", 0, 0)>] | ||
| [<InlineData("curried", 2, 0)>] | ||
| [<InlineData("tupledAndCurried", 3, 0)>] | ||
| [<InlineData("takesUnit", 0, 0)>] | ||
| [<InlineData("generic", 1, 1)>] | ||
| [<InlineData("C", 0, 2)>] | ||
| [<InlineData("Method", 2, 0)>] | ||
| [<InlineData("Property", 0, 0)>] | ||
| [<InlineData("Static", 2, 0)>] | ||
| let ``A declaration in an implementation file counts the parameters it compiles to`` (name: string, parameterCount: int, typeParameterCount: int) = | ||
| Assert.Equal((parameterCount, typeParameterCount), arityOf (getParseResults implementation) name) | ||
|
|
||
| [<Theory>] | ||
| [<InlineData("curried", 2, 0)>] | ||
| [<InlineData("takesUnit", 0, 0)>] | ||
| [<InlineData("takesNamedUnit", 0, 0)>] | ||
| [<InlineData("generic", 2, 1)>] | ||
| [<InlineData("C", 0, 1)>] | ||
| [<InlineData("Method", 2, 0)>] | ||
| [<InlineData("Abstract", 0, 0)>] | ||
| let ``A declaration in a signature file counts the parameters it compiles to`` (name: string, parameterCount: int, typeParameterCount: int) = | ||
| Assert.Equal((parameterCount, typeParameterCount), arityOf (getParseResultsOfSignatureFile signature) name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤖 🕵️ Named
unitparameter counted as 1 in the signature but 0 in the implementation — different Navigate To sort keys for the same zero-argument method.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in
bfb24868a5. The check that drops a solitaryunitlooked throughSynType.ParenandSynType.WithGlobalConstraintsbut not throughSynType.SignatureParameter, so naming the argument hid it:val Search : u: unit -> intcounted one wherelet Search () = 1counted none.isUnitTypesees through the named form now, andNavigateToTestscovers it — the signature fixture gainedval takesNamedUnit: u: unit -> intand asserts(0, 0), next to the unnamedtakesUnitthat was already there. 32 tests pass across both target frameworks.