Skip to content

Fix #891: arrow lambdas as named/extension arguments and extension-method hover#897

Merged
DavidObando merged 1 commit into
mainfrom
fix/issue-891-lambda-args-and-hover
Jun 20, 2026
Merged

Fix #891: arrow lambdas as named/extension arguments and extension-method hover#897
DavidObando merged 1 commit into
mainfrom
fix/issue-891-lambda-args-and-hover

Conversation

@DavidObando

Copy link
Copy Markdown
Owner

Fixes #891

Issue #891 reported three distinct problems when using lambdas as arguments to method/constructor calls. All three are fixed here, with regression tests for each.

Problem A — arrow lambda as a NAMED argument to a ctor/method taking Func<...>

Symptom: DoctorService(httpClientFactory: () -> { throw ... }) failed with the misleading Named arguments are only supported for data-struct .copy(...). (GS0162), while the func() T { ... } form worked.

Root cause: A block-body arrow lambda that only throws infers () -> void. CLR constructor overload resolution then fails to match the Func<...> parameter (void ≠ Func), and the single named-argument call falls through to the single-arg conversion path, which rebinds the NamedArgumentExpressionSyntax directly and reports GS0162.

Fix:

  • ExpressionBinder.Calls.cs (TryBindClrConstructorFromType): discover the delegate function-type for each argument position from the candidate constructors (by name for named args, by position otherwise) and target-type arrow/func literal arguments before binding, via BindCallArgumentWithDelegateTargetTyping / TryResolveDelegateTargetFromCandidates.
  • LambdaBinder.cs (InferLambdaReturnType): when a target delegate type is supplied and the lambda body never completes normally (all paths throw), adopt the target's return type — exactly as the equivalent func() T { throw ... } literal does. Reuses ControlFlowGraph.AllPathsReturn.

Problem B — arrow lambda passed to a generic LINQ extension method (Single)

Symptom: list.Single((x) -> x == 3) failed with Cannot find function Single. / GS0304, while list.Single(func(x int32) bool { ... }) worked.

Root cause: An un-typed arrow lambda parameter cannot be bound bare (GS0304), producing an error-typed argument whose null CLR type aborts overload resolution, so the generic extension method Single<TSource> is never matched.

Fix: ExpressionBinder.Calls.cs (BindAccessorCall): un-typed arrow lambda arguments are deferred to a placeholder (suppressing GS0304). ResolveDeferredArrowLambdaArguments probes the applicable candidates (instance methods on the receiver, imported extension methods, or static methods of the accessed class) with the lambda slot treated as unconstrained — generic inference skips it and infers TSource from the receiver — then re-binds the lambda against the resolved, closed delegate parameter (Func<TSource,bool>). The predicate body then type-checks to bool.

Problem C — wrong hover on extension methods

Symptom: Hovering the Single extension method showed struct float32 / "Represents a single-precision floating-point number." — i.e. it matched the type System.Single by name instead of the method.

Root cause: HoverComputer.BuildImportedClrModel resolved the token as a type name (ResolveImportedClrType("Single")System.Single) before any member resolution, and member resolution had no support for extension methods.

Fix:

  • SemanticLookup.cs: new TryResolveInvokedImportedMethod resolves the actual method an invoked call bound to, by scanning the lowered bound program for imported call nodes matching the method name and arity (the extension form carries one extra leading receiver parameter). This naturally handles generic extension methods.
  • HoverComputer.cs: when the hovered token is the identifier of an invoked call, resolve the invoked method before type-name resolution.

Tests

  • test/Compiler.Tests/Emit/Issue891LambdaArgumentEmitTests.cs (emit + ilverify + run):
    • named-arg arrow lambda Func argument (value-returning and throw-only), incl. a DoctorService-style helper assembly whose ctor is (int logger = 0, Func<string> httpClientFactory = null) invoked with only the named Func argument;
    • LINQ Single with an arrow-lambda predicate over List[int32] and List[string].
  • test/LanguageServer.Tests/HoverHandlerTests.cs: hover over the Single extension-method invocation resolves to the method (and not float32), for both the arrow-lambda and func(...) predicate forms.

Validation

Local dotnet build GSharp.sln -c Release succeeds (0 warnings/errors). Full dotnet test GSharp.sln -c Release: 5480 passed, 1 skipped, 0 failed (Core 3340, Compiler 1353, LanguageServer 337, Interpreter 308, Extensions 104, Sdk 38). Compiler.Tests validate emitted IL with ilverify.

…hover

Three distinct problems reported in issue #891 are addressed:

A) Arrow lambda as a NAMED argument to a constructor/method taking a
   Func<...>: the delegate-typed parameter now target-types the lambda
   before binding, so `Service(httpClientFactory: () -> { throw ... })`
   binds as Func<...> instead of misrouting to the data-struct .copy(...)
   named-argument path (the misleading GS0162). A statement-body arrow
   lambda whose every path throws now adopts the target delegate's return
   type (InferLambdaReturnType), mirroring `func() T { throw ... }`.

B) Un-typed arrow lambda passed to a generic LINQ extension method
   (Single<TSource>(this IEnumerable<TSource>, Func<TSource,bool>)):
   such lambdas are now deferred during argument binding, the overload is
   resolved with the lambda slot treated as unconstrained (so TSource is
   inferred from the receiver), and the lambda is then bound against the
   resolved closed delegate parameter. Previously this reported
   "Cannot find function Single" / GS0304.

C) Hover over an invoked (extension) method now resolves the actual
   method from the bound program before falling back to type-name
   resolution, so hovering `Single` shows the method instead of the
   unrelated type float32 (System.Single).

Adds emit/runtime regression tests (DoctorService-style ctor with named
Func arg incl. throw-only body; LINQ Single with arrow-lambda predicate)
and LanguageServer hover tests for the extension-method case.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@DavidObando DavidObando merged commit a0b04bb into main Jun 20, 2026
7 checks passed
@DavidObando DavidObando deleted the fix/issue-891-lambda-args-and-hover branch June 20, 2026 01:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Using lambda as parameter to method call

1 participant