Skip to content

fix(java): resolve static calls made through the class name - #806

Open
kunalgoesbyken wants to merge 1 commit into
trailhq:mainfrom
kunalgoesbyken:fix/java-static-class-calls-dropped
Open

kunalgoesbyken wants to merge 1 commit into
trailhq:mainfrom
kunalgoesbyken:fix/java-static-class-calls-dropped

Conversation

@kunalgoesbyken

Copy link
Copy Markdown
Contributor

Summary

Strings.trim(s) names the class as the receiver, but a Java receiver with no local binding got no type, so the call was dropped and callers Strings.trim found nothing. Python and Swift already read an UpperCamelCase receiver with no binding as the type; this does the same for Java.

Changes

  • bindings.ts: resolveRecvType returns an UpperCamelCase Java receiver as its own type when no binding matched. A typed parameter, field or local of that name is found first and wins. A constant (LOG.info()) or a JDK type (Math.max, List.of()) owns no in-repo method of that name, so it finds no owner-qualified match and still drops.
  • extract.ts, resolve.ts: a receiver that is a single-type import carries the import's name as the call's specifier, and resolveEdges drops the call when that name is not a file in the repo. Without this, import com.google.common.base.Strings; Strings.trim(s) linked to an in-repo Strings. Wildcard and static imports are not recorded.
$ graft callers Strings.trim      # before
trim · method · com/acme/util/Strings.java:L4-L6
  no indexed callers

$ graft callers Strings.trim      # after
trim · method · com/acme/util/Strings.java:L4-L6
  calls ← run (com/acme/Main.java:L6-L8)
      7: return Strings.trim(s);

Testing

On Windows 11 with Node 26:

  • test/graph-java.test.ts: the first test covers a static call resolving to its class, two classes with the same method name resolving to their own, and a constant, a JDK type and a parameter named like the class. The second covers an in-repo Strings imported by name resolving, and the same call under an external import of that name dropping. Both fail on main.
  • test/graph*, blast*, ask*, generic*, depth*, resolve* and covers* suites: 674 tests, 1 fail. The one failure is discoverWorkspaceChildren follows a symlinked child repo, a symlink permission error on Windows that fails the same way on main.
  • tsc --noEmit passes.

Fixes #805

`Strings.trim(s)` names the class as the receiver, but a Java receiver
with no local binding gave no type, so the call was dropped and
`callers Strings.trim` found nothing. Instance, field and implicit-this
calls resolved; the static utility calls that carry most of a Java
codebase's shared code did not. Python and Swift already read an
UpperCamelCase receiver with no binding as the type; do the same here.

A constant (`LOG.info()`) or JDK type (`Math.max`) owns no in-repo method
of that name and still drops, and a typed parameter or field wins over the
class name. A receiver imported by name from a class outside the repo
(`import com.google.common.base.Strings`) drops too, even when the repo has
its own `Strings`.
@trailhq-graft

trailhq-graft Bot commented Nov 10, 2026 •

Copy link
Copy Markdown

🌱 graft blast radius

1 area changed → 4 areas can be affected. 8 dependent symbols, depth 2.
Tests: Dependency Resolution has tests the diff did not touch.
Tag: @anirudhkumar-nanonets — 4 of 5 areas · @shhdwi — 3 of 5 areas · @Faeeth — Symbol Overlap Detection

flowchart TB
  A0(("Dependency Graph Building<br/>5 symbols"))
  A1(("Graph Engine<br/>1 symbol"))
  A2(("Pull Request Review<br/>1 symbol"))
  A3(("Symbol Overlap Detection<br/>1 symbol"))
  classDef reached fill:#D9EDF3,stroke:#3AA7C9,stroke-width:1.5px,color:#0E313C;
  class A0,A1,A2,A3 reached;
Loading
Can be affected Symbols Nearest hop Reached from
Dependency Graph Building 5 src/graph/build.ts:L217-L492 buildGraph — calls, depth 1 Dependency Resolution
Graph Engine 1 src/engine.ts:L104-L115 graph — calls, depth 2 Dependency Resolution
Pull Request Review 1 src/app/review.ts:L45-L99 reviewPullRequest — calls, depth 2 Dependency Resolution
Symbol Overlap Detection 1 src/notes/edit-overlap.ts:L79-L94 symbolsIn — calls, depth 2 Dependency Resolution
Who knows this code — 3 people across 5 areas
Area Who knows it
Dependency Resolution · changed @shhdwi — 16 commits, last 13d ago · @anirudhkumar-nanonets — 10 commits, last 2d ago
Dependency Graph Building · affected @anirudhkumar-nanonets — 22 commits, last 7d ago · @shhdwi — 9 commits, last 13d ago
Graph Engine · affected @anirudhkumar-nanonets — 17 commits, last 27d ago
Pull Request Review · affected @anirudhkumar-nanonets — 3 commits, last 2mo ago
Symbol Overlap Detection · affected @Faeeth — 1 commit, last 2d ago · @shhdwi — 1 commit, last 18d ago

Ownership is git history over each area's own files, weighted towards recent work (120-day half-life). Merge commits and bots are dropped, and you are dropped from your own PR. A name with no @ has no GitHub handle in its commit email — tag them by hand, or add a .mailmap entry. A suggestion from history, not a CODEOWNERS rule.

All 8 dependent symbols, grouped by area

Dependency Graph Building — 5 symbols in 5 files

  • src/graph/build.ts:L217-L492 — buildGraph (calls, depth 1)
    368: const edges = resolveEdges(nodes, rawEdges, { goModules: readGoModules(root, repoFiles) });
  • src/graph/check.ts:L59-L159 — checkGraph (calls, depth 2)
  • src/graph/container.ts:L204-L269 — extractContainer (calls, depth 2)
  • src/graph/refresh-run.ts:L18-L21 — main (calls, depth 2)
  • src/graph/refresh.ts:L202-L225 — rebuild (calls, depth 2)

Graph Engine — 1 symbol in 1 file

  • src/engine.ts:L104-L115 — graph (calls, depth 2)

Pull Request Review — 1 symbol in 1 file

  • src/app/review.ts:L45-L99 — reviewPullRequest (calls, depth 2)

Symbol Overlap Detection — 1 symbol in 1 file

  • src/notes/edit-overlap.ts:L79-L94 — symbolsIn (calls, depth 2)
Test signal per changed area — 1 ⚠

Reached = a node under a test path has a resolved edge into the changed symbol. It undercounts anything called indirectly — through a CLI, a spawned process or a dynamic import — so read a low ratio as “look here”, never as a coverage gate.

  • ⚠ Dependency Resolution — 1 of 5 reached · 4 test files reach it, none changed here
    • not reached: resolveRecvType, extractTree, walk, collectJavaImports
54 test suites also reference this code

64 symbols, kept out of the diagram and the table so they cannot crowd out the areas a reviewer has to look at.

  • test/ask-index.test.ts
  • test/ask.test.ts
  • test/claude-hooks.test.ts
  • test/claude-prompt-private.test.ts
  • test/container-extract.test.ts
  • test/context-only-dir.test.ts
  • test/context.test.ts
  • test/covers.test.ts
  • test/depth-grammars.test.ts
  • test/depth-parity.ts
  • test/generic-extract.test.ts
  • test/generic-node24-probe.ts
  • test/graph-bash.test.ts
  • test/graph-bindings.test.ts
  • test/graph-cross-language.test.ts
  • test/graph-elixir.test.ts
  • test/graph-extensions.test.ts
  • test/graph-extract-dedup.test.ts
  • test/graph-go.test.ts
  • test/graph-incremental.test.ts
  • …34 more

graft blast · origin/main...HEAD · depth 2 · 4 changed files

Open the interactive graph → — click an area to see its dependent symbols at file:line.

github-actions Bot added a commit that referenced this pull request Nov 10, 2026
@kunalgoesbyken

Copy link
Copy Markdown
Contributor Author

The only red check is test (windows-latest), failing in "right after init, the paths it recorded read clean", which this change does not touch. The same job passed on the sibling PRs. Could someone with access re-run it? Thanks.

This branch has not been deployed

No deployments
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.

Java: static calls through the class name (Strings.trim(s)) produce no call edge

1 participant