Repository navigation
fix(java): resolve receivers on their declared type, leave library types unguessed - #1942
danusha2345 wants to merge 1 commit into
Conversation
35a787b to
0b4bd03
Compare
|
Rebased onto main after the resolution wave. 7 of this PR's 12 cases now pass on main (#2126, #2166, #2169, #2171, #2177), so their code and tests are dropped: the local-variable regex (main's version with type arguments and for-each is kept) and
🤖 Generated with Claude Code |
5a22e2a to
80b7d4e
Compare
80b7d4e to
be018ef
Compare
…pes unguessed
Receivers the field lookup missed are typed: `Outer.this.f`, and a field
of an outer class read inside an inner or anonymous class (the lookup
only read the tightest enclosing class, so `mStore.save()` inside a
`new Runnable() {…}` went to a name-only guess). A chain of fields
(`owner.ctx.getResources()`) is read hop by hop, and a hop whose type is
a library class gets no edge instead of a same-named project method.
A type is read with the package or outer class written in source and
with the file's imports, so `import java.util.Iterator` is not taken for
a same-named type nested in another package of the project, and an
imported nested type (`import p.Http.Cookie`) is the one the import
names. Among same-named candidates no import pins, a Java call prefers
the Java declaration over a Scala or Kotlin one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@danusha2345, one more receiver form this PR doesn't cover yet, in case it fits its scope: a Java // src/com/app/Repo.java
package com.app;
public class Repo { public void query(String sql) {} }// src/com/app/Svc.java
package com.app;
import org.springframework.jdbc.core.JdbcTemplate;
public class Svc {
private JdbcTemplate jdbc;
JdbcTemplate getJdbc() { return jdbc; }
public void explicitType() { JdbcTemplate t = jdbc; t.query("x"); }
public void varFromField() { var t = jdbc; t.query("x"); }
public void varFromParam(JdbcTemplate p) { var t = p; t.query("x"); }
public void varFromCall() { var t = getJdbc(); t.query("x"); }
public void varFromNew() { var t = new JdbcTemplate(); t.query("x"); }
}
The Java patterns in If you'd rather keep this PR's scope as it is, I'm happy to send it as a follow-up once this lands. |
be018ef to
97d91cf
Compare
Problem
Java already types a call receiver from a local declaration or a plain field. Two gaps remain.
parcel.readInt()on anandroid.os.Parcel→ a projectVersionedParcel.readInt;mHandler.post(...)orlist.size()→ any projectpost/size;Log.e(...)→ a projectFalsingLog.e.this.fandOuter.this.f;Map<String, Foo> byName);this.mOwner.mRepo.save()).On decompiled Android SystemUI (3,421 Java files),
mainhas about 21.8k call edges whose receiver's declared type is a library class but that land on a project method. Another ~2.9k have a project-class receiver but go to an unrelated class. In playframework's Java sources (790 files) the numbers are ~1.7k and 125.Fix
All changes are in
src/resolution/name-matcher.ts. The extractor already emits the full receiver text, so the kernel and extraction version are unchanged.A declared type is read for every receiver shape above. A chain is typed one hop at a time, and each hop's field type is read with its own file's imports.
A written qualifier is kept:
Map.Entryandplay.api.mvc.BodyParser<B>stay as written, andimport p.Http.Cookiepins that exact nested class.When the declared type is a library class, the call gets no edge. A type counts as a library class when:
import java.util.Iteratornext to a projectUByteArray.Iterator);A short all-caps name (
T,VH) is taken for a type parameter and left alone.A static call on an imported or
java.langlibrary class (Log.d,TextUtils.isEmpty) is left unresolved the same way.When no import decides between same-named types, a Java call prefers the Java declaration over a Scala or Kotlin one (
play.data.Formvsplay.api.data.Form).This is the Java counterpart of the Kotlin receiver typing in #1933. The two share an idea but not code yet:
JAVA_EXTERNAL_TYPEhere,KOTLIN_EXTERNAL_TYPEthere. Whichever lands second will need a small textual merge ininferJavaFieldReceiverType, whose Java/Kotlin field block this PR narrows to Kotlin.Measured
Each corpus was indexed with
main(ba3c21e) and with this branch. Node counts are identical.Every gained edge is a typed resolution at 0.9. I hand-checked samples against source; every sampled edge was correct:
Indexing time on SystemUI isn't higher.
Known limits
PARSER.parseFrom→Parserrather thanAbstractParser. This is the same choice as for Kotlin.records aren't indexed as types, an existing extractor gap. A call on a record-typed receiver now gets no edge instead of a guess.Tests
The new
__tests__/java-receiver-type.test.tshas 12 cases: 8 fail onmain, and 4 are regression guards. The Java-over-Scala guard also fails if the tie-break is removed. The full suite passes: 275 files, 4,730 tests.🤖 Generated with Claude Code