Skip to content

fix(csharp): recover arity-sibling base types dropped by the self-loop guard - #762

Merged
vitali87 merged 4 commits into
mainfrom
fix/csharp-arity-sibling-inherits
Jul 16, 2026
Merged

fix(csharp): recover arity-sibling base types dropped by the self-loop guard#762
vitali87 merged 4 commits into
mainfrom
fix/csharp-arity-sibling-inherits

Conversation

@vitali87

Copy link
Copy Markdown
Owner

Problem

C# overloads type names by generic arity: public sealed class PredicateBuilder : PredicateBuilder<object> declares a base that is a different type sharing the simple name. cgr's name resolution lands on the declaring type itself, and the deferred-inherits self-loop guard then drops the edge (dotted module-anchored remainder) or externalizes it. On Polly this silently lost 6 INHERITS edges (RetryStrategyOptions, CircuitBreakerStrategyOptions, PredicateBuilder, BrokenCircuitException<TResult>, ITtlStrategy, ExecuteParameters).

Fix

When a deferred C# base resolves to the child itself, recover the arity sibling before falling back:

  1. Package tier: a unique other type declaration (Class/Interface/Enum) with the same simple name under the module's parent package — the conventional Foo.cs + Foo.TResult.cs sibling layout.
  2. Project-wide tier: only when the package tier finds zero candidates (the pair can span projects: Polly's legacy BrokenCircuitException<TResult> : BrokenCircuitException inherits from Polly.Core), accept a project-wide unique declaration.

Ambiguity at either tier keeps the no-edge answer rather than guessing, and the recovery is gated to C# — only C# can legally name a different type with the declaring type's own simple name, so other languages keep the existing shadowed-name semantics (e.g. thrift's pub enum Error implementing std Error).

Validation

RED → GREEN in commit history: failing tests first for the same-directory pair (class and interface variants), then the cross-directory pair.

On Polly, INHERITS false negatives drop from 5 to 2 (tp 192 → 195, F1 0.9253 → 0.9330). The two remaining misses have distinct root causes, out of scope here:

  • PredicateBuilder<TResult> is partial across two files, so the package tier sees two candidates and correctly refuses to guess; the opt-in Roslyn hybrid frontend's partial-group merge is the semantic answer for that shape.
  • ExecuteParameters<T> : ExecuteParameters nests both types in one class, so their qualified names collide (the known duplicate-QN limitation; tracked separately).

Full suite: 5224 passed, 10 skipped.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for C# arity overloading resolution during class ingestion. Specifically, it adds a helper method _csharp_arity_sibling to recover generic sibling types when a base class resolves to the declaring type itself (e.g., class Foo : Foo<object>), preventing the self-loop guard from dropping the inheritance edge. Unit tests have also been added to cover these scenarios. Feedback on the PR points out potential issues with the package-level sibling matching logic when dealing with root modules (no namespace) or subpackages, and provides a robust suggestion to compare exact parent packages instead.

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.

Comment thread codebase_rag/parsers/class_ingest/mixin.py
@greptile-apps

greptile-apps Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR updates C# inheritance resolution for generic-arity sibling types. The main changes are:

  • Recover a unique same-name C# type declaration when deferred base resolution points back to the child type.
  • Prefer a unique sibling under the module parent package before falling back to a project-wide unique declaration.
  • Keep ambiguous matches as no-edge results instead of guessing.
  • Add tests for same-directory class and interface pairs, plus cross-directory sibling recovery.

Confidence Score: 5/5

Safe to merge with minimal risk.

The change is narrowly scoped to C# self-loop recovery, preserves existing fallback behavior for other languages and ambiguous matches, and adds focused tests for the new paths.

No files require special attention.

T-Rex T-Rex Logs

What T-Rex did

  • Attempted the narrow C# inheritance pytest validation for codebase_rag/tests/test_csharp_inheritance.py.
  • Dependency setup blocked before pytest collection because pymgclient==1.5.1 could not build without cmake.
  • Tool availability check showed uv was present, but cmake3 and cmake were unavailable.
  • Pytest environment activity shows uv created a virtual environment and dependency resolution started, then building pymgclient==1.5.1 failed due to missing suitable cmake, so validation remains inconclusive.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
codebase_rag/parsers/class_ingest/mixin.py Adds C#-only deferred inheritance recovery for arity-sibling base types when self-loop resolution would otherwise drop the edge.
codebase_rag/tests/test_csharp_inheritance.py Adds tests for same-directory class/interface arity siblings and cross-directory project-wide recovery.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Deferred C# base resolution] --> B{parent_qn == child_qn?}
B -- No --> C[Existing deferred parent resolution]
B -- Yes --> D{Language is C#?}
D -- No --> E[Existing self-loop fallback]
D -- Yes --> F[Find same-simple-name type declarations]
F --> G{Unique candidate under module parent package?}
G -- Yes --> H[Emit INHERITS to package sibling]
G -- Multiple --> I[Emit no edge]
G -- None --> J{Unique project-wide candidate?}
J -- Yes --> K[Emit INHERITS to project-wide sibling]
J -- No --> I
Loading
%%{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"}}}%%
flowchart TD
A[Deferred C# base resolution] --> B{parent_qn == child_qn?}
B -- No --> C[Existing deferred parent resolution]
B -- Yes --> D{Language is C#?}
D -- No --> E[Existing self-loop fallback]
D -- Yes --> F[Find same-simple-name type declarations]
F --> G{Unique candidate under module parent package?}
G -- Yes --> H[Emit INHERITS to package sibling]
G -- Multiple --> I[Emit no edge]
G -- None --> J{Unique project-wide candidate?}
J -- Yes --> K[Emit INHERITS to project-wide sibling]
J -- No --> I
Loading

Reviews (1): Last reviewed commit: "fix(csharp): fall back to a project-wide..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant