fix(csharp): resolve fluent-chain receivers, generic member names, and type twins for call recall - #787
Conversation
There was a problem hiding this comment.
Code Review
This pull request enhances C# type inference and call resolution, particularly for generic types and fluent/chained method calls. It introduces tracking of C# class generic arity and method return types during ingestion. These additions allow the type inference engine to resolve chained invocations (such as Policy.Handle<T>().Wrap(...)), handle object creation expressions as receivers, and disambiguate same-name classes that differ by generic arity (e.g., Builder vs. Builder<TResult>). Corresponding unit tests have been added to verify these improvements. I have no feedback to provide as there are no review comments.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Greptile SummaryThis PR improves C# call recall for fluent APIs and generic type variants. The main changes are:
Confidence Score: 5/5This PR appears safe to merge with low risk. No blocking correctness or security issues were identified in the changed C# inference paths. The updated code includes focused tests for the main resolver cases changed by this PR. Previously reported arity-context issues appear addressed in the reviewed code. No files require special attention.
What T-Rex did
Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Ingest as C# ingestion
participant Indexes as Arity/return-type indexes
participant Resolver as Type inference resolver
participant Calls as CALLS edge emission
Ingest->>Indexes: Record class generic arity and method return types
Ingest->>Indexes: Index extension receiver type and arity
Resolver->>Resolver: Strip generic call-name type arguments
Resolver->>Indexes: Resolve object creation or inner invocation receiver
Indexes-->>Resolver: Return receiver type and generic arity
Resolver->>Calls: Emit matching instance or extension method edge
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Ingest as C# ingestion
participant Indexes as Arity/return-type indexes
participant Resolver as Type inference resolver
participant Calls as CALLS edge emission
Ingest->>Indexes: Record class generic arity and method return types
Ingest->>Indexes: Index extension receiver type and arity
Resolver->>Resolver: Strip generic call-name type arguments
Resolver->>Indexes: Resolve object creation or inner invocation receiver
Indexes-->>Resolver: Return receiver type and generic arity
Resolver->>Calls: Emit matching instance or extension method edge
Reviews (3): Last reviewed commit: "fix(csharp): keep caller context when re..." | Re-trigger Greptile |
|
@greptile review |
|
@greptile review |
… entry points resolve
…opping call edges
…corded return types
…ity for chained receivers and extensions
…er's return arity
ca59e6c to
dcb0846
Compare
|



What
Round 1 of the C# calls-recall campaign (follows the dead-code dogfood, #782/#784/#785/#786): three resolution gaps behind Polly's fluent-API surface. The retrieval eval gains 280 true call edges with zero new false positives: tp 3113 -> 3393, recall 0.7828 -> 0.8532, f1 0.8781 -> 0.9208, precision stays 1.0000. The dead-code report is unchanged at its single verified true positive.
Root causes and fixes
Policy.Handle<InvalidOperationException>()keeps its type arguments through both call-name extraction and the member resolver, while methods register generic-free, so Polly's whole fluent entry point emitted nothing. Both paths now strip the type-argument list.new Builder().Add()andPolicy.Handle<T>().Wrap(...)hand the resolver receiver nodes it had no branch for. An object-creation receiver IS its type; an invocation receiver types the next hop through the resolved inner call's recorded return type (newcsharp_method_return_types, recorded at both the member-ingest and#if-recovery paths from thereturnsfield, which is the C# grammar's return-type field; there is notypefield on methods). Mirrors the existing C++ chained-factory typing.ResiliencePipelineBuilderandResiliencePipelineBuilder<TResult>share a simple name, so the receiver-type sweep returned None (ambiguity) and the extension matcher's unqualified-ambiguity guard refused to bind. Class declarations now record their type-parameter count (csharp_class_generic_arity); the type sweep filters candidates by the reference's written arity, and the extension guard treats same-name declarations that all differ by generic arity as non-ambiguous (a compilable call binds the unique matching extension; trueN1.Widget/N2.Widgetnamespace splits, same arity, stay guarded).Validation
PolicyBuildertrue positive), zero new findings.Remaining recall tail (~584)
Dominated by two classes for follow-up rounds: BCL-name collisions in the eval reduction (
Cancel,Add,Dispose,ToStringon BCL receivers that share a simple name with first-party declarations; needs eval-semantics investigation before touching the graph) and deeper chains through field-typed or awaited receivers.