Two related gaps in how C# parameter lists reach the graph, both traced to the vendored grammar and both reducing what overload resolution can do.
1. A params entry is not a parameter node, so the method gets no arity at all
In tree-sitter-c-sharp v0.23.5 _parameter_array is a hidden rule and params is an anonymous token, so a params entry never materialises as a parameter node — the list flattens into loose array_type / generic_name + identifier siblings. Verified for object[], int[], object?[], ReadOnlySpan<int> and List<int>.
csharpParamArity (internal/parser/languages/csharp_arity.go:111) therefore bails with ok=false — deliberately, since a partial count would be worse than none — and the method carries no param_count / param_required / param_variadic. It is then universally applicable, so it never narrows out and keeps its siblings ambiguous.
Measured: l.Log("hello") (arg_count=1) against Log(this ILogger, string) and Log(this ILogger, string, params object[]) stays unresolved::*.Log. Dropping only the params keyword makes it bind. This is exactly the Log(msg) / Log(fmt, params object[]) shape .NET logging helpers use.
Corollary: param_variadic can never be stamped, so the variadic branch in csharp_applicability.go:137-139 is unreachable today. It is correct forward-compatible logic, not dead weight — but it is currently untested by anything real.
2. params and discard parameters emit no KindParam node
internal/parser/languages/csharp_function_shape.go:38-40,55 skips any child that is not a parameter (which excludes params entries per the above) and any parameter named _. Measured: Log(string fmt, params object[] rest) → 1 param node; ParamsOnly(params int[] xs) → 0; Disc(this ILogger l, int _, string name) → 2.
Positions are correct since #536 (the discard occupies its slot), but the nodes are still absent, so any consumer counting EdgeParamOf under-counts the signature.
Possible fix
Both would be solved by recognising the flattened params shape and reconstructing the entry. That is grammar-version-specific, so it needs a test that fails loudly if a grammar bump changes the shape — the current ok=false behaviour is the safe fallback and should stay as the default when the shape is unrecognised.
Two related gaps in how C# parameter lists reach the graph, both traced to the vendored grammar and both reducing what overload resolution can do.
1. A
paramsentry is not aparameternode, so the method gets no arity at allIn tree-sitter-c-sharp v0.23.5
_parameter_arrayis a hidden rule andparamsis an anonymous token, so aparamsentry never materialises as aparameternode — the list flattens into loosearray_type/generic_name+identifiersiblings. Verified forobject[],int[],object?[],ReadOnlySpan<int>andList<int>.csharpParamArity(internal/parser/languages/csharp_arity.go:111) therefore bails withok=false— deliberately, since a partial count would be worse than none — and the method carries noparam_count/param_required/param_variadic. It is then universally applicable, so it never narrows out and keeps its siblings ambiguous.Measured:
l.Log("hello")(arg_count=1) againstLog(this ILogger, string)andLog(this ILogger, string, params object[])staysunresolved::*.Log. Dropping only theparamskeyword makes it bind. This is exactly theLog(msg)/Log(fmt, params object[])shape .NET logging helpers use.Corollary:
param_variadiccan never be stamped, so the variadic branch incsharp_applicability.go:137-139is unreachable today. It is correct forward-compatible logic, not dead weight — but it is currently untested by anything real.2.
paramsand discard parameters emit noKindParamnodeinternal/parser/languages/csharp_function_shape.go:38-40,55skips any child that is not aparameter(which excludesparamsentries per the above) and any parameter named_. Measured:Log(string fmt, params object[] rest)→ 1 param node;ParamsOnly(params int[] xs)→ 0;Disc(this ILogger l, int _, string name)→ 2.Positions are correct since #536 (the discard occupies its slot), but the nodes are still absent, so any consumer counting
EdgeParamOfunder-counts the signature.Possible fix
Both would be solved by recognising the flattened
paramsshape and reconstructing the entry. That is grammar-version-specific, so it needs a test that fails loudly if a grammar bump changes the shape — the currentok=falsebehaviour is the safe fallback and should stay as the default when the shape is unrecognised.