fix(java): a superclass method wins over an interface default for a bare call

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B8gwEpjajiWHVW3c9smuKf
This commit is contained in:
rohit-jsfreakyandClaude Opus 5.5 committed 2026-10-12 22:24:42 +05:30
1 parent 724626659a
commit 85fab922d2
2 files changed
+35 -5

No files matched your search

+19 -3
View File
@@ -5512,10 +5512,26 @@ def _resolve_java_member_calls(
return None
def _inherited_method(class_nid: str, callee_key: str) -> str | None:
# Level by level up the supertypes, as Java looks a simple method name
# up in the class's members; the class itself was searched in-file.
# Java's "class wins": a method declared up the superclass chain beats
# an interface default, whatever the depth. Only then the interfaces,
# level by level. The class itself was searched in-file.
seen = {class_nid}
level = list(dict.fromkeys(supertypes.get(class_nid, [])))
chain: list[str] = []
cls = class_nid
while len(parents := [b for b in inherits_bases.get(cls, []) if b not in seen]) == 1:
cls = parents[0]
seen.add(cls)
chain.append(cls)
for nid in chain:
source_file = node_by_id.get(nid, {}).get("source_file")
if source_file and _lang_family(source_file) != "jvm":
return None
declared = method_index.get((nid, callee_key))
if declared:
return next(iter(declared)) if len(declared) == 1 else None
level = list(dict.fromkeys(
base for nid in [class_nid, *chain] for base in supertypes.get(nid, []) if base not in seen
))
while level:
hits: set[str] = set()
for nid in level:
+16 -2
View File
@@ -430,13 +430,27 @@ def test_bare_call_takes_the_nearest_supertype_declaring_it(tmp_path: Path):
assert (run, _find(result, ".stop()", "root_stop")) not in calls
def test_bare_call_declared_twice_on_one_level_binds_nothing(tmp_path: Path):
def test_superclass_method_wins_over_an_interface_default(tmp_path: Path):
# Java's "class wins": Root.log() beats Loud's default, even from a deeper level.
calls, result = _calls(tmp_path, {
"Base.java": "class Base { void log() {} }\n",
"Root.java": "class Root { void log() {} }\n",
"Base.java": "class Base extends Root {}\n",
"Loud.java": "interface Loud { default void log() {} }\n",
"Child.java": "class Child extends Base implements Loud { void run() { log(); } }\n",
})
run = _find(result, ".run()", "child_run")
assert (run, _find(result, ".log()", "root_log")) in calls
assert (run, _find(result, ".log()", "loud_log")) not in calls
def test_bare_call_declared_by_two_interfaces_binds_nothing(tmp_path: Path):
calls, result = _calls(tmp_path, {
"Loud.java": "interface Loud { void log(); }\n",
"Quiet.java": "interface Quiet { void log(); }\n",
"Child.java": "abstract class Child implements Loud, Quiet { void run() { log(); } }\n",
})
run = _find(result, ".run()", "child_run")
assert not any(source == run and "log" in target for source, target in calls)