Skip to content

fix(cha): CHA dispatch omits the receiver's own instantiated type - #2494

Merged
carlos-alm merged 2 commits into
mainfrom
fix/issue-2348-zero-line-file-edges
Aug 14, 2026
Merged

fix(cha): CHA dispatch omits the receiver's own instantiated type#2494
carlos-alm merged 2 commits into
mainfrom
fix/issue-2348-zero-line-file-edges

Conversation

@carlos-alm

Copy link
Copy Markdown
Contributor

Summary

Test plan

  • New dual-engine regression test tests/integration/issue-2348-cha-base-type-own-method.test.ts — synthetic fixture mirroring the real repro shape (base class instantiated directly + two unrelated local subclass overrides in separate files); verified to fail on both engines with the fix disabled, pass with it enabled
  • Directly re-verified against the actual real-world repro (this repo's own tests/unit/in-memory-repository.test.ts/tests/integration/triage.test.ts): InMemoryRepository.findNodesForTriage now correctly appears as a target for both engines (additive alongside the pre-existing, harmless spurious edges)
  • cargo test --workspace: 1011 passed (was 1009 passed / 2 failed against the naive fix, before the strict-evidence refinement)
  • cargo fmt --check / cargo clippy --workspace --all-targets -- -D warnings: clean
  • Full test suite: 328 files / 5257 tests passed
  • npm run lint clean

Closes #2348

resolveChaTargets/resolve_cha_dispatch only walked implementors
(subclasses) starting from the receiver's declared type, never checking
whether that type itself is instantiated. When a class is instantiated
directly AND an unrelated file also declares a local subclass overriding
the same method (e.g. a test double), the base class's own method was
dropped from the resolved edge set while the unrelated subclass's
override leaked in instead (#2348).

Both engines now also resolve the receiver's own type via
resolveMethodViaAncestors/resolve_method_via_ancestors, gated on strict
new-expression evidence only (a new newExpressionTypes/
cha_new_expression_types set) rather than the merged instantiatedTypes/
cha_instantiated_types set (which also credits a bare high-confidence
type annotation as "instantiated"). The merged set is too broad for this
particular check: it would resurrect a distant interface's own bodyless
method whenever some unrelated concrete subclass overrides the same
method name, which regressed two existing native unit tests
(cha_typed_dispatch_fallback_resolves_distant_interface_implementation
and ..._respects_rta_filter) until this stricter gate was added.

Adds a dual-engine regression test
(issue-2348-cha-base-type-own-method.test.ts) reproducing the shape
with a synthetic base class + two unrelated local override subclasses,
verified to fail on both engines with the fix disabled and pass with it
enabled.

docs check acknowledged

Impact: 6 functions changed, 0 affected
@greptile-apps

greptile-apps Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This follow-up scopes receiver-own-type instantiation evidence by declaring file, preventing unrelated same-named local types from producing spurious CHA targets while preserving the intended base-type edge.

  • Adds file-scoped construction and declaration evidence to both TypeScript and Rust CHA contexts.
  • Uses scoped evidence for receiver-own-type dispatch when the caller file declares the receiver type.
  • Adds mirrored unit and integration coverage for native and WASM engines.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/domain/graph/builder/cha.ts Adds strict, file-scoped receiver instantiation evidence and uses it to disambiguate local same-named types during CHA dispatch.
crates/codegraph-core/src/domain/graph/builder/stages/build_edges.rs Mirrors the TypeScript CHA scoping behavior in the native Rust edge builder.
src/domain/graph/builder/stages/native-orchestrator.ts Initializes the expanded CHA context used by post-native this/super dispatch.
tests/integration/issue-2348-cha-base-type-own-method.test.ts Verifies both engines retain the instantiated base type’s own method as a call target.
tests/unit/cha.test.ts Covers same-named local-type disambiguation and the explicitly retained imported-type fallback.

Reviews (2): Last reviewed commit: "fix(cha): scope receiver-own-type RTA ev..." | Re-trigger Greptile

Comment thread src/domain/graph/builder/cha.ts
@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Codegraph Impact Analysis

5 functions changed22 callers affected across 8 files

  • collectInstantiatedTypes in src/domain/graph/builder/cha.ts:213 (3 transitive callers)
  • buildChaContext in src/domain/graph/builder/cha.ts:247 (3 transitive callers)
  • buildChaContextFromDb in src/domain/graph/builder/cha.ts:325 (12 transitive callers)
  • resolveChaTargets in src/domain/graph/builder/cha.ts:818 (9 transitive callers)
  • runPostNativeThisDispatch in src/domain/graph/builder/stages/native-orchestrator.ts:1461 (3 transitive callers)

Greptile review on PR #2494: newExpressionTypes/cha_new_expression_types
(added for #2348's receiver-own-type check) was a bare, project-wide
set with no file scoping, unlike implementorsByFile/cha_implementors_by_file
which this codebase already splits carefully for the same reason
(#2237). Two unrelated files can each declare their own unrelated class
with the same bare name; if only one is ever instantiated, the bare set
couldn't tell them apart, and resolveMethodViaAncestors/
resolve_method_via_ancestors' own bare/global fallback could then
resolve to the OTHER file's method.

Both engines now also track, per file: which type names it locally
declares (declaredTypeNamesByFile/cha_declared_type_names_by_file,
mirroring the same local-declaration anchor recordImplements/
recordExtends already use for implementorsByFile) and which type names
its own new-expression evidence names
(newExpressionTypesByFile/cha_new_expression_types_by_file). The
root-type check now prefers the file-scoped pair whenever the caller's
file locally declares the receiver type - trusting a scoped miss as
authoritative - and only falls back to the bare set when the caller's
file has no local declaration to anchor against (the same accepted
limitation implementorsByFile already has in that case).

Adds a dual-engine regression test proving the disambiguation (three
TS unit tests in cha.test.ts covering the collision, the same-file
positive case, and the no-anchor fallback; one Rust integration-style
unit test through build_call_edges), plus re-verifies the #2348
regression test and the real-world in-memory-repository.test.ts repro
are unaffected on both engines.

docs check acknowledged

Impact: 7 functions changed, 0 affected
@carlos-alm
carlos-alm merged commit 11475e7 into main Aug 14, 2026
36 checks passed
@carlos-alm
carlos-alm deleted the fix/issue-2348-zero-line-file-edges branch August 14, 2026 07:33
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 14, 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.

follow-up: odd zero-line "file calls itself" edges (module-level dispatch?) — WASM-only

1 participant