Skip to content

fix(spring): scope DI resolver to Java/Kotlin and skip extends/implements references - #1827

Open
htarnacki wants to merge 1 commit into
colbymchenry:mainfrom
htarnacki:fix/spring-inheritance-hijack
Open

fix(spring): scope DI resolver to Java/Kotlin and skip extends/implements references#1827
htarnacki wants to merge 1 commit into
colbymchenry:mainfrom
htarnacki:fix/spring-inheritance-hijack

Conversation

@htarnacki

Copy link
Copy Markdown

Fixes #1825

Problem

Once Spring is detected (a single sibling Java module with spring-boot in its pom.xml is enough in a polyglot repo), springResolver.resolve() evaluated every unresolved reference regardless of language or reference kind. Pattern 4 (Entity/Model) matches any bare capitalized name and falls back to any same-named class in the graph, so Scala extends X references were hijacked to unrelated Java classes (in practice: test fixtures under /test/.../model/) with resolvedBy: 'framework', bypassing import/name matching.

Changes

  • frameworks/java.ts: two gates in front of the DI/convention patterns (1–5), leaving the Spring config-key resolution above them untouched (it already checks language):
    if (ref.language !== 'java' && ref.language !== 'kotlin') return null;
    if (ref.referenceKind === 'extends' || ref.referenceKind === 'implements') return null;
    Spring DI conventions do not apply to non-Java/Kotlin code, and inheritance is never a DI point — extends/implements must resolve via imports/name matching in every language.
  • Tests (frameworks.test.ts): Scala extends ref and Scala plain ref do not resolve through the Spring resolver; Java extends ref does not either; a Java DI reference still resolves through Pattern 4.
  • CHANGELOG: entry under [Unreleased].

Verification

On a production Scala codebase with sibling Java modules (~900k nodes), Scala extends/implements edges resolved by the framework resolver dropped from 5894 to 633 — the remaining ones are legitimate Java/Kotlin resolutions. Combined with #1824, codegraph impact on a widely-mixed-in trait went from 28 to 186 affected symbols.

Full test suite run with Node 24 (FTS5): green (one timing-sensitive test, sync-rebuild-convergence, is flaky under load on main as well and passes in isolation).

…ce refs

In a polyglot repo the Spring resolver is detected globally (a sibling
Java module with spring-boot in its pom is enough), and its resolve()
then ran on references from every language. Pattern 4's bare-name
fallback ('any [A-Z][a-zA-Z]+ name, prefer /model/ dirs, else ANY
same-named class') could therefore hijack a Scala 'extends ExtCustomer'
to an unrelated same-named test class, corrupting the inheritance graph
that impact analysis walks.

Two gates in front of the DI/convention patterns (1-5):
- skip refs whose language is not java/kotlin - Spring DI conventions
  say nothing about other languages
- skip extends/implements refs entirely - inheriting from a class is
  never a DI injection point; those must resolve via imports/name
  matching

The Spring config-key resolution above the gates is untouched (it
already gates itself). Regression tests cover the hijack case and the
still-working Java DI path.
@htarnacki
htarnacki force-pushed the fix/spring-inheritance-hijack branch from edfdfab to 2668d5c Compare September 10, 2026 07:09
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.

Spring resolver hijacks Scala extends references via bare-name Pattern 4 — inheritance edges point at unrelated test classes

1 participant