Skip to content

feat(go): infer structural interface implementations#1966

Merged
magyargergo merged 18 commits into
abhigyanpatwari:mainfrom
evander-wang:codex-go-structural-interface-dispatch
Jun 2, 2026
Merged

feat(go): infer structural interface implementations#1966
magyargergo merged 18 commits into
abhigyanpatwari:mainfrom
evander-wang:codex-go-structural-interface-dispatch

Conversation

@evander-wang

@evander-wang evander-wang commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds signature-aware structural interface implementation detection for Go and feeds the inferred relationships into the scope-resolution pipeline.

Go does not have explicit implements declarations, so the analyzer has to infer interface satisfaction from method sets. Before this change, the Go analyzer could only reason about direct receiver calls and did not have a reliable way to emit IMPLEMENTS edges for structurally satisfied interfaces. That meant interface-typed calls such as repo.Save(user) could not fan out through the graph to known concrete implementations.

The new flow is:

  1. Extract richer Go declaration metadata from tree-sitter captures.
  2. Populate Go method ownership across package files.
  3. Infer structural IMPLEMENTS edges from Go interface method sets.
  4. Emit those edges before MRO/interface dispatch is built.
  5. Let existing interface-dispatch and METHOD_IMPLEMENTS logic consume the inferred relationships.

What Changed

Structural interface implementation detection

  • Reworked detectGoInterfaceImplementations from method-name-only matching into method-set + signature matching.
  • Collects interface and struct methods by owner.
  • Requires interface methods to have verifiable signature metadata before emitting edges.
  • Compares parameter counts, required parameter counts, parameter types, and return types.
  • Preserves Go type shape during signature checks:
    • User is not equal to []User
    • []User is not equal to ...User
    • a.User is not equal to b.User
  • Emits inferred edges with reason go-structural-implements.

Embedded interface handling

  • Captures embedded interface references from Go interface declarations.
  • Recursively merges locally resolvable embedded interface method sets.
  • Allows embedded empty interfaces to contribute no required methods.
  • Suppresses structural detection when an embedded interface cannot be resolved locally, avoiding false positives for cases such as io.Reader.

Example:

type Reader interface {
    Read() error
}

type ReadCloser interface {
    Reader
    Close() error
}

ReadCloser now requires both Read() and Close(). A type with only Close() no longer gets a false IMPLEMENTS edge.

Pointer receiver method-set correctness

Go value and pointer method sets differ:

type I interface { M() }
type T struct{}
func (*T) M() {}

var _ I = &T{} // OK
var _ I = T{}  // compile error

This PR preserves the raw receiver shape for Go self bindings and records whether a method was declared on T or *T.

  • func (t T) M() contributes to the value type method set.
  • func (t *T) M() does not cause T to structurally implement the interface.
  • Regular receiver lookup still normalizes *T to T where appropriate so existing member-call resolution continues to work.

Return type and grouped return fixes

  • Return types are now checked strictly for interface implementation compatibility.
    • Close() error is not compatible with Close().
  • Grouped named returns are expanded correctly.
    • M() (a, b int) is treated as returning (int, int), not int.

Go capture and type metadata improvements

  • Captures Go interface method_elem declarations.
  • Adds arity/signature metadata for interface methods.
  • Preserves raw pointer receiver text in receiver bindings.
  • Adds concrete RHS inference for explicit interface variable declarations:
var repo Repository = SqlRepository{}
repo.Save(user)

This lets the resolver prefer the concrete local assignment over broad interface fan-out.

Scope-resolution pipeline integration

  • Adds a pipeline step that emits inferred interface implementations before MRO/interface dispatch runs.
  • Extends receiver-bound call owner lookup to include Go Struct definitions.
  • Registers Go structural implementation detection on the Go scope resolver.

Behavior Examples

Precise concrete dispatch

func precise(user User) {
    var repo Repository = SqlRepository{}
    repo.Save(user)
}

The analyzer resolves repo.Save(user) directly to SqlRepository.Save because the concrete RHS type is known.

Interface fan-out when runtime type is unknown

func fallback(repo Repository, user User) {
    repo.Save(user)
}

When the receiver is only known as Repository, the analyzer emits interface-dispatch calls to all statically known valid implementors.

Invalid implementors are rejected

type Repository interface {
    Save(user User) error
}

type BadRepository struct{}
func (b BadRepository) Save(id string) error { return nil }

BadRepository no longer receives an IMPLEMENTS edge because the parameter type does not match.

Tests

Added a dedicated Go fixture:

  • gitnexus/test/fixtures/lang-resolution/go-structural-interface-dispatch/repository.go

Added/updated coverage for:

  • signature-checked structural implementations
  • precise concrete assignment dispatch
  • interface fan-out dispatch
  • METHOD_IMPLEMENTS generation from inferred Go structural edges
  • embedded interfaces
  • unresolved embedded interfaces
  • embedded empty interfaces
  • package-qualified type signatures
  • slice vs scalar parameter types
  • variadic vs slice parameter types
  • missing return type rejection
  • grouped named return expansion
  • pointer-receiver-only method-set behavior
  • Go capture golden updates

Validation run:

npm test -- test/unit/scope-resolution/go --reporter verbose
npm test -- test/integration/resolvers/go.test.ts --reporter verbose
npx tsc --noEmit
git diff --check

Pre-commit also passed:

  • staged formatting
  • eslint --fix
  • prettier --write
  • pre-commit: typechecking gitnexus...

Notes

  • This PR intentionally does not try to prove a single runtime implementation for interface-typed receivers. If the static receiver type is only an interface and multiple valid implementors are known, multiple interface-dispatch edges are expected.
  • More precise caller-context narrowing can be added later as a separate improvement.

Refs #1927

@vercel

vercel Bot commented Jun 1, 2026

Copy link
Copy Markdown

@evander-wang is attempting to deploy a commit to the NexusCore Team on Vercel.

A member of the Team first needs to authorize it.

Comment thread gitnexus/test/integration/resolvers/go.test.ts Fixed
@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

CI Report

All checks passed

Pipeline Status

Stage Status Details
✅ Typecheck success tsc --noEmit
✅ Tests success unit tests, 3 platforms
✅ E2E success gitnexus-web changes only

Test Results

Tests Passed Failed Skipped Duration
10901 10889 0 12 684s

✅ All 10889 tests passed

12 test(s) skipped — expand for details
  • COBOL pipeline benchmark > scales with file count
  • C# pipeline benchmark > scales with file count — namespaces spread across the solution
  • C# pipeline benchmark > scales with file count — all types in one (global) namespace bucket
  • C# pipeline benchmark > scales with file count — all types in one (named) namespace bucket
  • Go pipeline benchmark > scales with file count (workers enabled)
  • Go pipeline benchmark — worker pool (issue Worker idle timeout kills long Go scope extraction and surfaces as Napi::Error during analyze #1848) > does not quarantine the large generated Go file on sub-batch idle timeout
  • Go structural interface detection benchmark > scales linearly with interface × struct count
  • PHP pipeline benchmark > scales with file count (workers enabled)
  • Ruby pipeline benchmark > scales with file count (workers enabled)
  • Rust pipeline benchmark > scales with file count (workers enabled)
  • run.cjs direct-exec entrypoint (fix(cli): steer docs, skills, and hooks through a CLI-neutral project-local runner (#1939) #1945) > resolves a .cmd shim via the Windows shell branch, passing args and exit code
  • buildTypeEnv > known limitations (documented skip tests) > Ruby block parameter: users.each { |user| } — closure param inference, different feature

Code Coverage

Tests

Metric Coverage Covered Base Delta Status
Statements 80.53% 37892/47050 79.84% 📈 +0.7 🟢 ████████████████░░░░
Branches 69.12% 24117/34887 68.5% 📈 +0.6 🟢 █████████████░░░░░░░
Functions 85.68% 3937/4595 84.94% 📈 +0.7 🟢 █████████████████░░░
Lines 84.11% 34089/40527 83.36% 📈 +0.8 🟢 ████████████████░░░░

📋 View full run · Generated by CI

@magyargergo

Copy link
Copy Markdown
Collaborator

Could you please resolve the conflict?

@magyargergo

magyargergo commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

@evander-wang Could we also write benchmarking for these changes? we need to ensure linear scaling. We already have an implementation in place for other languages.

Add signature-aware structural interface detection for Go and emit inferred IMPLEMENTS edges during scope resolution so interface-dispatch and METHOD_IMPLEMENTS can use them.

The detector now builds interface method sets, includes locally embedded interfaces, suppresses unresolved embedded interfaces, preserves Go type shape and package qualifiers, validates return types, expands grouped named returns, and avoids treating pointer-receiver-only methods as value-type implementations.

Also improve Go type metadata for interface method elements, receiver bindings, explicit interface variables with concrete composite literal RHS, multi-assignment/range inference, and struct owner lookup in receiver-bound calls.

Add Go structural interface dispatch fixtures and unit/integration coverage for precise assignment dispatch, interface fan-out, embedded interfaces, invalid signatures, grouped returns, and pointer receiver method-set semantics.
Add focused coverage for Go interface method return signature captures, cyclic embedded interfaces, and structs satisfying multiple unrelated interfaces.

Document why structural IMPLEMENTS edges are emitted before MRO/interface dispatch and why Go signature normalization preserves type shape and package qualifiers.
Qualify Go structural interface signature types with file package context and finalized namespace import aliases so interface methods using local package types match implementations that spell the same type as pkg.Type or an import alias.

Add a cross-package fixture that proves GoodStore satisfies api.Saver through aliased api.User signatures while WrongStore using other.User is rejected, and verifies interface dispatch only fans out to the valid implementor.
Build Go structural interface detection indexes once per pipeline run instead of repeatedly scanning parsed files and scopes for each interface.

Cache interface method sets, embedded interface sites, methods by owner, signature contexts, and method-name-to-struct candidates. Candidate structs are narrowed by the rarest required method name before signature compatibility checks, preserving output while reducing work on large Go repositories.
Cache collected Go interface method sets so shared embedded interfaces are not recomputed repeatedly during structural implementation detection.

Document why receiver self-bindings preserve pointer shape and assert that inferred structural IMPLEMENTS edges keep the expected confidence value.
Drop the unused nodeQualifiedName helper from the Go structural interface integration tests after the assertions were narrowed to edge sets and owner names.
The Go legacy DAG does not synthesize structural IMPLEMENTS or METHOD_IMPLEMENTS edges and cannot feed those inferred relationships into interface dispatch. Register the new structural interface integration cases as expected legacy parity failures so the scope-resolution parity job continues to validate the supported registry-primary path without requiring a legacy backport.
@magyargergo magyargergo linked an issue Jun 1, 2026 that may be closed by this pull request
5 tasks
@magyargergo

Copy link
Copy Markdown
Collaborator

Cross-ref against the Go audit sub-issue #1927 (parent #1919) — this PR looks like the right home for F30 and a meaningful slice of F31, but a few ticket items still look open after merge.

Already covered here (thanks):

  • F30method_elem capture + signature/return metadata → signature-aware structural IMPLEMENTS (the main gap called out in the audit).
  • F31 (partial) — embedded interface refs via @reference.inherits (type_identifier / qualified_type) + merged method sets for structural detection.

Still missing from #1927 — could we implement these in this PR (or follow up immediately if scope is already large)?

Finding Gap Suggested scope
F31 (remainder) Struct embedding heritage still limited to bare names; generic embed shapes called out in the audit Extend struct/interface @reference.inherits / embedding capture to qualified + generic embeds, with parity fixtures (complements #1956 struct-embedding worker-path work)
F33 Composite-literal / constructor refs miss generic_type instantiation Handle generic_type (and instantiations) in composite-literal RHS inference, not just type_identifier / qualified_type
F32 Multi-name var / const / field declarations only extract the first name in config-based extractors Fix extractors to emit all declared names (regression fixture with var a, b int / multi-field struct tags)
F34 Type-binding param annotation queries omit channel, array, func, interface, generic parameter shapes Widen Go @type-binding.parameter (or equivalent) query arms + interpretation so signature matching isn’t blind to those shapes

Happy to split F32/F34 into a tiny follow-up if you’d rather keep this PR focused on structural interface dispatch — but it’d be good to either land them here or open tracked follow-ups on #1927 so the audit boxes don’t stay unchecked by accident.

Let me know which of the above you prefer in-scope vs follow-up.

@evander-wang evander-wang force-pushed the codex-go-structural-interface-dispatch branch from d5eca84 to d39ae85 Compare June 1, 2026 16:28
magyargergo and others added 2 commits June 1, 2026 21:09
Add two benchmark suites for detectGoInterfaceImplementations:

1. Ungated tripwire (runs in CI): 50 interfaces × 50 structs,
   asserts completion within 5s budget and correct IMPLEMENTS edges.

2. Gated scaling benchmark (GITNEXUS_BENCH=1): measures at
   50×50, 200×200, and 800×800 pairs, asserts scaling ratio < 1.5.

Results: 0.96x and 1.16x scaling — confirms near-linear behavior
thanks to the structIdsByMethodName candidate index optimization.
@evander-wang

Copy link
Copy Markdown
Contributor Author

@evander-wang Could we also write benchmarking for these changes? we need to ensure linear scaling. We already have an implementation in place for other languages.

of course let me first look at the existing benchmark implementation

Go capture output changed due to structural interface detection
(method_elem, return-type, pointer receiver raw form, new container
types). Scaling remains linear (1.08x).
@evander-wang

Copy link
Copy Markdown
Contributor Author

Cross-ref against the Go audit sub-issue #1927 (parent #1919) — this PR looks like the right home for F30 and a meaningful slice of F31, but a few ticket items still look open after merge.

Already covered here (thanks):

  • F30method_elem capture + signature/return metadata → signature-aware structural IMPLEMENTS (the main gap called out in the audit).
  • F31 (partial) — embedded interface refs via @reference.inherits (type_identifier / qualified_type) + merged method sets for structural detection.

Still missing from #1927 — could we implement these in this PR (or follow up immediately if scope is already large)?

Finding Gap Suggested scope
F31 (remainder) Struct embedding heritage still limited to bare names; generic embed shapes called out in the audit Extend struct/interface @reference.inherits / embedding capture to qualified + generic embeds, with parity fixtures (complements #1956 struct-embedding worker-path work)
F33 Composite-literal / constructor refs miss generic_type instantiation Handle generic_type (and instantiations) in composite-literal RHS inference, not just type_identifier / qualified_type
F32 Multi-name var / const / field declarations only extract the first name in config-based extractors Fix extractors to emit all declared names (regression fixture with var a, b int / multi-field struct tags)
F34 Type-binding param annotation queries omit channel, array, func, interface, generic parameter shapes Widen Go @type-binding.parameter (or equivalent) query arms + interpretation so signature matching isn’t blind to those shapes
Happy to split F32/F34 into a tiny follow-up if you’d rather keep this PR focused on structural interface dispatch — but it’d be good to either land them here or open tracked follow-ups on #1927 so the audit boxes don’t stay unchecked by accident.

Let me know which of the above you prefer in-scope vs follow-up.

I'll include F31 (remainder) in this PR since it's closely tied to the structural interface dispatch work.

magyargergo and others added 4 commits June 2, 2026 05:17
Add signature-aware Go structural interface implementation inference to the scope-resolution pipeline. The detector now preserves Go type-shape and package-qualified signature checks, resolves embedded interface references through scope/import context, and uses promoted methods from embedded structs when building candidate method sets.

Keep the shared inheritance resolver path centralized so explicit heritage emission and Go structural detection consume the same binding logic. Remove the duplicate Go embedded-interface query now covered by synthesized inheritance references.

Cover pointer receiver exclusions, embedded interfaces, cross-package embedded interfaces, promoted embedded struct methods, direct method shadowing, ambiguous promoted methods, grouped returns, missing returns, and legacy expected-failure parity. Update Go capture golden data for the intentional capture changes.

@magyargergo magyargergo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Tri-method review — PR #1966 feat(go): infer structural interface implementations

Methods: GitNexus swarm + Compound-Engineering personas (both Claude) + Codex gpt-5.5/xhigh.
Engine-independence caveat: five of the six lanes are Claude (same base model) — only Codex is a genuinely independent engine, and its sandbox blocked code execution (static-analysis only). Cross-Claude agreement reduces individual noise but does not eliminate shared blind spots, so the coordinator independently verified every posted finding: the three soundness findings were reproduced end-to-end via runPipelineFromRepo under npx tsx in a worktree (real edge output, named receivers, with positive/negative controls), and the CI blocker was reproduced both on CI and via a local bench/scope-capture/measure.mjs --check. Lane status: 5 Claude lanes returned findings; 2 gitnexus lanes ran partial (their domains — shared-file blast radius, CI/test wiring — were verified directly by the coordinator).
Severity key: P1 = blocks merge or emits an incorrect edge that propagates downstream (call graph / dispatch); P2 = isolated false positive/negative; P3 = minor.

Bottom line: currently merge-blocked by CI — not ready to merge as-is

tests / benchmarks (GITNEXUS_BENCH) fails on a stale Go scope-capture baseline (→ CI Gate fails, mergeStateStatus: BLOCKED). That is a one-line re-baseline fix. Separately, three reproduced signature/embedding soundness corner cases remain — one P1 false-positive that pollutes the call graph, two P2s. No happy-path regressions; scope-parity, typecheck, and coverage all pass.

What's solid (credit where due)

The disabled naive method-name superset matcher is replaced by signature-aware structural matching (param counts/types, return types, package-qualified) with interface embedding, struct-embedding method promotion, recursion cycle guards, and rarest-method candidate pruning, wired into the generic pipeline behind a gated, dedup-aware hook (existing explicit IMPLEMENTS win; edges emitted before MRO). Test coverage is strong — exact edge-set assertions plus explicit negatives (wrong-signature, shadowed-method, missing-method, pointer-only). Validated suspicions that turned out fine: the pointer-receiver exclusion is a deliberate, test-codified value-method-set choice; range-binding.ts's +1 is a latent correctness fix (1-based scope-line convention), not a regression; the qualifiedName mutation is guarded against double-qualify; same-depth embedding ambiguity is handled correctly. (Codex + correctness verified these.)

Findings (inline; all reproduced by the coordinator)

  1. P1 (CI-verified) — stale Go scope-capture baseline blocks the benchmark gate → merge BLOCKED. (baselines.json)
  2. P1 — cross-package signature normalization emits a false IMPLEMENTS when an imported type's package can't be resolved. Strong: Codex + adversarial independently. (interface-impls.ts:481)
  3. P2 — struct-embedding promotion drops a method as "ambiguous" when it's unambiguous at the shallowest depth → false negative. Strong: Codex + correctness independently. (interface-impls.ts:251)
  4. P2 — variadic ...T element types are never package-qualified → cross-package false IMPLEMENTS. (interface-impls.ts:479)

Non-blocking

  • any vs interface{} treated as distinct → false negative for a common idiom.
  • Perf — required-side signature normalization is recomputed per candidate struct (regex per type); pre-normalize once. Bounded by candidate pruning except on ubiquitous method names (Stringer/Closer-like) where the candidate set collapses to all structs. cloneMethodSet allocates on every cache hit.
  • MaintainabilitygoReceiverKind is a structural-cast bolt-on (not on SymbolDefinition); extractCompositeLiteralTypeNode duplicates extractTypeNode; the emitted-edge count is dropped from run stats; packageQualifierForFile uses the directory path, not the Go module path.
  • Defensive — the undefined-metadata leniency is fail-open; flagged by Codex + 3 Claude lanes but unanimously unreachable today (arity-metadata co-populates count+types), so a fail-closed guard is hardening, not a bug fix.
  • Shared-file noteCLASS_CONTAINER_TYPES now matches Go struct_type/interface_type (consumed by call-processor / parsing-processor / type-env / parse-worker); Go-specific node names + scope-parity green → low risk.

Test gaps

  • The cross-package describe asserts IMPLEMENTS only with toContain/not.toContain — add an exact toEqual([...]) bound (would have caught finding 2 and the variadic finding 4).
  • No unit coverage for qualifyGoSignatureTypes with a real packageQualifier (variadic / func-typed / unresolved alias), goBindingScopeFor, the var_spec unary arm, or the "existing IMPLEMENTS wins" dedup.

CI at review time

format / lint / typecheck / typecheck-web, scope-parity / scope-resolution parity, and tests / ubuntu / coverage PASS; tests / benchmarks (GITNEXUS_BENCH) and CI Gate FAIL (the stale Go baseline, finding 1); Vercel fails on auth only (ignore). mergeStateStatus: BLOCKED.


Automated multi-tool digest (3 review methods; only Codex is a separate engine, and it was execution-sandboxed). Every inline finding was reproduced by the coordinator against the real pipeline. Verify before acting.

Comment thread gitnexus/bench/scope-capture/baselines.json Outdated
Comment thread gitnexus/src/core/ingestion/languages/go/interface-impls.ts Outdated
Comment thread gitnexus/src/core/ingestion/languages/go/interface-impls.ts Outdated
Comment thread gitnexus/src/core/ingestion/languages/go/interface-impls.ts Outdated
magyargergo and others added 2 commits June 2, 2026 08:13
Apply Go method promotion depth rules so shallow embedded methods win and same-depth promoted methods remain ambiguous.

Treat unresolved import-qualified signature types as unverifiable, preserve variadic element package identity, and tighten cross-package structural edge tests.

Update the Go scope-capture benchmark fingerprint for the structural interface capture changes.
@magyargergo magyargergo merged commit 0523193 into abhigyanpatwari:main Jun 2, 2026
29 of 30 checks passed
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.

Go: parsing-layer coverage gaps (5 findings)

3 participants