Skip to content

feat: Go structural interface matching and C# implements disambiguation (4.3)#522

Merged
carlos-alm merged 4 commits intomainfrom
feat/impl-tracking-4.3
Mar 20, 2026
Merged

feat: Go structural interface matching and C# implements disambiguation (4.3)#522
carlos-alm merged 4 commits intomainfrom
feat/impl-tracking-4.3

Conversation

@carlos-alm
Copy link
Copy Markdown
Contributor

Summary

Completes roadmap item 4.3 — Interface and Trait Implementation Tracking by filling the two remaining extractor-level gaps. The query layer, CLI commands (implementations, interfaces), MCP tools, impact analysis integration (bfsTransitiveCallers with includeImplementors), presentation formatting, and integration tests were already in place.

Go extractor — structural interface matching:

  • Post-walk pass compares struct method sets against interface method sets within the same file
  • Pushes { name, implements, line } entries to the classes array
  • Empty interfaces are excluded (they'd match everything)

C# extractor — implements disambiguation:

  • Fixed extractCSharpBaseTypes: childForFieldName('bases') returns null in tree-sitter-c-sharp; added findChild(node, 'base_list') fallback
  • Added reclassifyCSharpImplements post-walk pass that reclassifies extends entries as implements when the target is a known same-file interface definition

Type definitions:

  • Added ImplementationsResult, InterfacesResult to src/types.ts
  • Added findImplementors, findInterfaces to Repository interface
  • Added implementations, interfaces to McpDefaults

Test plan

  • 4 new Go parser tests: structural matching, missing methods, multiple interfaces, empty interfaces
  • 3 new C# parser tests: same-file interface reclassification, cross-file extends preserved, mixed extends+implements
  • C# engine parity test skipped (known native gap: native engine doesn't extract base_list)
  • All 13 integration tests pass (implementationsData, interfacesData, contextData, fnImpactData)
  • Full test suite: 2006 passed, 7 skipped, 0 failed

@claude
Copy link
Copy Markdown

claude bot commented Mar 19, 2026

Claude finished @carlos-alm's task —— View job


PR Review Complete ✅

Reviewing PR #522: Go structural interface matching and C# implements disambiguation (4.3)

Review Checklist

  • Phase 1: Root cause analysis - understand the problem being solved
  • Phase 2: Configuration change detection (high scrutiny)
  • Phase 3: Test integrity verification
  • Phase 4: Code quality review
  • Phase 5: Backlog compliance assessment
  • Final assessment and recommendation

Root Cause Analysis

Problem Being Solved: This PR completes roadmap item 4.3 by filling two extractor-level gaps in interface/trait implementation tracking. The CLI commands, MCP tools, and impact analysis integration were already implemented, but Go structural interface matching and C# implements disambiguation were missing at the extractor level.

Why This Approach: The implementation correctly addresses language-specific challenges:

  • Go uses structural typing where interfaces are satisfied by method set matching (duck typing)
  • C# tree-sitter grammar doesn't distinguish interfaces from classes in base_list, requiring post-processing

Risk Assessment: Low risk. This is additive functionality with comprehensive test coverage, no breaking changes, and proper error handling.

Backlog Compliance

  • Zero-dep: ✓ — No new runtime dependencies, builds on existing tree-sitter infrastructure
  • Foundation-aligned: ✓ — Pure local analysis, no cloud calls, maintains zero-infrastructure goal
  • Problem-fit: 4 — Directly improves AI agent accuracy by tracking implementation relationships, prevents hallucinated dependencies
  • Breaking: No — Purely additive, existing APIs unchanged
  • Tier: 1 (zero-dep ✓ + foundation-aligned ✓)

Technical Review

Go Extractor (src/extractors/go.js) - EXCELLENT

Lines 365-416: matchGoStructuralInterfaces

  • Correct implementation: Properly implements Go's structural typing by comparing method sets
  • Performance: O(n²) complexity is acceptable for same-file matching
  • Edge case handling: Correctly excludes empty interfaces (line 407: ifaceMethods.size > 0)
  • Data structure design: Uses Set for efficient method lookup and Map for grouping

C# Extractor (src/extractors/csharp.js) - SOLID

Lines 393-408: extractCSharpBaseTypes

  • Grammar adaptation: Correctly handles tree-sitter-c-sharp's base_list node structure with fallback
  • Dual approach: childForFieldName('bases') primary, findChild(node, 'base_list') fallback

Lines 380-391: reclassifyCSharpImplements

  • Post-processing pattern: Clean separation of concerns - extract first, disambiguate later
  • Safe mutation: Only reclassifies extends entries when target is a known same-file interface

Type Definitions (src/types.ts) - COMPREHENSIVE

Lines 1500-1522: New result types properly integrated into existing type system with pagination support.

Test Coverage - ROBUST

Go Tests (4 new tests)

  • Positive case: Structural matching with all methods present
  • Negative case: Missing methods correctly excluded
  • Multiple interfaces: One struct implementing multiple interfaces
  • Empty interfaces: Correctly excluded (prevents everything matching interface{})

C# Tests (3 new tests)

  • Same-file reclassification: extendsimplements for interfaces
  • Cross-file preservation: extends preserved when target not in same file
  • Mixed inheritance: Both extends and implements coexist correctly

Engine Parity

  • Documented gap: C# test properly skipped with clear reasoning (native engine doesn't extract base_list)

Configuration Changes

No configuration changes detected - No linting rules relaxed, no test thresholds lowered.

Integration Impact

Proper integration: Changes wire through existing infrastructure:

  • Database layer already supports implements edges
  • MCP tools (implementations, interfaces) already exist
  • CLI commands already implemented
  • Impact analysis already includes includeImplementors

Critical Concerns

None identified. This is a well-engineered, focused implementation.

Minor Observations

  • Code follows existing patterns and conventions consistently
  • Error handling is appropriate for the context
  • Performance characteristics are reasonable for file-local analysis
  • Documentation and comments are clear and helpful

Final Recommendation

  • Rating: ⭐⭐⭐⭐☆ (4/5)
  • Action: APPROVE
  • Reasoning: High-quality implementation that properly completes the roadmap item with comprehensive tests, no breaking changes, and excellent technical design. This directly improves AI agent accuracy by providing complete interface implementation tracking.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 19, 2026

Greptile Summary

This PR completes roadmap item 4.3 by filling the two remaining extractor-level gaps for Go structural interface matching and C# implements disambiguation. Both extractors implement clean post-walk passes that layer on top of the existing walk without disrupting it, and the integration and parser test suites continue to pass in full.

Go structural matching (matchGoStructuralInterfaces)

  • Correctly leverages the existing InterfaceName.MethodName convention already established by handleGoTypeDecl for method_elem nodes, and the ReceiverType.MethodName convention from handleGoMethodDecl (with the * pointer-receiver strip), so the method-set comparison is wired up correctly.
  • Empty-interface guard (ifaceMethods.size > 0) prevents the "matches everything" false-positive.
  • Both previously-discussed limitations — name-only (no signature) matching and embedded/composite interface exclusion — are now documented inline with clear guidance to treat results as candidate matches.

C# base-type disambiguation (reclassifyCSharpImplements + base_list fix)

  • findChild(node, 'base_list') fallback correctly reaches the base_list node that childForFieldName('bases') misses in the current tree-sitter-c-sharp grammar; the now-redundant nested base_list branch is cleanly removed.
  • The reclassify pass runs after the full walk so every in-file interface definition is present in ctx.definitions, and it only reclassifies entries pointing at a known same-file interface — cross-file base classes are conservatively left as extends.

Minor observations

  • The skipped C# parity test has an appropriate TODO comment and is tracked for a future issue. No code-level mechanism enforces re-enablement, but this was acknowledged in a prior thread.
  • No test covers generic interface reclassification (e.g. class Foo : IList<int> where IList<T> is defined in the same file), though the code handles this correctly since extractCSharpBaseTypes already strips generic args before storing the base name.

Confidence Score: 4/5

  • Safe to merge — both extractor passes are additive, tests are comprehensive, and all previously raised concerns have been addressed with appropriate documentation.
  • The implementation is correct and well-tested (4 Go + 3 C# new parser tests, all 13 integration tests pass). The only points that prevent a 5 are: the C# parity test is skipped with no automated re-enable path, and the Go matching produces only candidate (name-only) matches that could silently mislead consumers who don't read the JSDoc. Neither is a blocking issue for the current scope.
  • No files require special attention for merging; the skipped parity test in tests/engines/parity.test.js should be tracked externally until the native engine extracts base_list.

Important Files Changed

Filename Overview
src/extractors/go.js Adds matchGoStructuralInterfaces post-walk pass for file-local structural interface matching; correctly strips pointer receiver asterisks, skips empty interfaces, and documents name-only and embedded-interface limitations. No functional bugs found.
src/extractors/csharp.js Adds reclassifyCSharpImplements post-walk pass and fixes base_list lookup via findChild fallback. The nested base_list handling in the old code is correctly superseded by directly fetching the node. Reclassification logic correctly mutates only same-file interface targets.
src/types.ts Adds ImplementationsResult, InterfacesResult result types, extends Repository with findImplementors/findInterfaces, and adds implementations/interfaces to McpDefaults. Types are structurally consistent with the rest of the codebase.
tests/parsers/go.test.js Adds 4 well-scoped tests: structural match, missing method (negative), multiple interfaces, and empty interface exclusion. Covers the main branches of matchGoStructuralInterfaces.
tests/parsers/csharp.test.js Adds 3 tests covering same-file interface reclassification, cross-file extends preservation, and mixed extends+implements. Well-targeted.
tests/engines/parity.test.js Adds skip: true with a TODO comment for the C# base_list parity test. Documented gap is acceptable, but the skip removes a safety net for the native engine with no automated re-enablement trigger.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    subgraph Go Extractor
        A[walkGoNode] --> B[handleGoTypeDecl]
        B -->|struct| C[ctx.definitions: kind=struct]
        B -->|interface| D[ctx.definitions: kind=interface\n+ interface methods as kind=method]
        E[handleGoMethodDecl] -->|strips * from pointer receiver| F[ctx.definitions: kind=method\nname=ReceiverType.MethodName]
        A --> E
        G[matchGoStructuralInterfaces] -->|collects interface methods| H{ifaceMethods.size > 0?}
        H -->|yes| I{struct methods ⊇ interface methods?}
        I -->|yes - name-only match| J[ctx.classes.push\nname=struct, implements=iface]
        I -->|no| K[skip]
        H -->|no - empty interface| K
        C --> G
        D --> G
        F --> G
    end

    subgraph C# Extractor
        L[walkCSharpNode] --> M[handleCsClassDecl\nhandleCsStructDecl\nhandleCsRecordDecl]
        M --> N[extractCSharpBaseTypes]
        N -->|childForFieldName bases OR\nfindChild base_list| O{base_list found?}
        O -->|yes| P[ctx.classes.push\nname=class, extends=baseName]
        O -->|no| Q[return]
        L --> R[handleCsInterfaceDecl]
        R --> S[ctx.definitions: kind=interface]
        T[reclassifyCSharpImplements] -->|builds interfaceNames from ctx.definitions| U{cls.extends in interfaceNames?}
        U -->|yes - same-file interface| V[mutate: cls.implements = cls.extends\ndelete cls.extends]
        U -->|no - cross-file or base class| W[keep as extends]
        P --> T
        S --> T
    end
Loading

Last reviewed commit: "fix: add TODO tracki..."

Comment on lines +405 to +414
for (const [structName, methods] of structMethods) {
for (const [ifaceName, ifaceMethods] of interfaceMethods) {
if (ifaceMethods.size > 0 && [...ifaceMethods].every((m) => methods.has(m))) {
ctx.classes.push({
name: structName,
implements: ifaceName,
line: structLines.get(structName) || 1,
});
}
}
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.

P2 Method-name-only matching produces false positives

The structural matching only compares method names — not signatures. In Go, a method set is defined by both the method name and its signature, so this can generate incorrect implements edges. For example, if a struct has Write(string) error and an interface requires Write([]byte) (int, error), the code will report the struct as an implementor even though the signatures differ.

For the file-local use case this is a pragmatic trade-off, but it's worth an explicit note in the JSDoc that the check is name-only (not signature-verified), so downstream consumers of the implements edges know to treat them as candidates rather than definitive matches.

// Match: struct satisfies interface if it has all interface methods (name-only;
// signatures are not verified — use this as a candidate match, not a definitive one)
for (const [structName, methods] of structMethods) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed — added comment noting that matching is name-only (no signature verification) and that results should be treated as candidate matches, not definitive.

Comment on lines +388 to +401
for (const def of ctx.definitions) {
if (def.kind !== 'method' || !def.name.includes('.')) continue;
const dotIdx = def.name.indexOf('.');
const receiver = def.name.slice(0, dotIdx);
const method = def.name.slice(dotIdx + 1);

if (interfaceNames.has(receiver)) {
if (!interfaceMethods.has(receiver)) interfaceMethods.set(receiver, new Set());
interfaceMethods.get(receiver).add(method);
}
if (structNames.has(receiver)) {
if (!structMethods.has(receiver)) structMethods.set(receiver, new Set());
structMethods.get(receiver).add(method);
}
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.

P1 Embedded interfaces in Go are silently skipped

When a Go interface embeds another interface (e.g. type ReadWriter interface { Reader; Writer }), tree-sitter represents the embedded names as type_elem nodes, not method_elem nodes. The extractor only collects method_elem children for interface methods (see handleGoTypeDecl), so embedded interface names are never added to interfaceMethods.

As a result, ReadWriter would end up with ifaceMethods.size === 0 and be treated like an empty interface — skipped silently. A struct that implements Read() and Write() would not be matched to ReadWriter, even though it should be.

This is an edge case that could confuse users of the implementations command for composite interfaces. Consider adding a note in the JSDoc or a TODO tracking this gap.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed — added inline note documenting the embedded interface (type_elem) limitation. Composite interfaces like ReadWriter will be silently excluded from matching until this is addressed.

Comment on lines 228 to +230
file: 'Test.cs',
// Known native gap: native engine doesn't extract base_list into classes array
skip: true,
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.

P2 Skipped parity test removes the native-engine safety net

Marking this test skip: true is documented as a known gap (native engine doesn't extract base_list into classes array), which is a fair explanation. However, there's no mechanism to surface when the native engine eventually fixes this gap — the skip will silently remain even after the Rust engine is updated.

Consider replacing the bare skip with a tracked comment or a lightweight assertion so the test can be re-enabled without forgetting:

Suggested change
file: 'Test.cs',
// Known native gap: native engine doesn't extract base_list into classes array
skip: true,
// TODO(#NNN): re-enable once the native engine extracts base_list
// (track at https://...)
skip: true,

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Acknowledged — the skip already has a descriptive comment explaining the gap. A tracking issue will be filed separately for the native engine base_list extraction.

…on (4.3)

Complete the extractor-level gaps in interface/trait implementation tracking.
The query layer, CLI commands, MCP tools, impact analysis integration, and
presentation were already in place — this fills the two remaining extractor gaps.

Go extractor: add post-walk structural matching that compares struct method
sets against interface method sets within the same file, pushing implements
entries to the classes array. Empty interfaces are excluded.

C# extractor: fix extractCSharpBaseTypes to find base_list nodes (was using
childForFieldName('bases') which returns null in tree-sitter-c-sharp). Add
reclassifyCSharpImplements post-walk pass that reclassifies extends entries
as implements when the target is a known same-file interface definition.

Type definitions: add ImplementationsResult, InterfacesResult, and
Repository.findImplementors/findInterfaces to src/types.ts.

Tests: 8 new parser tests (Go structural matching, C# implements
disambiguation). Skip C# engine parity case (known native gap: native
engine does not extract base_list).

Impact: 15 functions changed, 6 affected
@carlos-alm carlos-alm force-pushed the feat/impl-tracking-4.3 branch from b3da2d9 to 51d99b3 Compare March 19, 2026 08:56
@carlos-alm
Copy link
Copy Markdown
Contributor Author

@greptileai

@carlos-alm
Copy link
Copy Markdown
Contributor Author

@greptileai

@carlos-alm carlos-alm merged commit df9d4bb into main Mar 20, 2026
15 checks passed
@carlos-alm carlos-alm deleted the feat/impl-tracking-4.3 branch March 20, 2026 10:02
@github-actions github-actions bot locked and limited conversation to collaborators Mar 20, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant