Kotlin: receiver-typed member calls, and park them for cross-repo merges - #3389
Kotlin: receiver-typed member calls, and park them for cross-repo merges#3389xiongjianxu wants to merge 11 commits into
Conversation
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 4 advisory finding(s) below merit a look before merge.
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
Adds Kotlin cross-file member-call resolution: _resolve_kotlin_member_calls looks up a receiver's declared type in the per-file kotlin_type_table (or treats a capitalized receiver as the type for companion/object/static calls), then emits a calls edge to the single class/object declaring that method — EXTRACTED when the type is named in source, INFERRED when pulled from the table. Bails on ambiguous multi-definition types and skips Kotlin/Java/global builtin types; a receiver typed to a class declared nowhere in the corpus is parked on the caller for a later merge rather than dropped. Extends the kotlin cross-repo suffix set to include .java so Kotlin calls can bind against Java declarations, and registers the new resolver alongside the existing qualified-call pass.
Worth a look
- Kotlin overloads are resolved to an arbitrary method —
graphify/extract.py:4467· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Existing non-call edge suppresses required call edge —
graphify/extract.py:4496· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- _kotlin_constructor_type returns None after first non-matching call_expression head —
graphify/extractors/engine.py· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Kotlin local shadowing is ignored —
graphify/extractors/engine.py:887· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 2167 functions depend on the 524 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 546 callers, 43 callees - new:
_rebuild_code()— 115 callers, 51 callees - new:
_extract_generic()— 18 callers, 27 callees - new:
extract_js()— 85 callers, 4 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
dispatch_command()— 2 callers, 124 callees - new:
extract_objc()— 27 callers, 9 callees - new:
_get_extractor()— 26 callers, 6 callees - …and 44 more — each is listed as a finding
Verification — 2167 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 2002 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify \_extract\_generic.
The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
· 1 grounded finding(s) anchored inline below; 51 more finding(s) on lines outside this diff (see the check run).
| "line": "L2"}] | ||
|
|
||
|
|
||
| def test_a_kotlin_call_binds_to_a_java_declaration(): |
There was a problem hiding this comment.
test_a_kotlin_call_binds_to_a_java_declaration()
fans out to 6 callees (efferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
|
Self-audit pass over the bot review. Three fixes pushed, one finding I am not acting on. 1. The declaration index was corpus-wide. A same-named class in an unrelated language could answer a Kotlin receiver, and its presence made 2. The INFERRED score was off the rubric. It emitted 3. Not acting on — Verification: |
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 4 advisory finding(s) below merit a look before merge.
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
Adds Kotlin receiver-typed member-call resolution: _resolve_kotlin_member_calls looks a receiver up in the per-file kotlin_type_table, and when exactly one JVM-family class/object declares that type, emits a calls edge to its member — EXTRACTED for a source-named type like Registry.register(), INFERRED (0.85) for a table-inferred receiver. Treats a receiver whose type is declared nowhere in the corpus by parking the call on the caller for a later merge to finish, and skips builtin, ambiguous (multiple-definition), and self-referential targets. Widens the cross-repo Kotlin suffix set to the whole JVM family (.java, .scala, .groovy, .gradle) so a Kotlin call can bind to a Java/JVM declaration on the shared classpath.
Worth a look
- Overloaded Kotlin methods resolve to an arbitrary target —
graphify/extract.py:4467· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Kotlin member resolver picks an arbitrary overload —
graphify/extract.py:4473· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Non-call edges suppress Kotlin call emission —
graphify/extract.py:4505· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- DFS pop ordering breaks documented 'first binding wins' semantics —
graphify/extractors/engine.py:883· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 2169 functions depend on the 526 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 546 callers, 43 callees - new:
_rebuild_code()— 115 callers, 51 callees - new:
_extract_generic()— 18 callers, 27 callees - new:
extract_js()— 85 callers, 4 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
dispatch_command()— 2 callers, 124 callees - new:
extract_objc()— 27 callers, 9 callees - new:
_get_extractor()— 26 callers, 6 callees - …and 44 more — each is listed as a finding
Verification — 2169 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 2004 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify \_extract\_generic.
The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
· 1 grounded finding(s) anchored inline below; 51 more finding(s) on lines outside this diff (see the check run).
| "line": "L2"}] | ||
|
|
||
|
|
||
| def test_a_kotlin_call_binds_to_a_java_declaration(): |
There was a problem hiding this comment.
test_a_kotlin_call_binds_to_a_java_declaration()
fans out to 6 callees (efferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
There was a problem hiding this comment.
Graphify reviewed this change.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
Adds Kotlin receiver-typed member-call resolution: a new _resolve_kotlin_member_calls resolver looks each greeter.greet() receiver up in the per-file kotlin_type_table, and when exactly one JVM-family class/object declares that type, emits a calls edge to its member (EXTRACTED when the receiver spells the type, INFERRED when it comes from the table); ambiguous or builtin types are skipped, and a receiver whose type is declared nowhere in the corpus is parked on the caller for a later merged graph. Treats Kotlin's cross-repo call surface as the whole JVM classpath by resolving .kt/.kts calls against .java, .scala, .groovy, and .gradle declarations too. Adds Kotlin type/name node helpers (_kotlin_property_name, _kotlin_head_type_name) and a _KOTLIN_BUILTIN_TYPES set feeding the builtin filter.
No blocking issues surfaced. 16 lower-confidence candidates did not survive cross-model review.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 2170 functions depend on the 527 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 546 callers, 43 callees - new:
_rebuild_code()— 115 callers, 51 callees - new:
_extract_generic()— 18 callers, 27 callees - new:
extract_js()— 85 callers, 4 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
dispatch_command()— 2 callers, 124 callees - new:
extract_objc()— 27 callers, 9 callees - new:
_get_extractor()— 26 callers, 6 callees - …and 44 more — each is listed as a finding
Verification — 2170 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 2005 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify \_extract\_generic.
The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
· 1 grounded finding(s) anchored inline below; 51 more finding(s) on lines outside this diff (see the check run).
| "line": "L2"}] | ||
|
|
||
|
|
||
| def test_a_kotlin_call_binds_to_a_java_declaration(): |
There was a problem hiding this comment.
test_a_kotlin_call_binds_to_a_java_declaration()
fans out to 6 callees (efferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
|
Thanks — the ordering finding was real and is now fixed in
fun run(flag: Boolean) {
if (flag) {
val svc: Alpha = Alpha()
svc.doThing() // before: bound to Beta.doThing
} else {
val svc: Beta = Beta()
}
}Fix is The other three findings are unchanged from the previous round and remain deliberate:
|
2489847 to
08124af
Compare
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 3 advisory finding(s) below merit a look before merge.
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
Adds Kotlin receiver-typed member-call resolution: _resolve_kotlin_member_calls looks up a receiver in the per-file kotlin_type_table, takes the single JVM-family declaration of that type, and emits a calls edge to its member — EXTRACTED when the receiver spells the type in source, INFERRED when the type came from the table. Treats a capitalized receiver as the type itself (companion/static or object singleton), skips builtin and ambiguous multi-definition types, and parks receivers whose type this corpus declares nowhere for a later merged graph. Widens the Kotlin cross-repo suffix set to the whole JVM family (.java, .scala, .groovy, .gradle) so a Kotlin call can bind to a Java-library declaration over one classpath.
Worth a look
- Overloaded Kotlin methods resolve to an arbitrary target —
graphify/extract.py:4530· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Non-call edges suppress required Kotlin call edges —
graphify/extract.py:4564· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Kotlin function parameter receiver types are stored in a file-global table —
graphify/extractors/engine.py:4629· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 2206 functions depend on the 535 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 565 callers, 43 callees - new:
_rebuild_code()— 115 callers, 51 callees - new:
_extract_generic()— 18 callers, 27 callees - new:
extract_js()— 85 callers, 4 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
dispatch_command()— 2 callers, 124 callees - new:
extract_objc()— 27 callers, 9 callees - new:
_get_extractor()— 26 callers, 6 callees - …and 45 more — each is listed as a finding
Verification — 2206 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 2041 function(s) in the blast radius were not formally verified this run
Test selection
Test selection
106 of 263 test file(s) selected (40%) via static blast radius.
tests/test_astro_extraction.py— impacttests/test_astro_import_ids.py— impacttests/test_build.py— impacttests/test_builtin_global_type_refs.py— impacttests/test_case_sensitive_resolution.py— impacttests/test_cjs_module_extension.py— impacttests/test_cpp_nested_and_cli.py— impacttests/test_cpp_objc_cross_file_calls.py— impacttests/test_cross_extension_reexport_self_cycle.py— impacttests/test_cross_language_call_resolution.py— impacttests/test_cross_repo_member_calls.py— impact, changed-testtests/test_csharp_call_site_generic_args.py— impacttests/test_csharp_enum_members.py— impacttests/test_csharp_field_generic_args.py— impacttests/test_csharp_generic_callsites.py— impacttests/test_csharp_interface_dispatch.py— impacttests/test_csharp_member_calls.py— impacttests/test_csharp_member_nodes.py— impacttests/test_csharp_object_creation.py— impacttests/test_csharp_partial_classes.py— impacttests/test_csharp_type_resolution.py— impacttests/test_definition_file_portability.py— impacttests/test_detect.py— impacttests/test_dotnet.py— impacttests/test_duplicate_annotation_edges.py— impacttests/test_extract.py— impacttests/test_extract_cache_location.py— impacttests/test_file_label_disambiguation.py— impacttests/test_file_node_id_spec.py— impacttests/test_forwarding_review_findings.py— impacttests/test_global_graph.py— impacttests/test_go_builtin_call_targets.py— impacttests/test_go_qualified_resolution.py— impacttests/test_import_extension_resolution.py— impacttests/test_import_self_loops.py— impacttests/test_imported_export_forwarding.py— impacttests/test_incremental.py— impacttests/test_indirect_call_arrow_single_param_shadow.py— impacttests/test_indirect_call_catch_binding_shadow.py— impacttests/test_indirect_call_external_import_shadow.py— impacttests/test_indirect_call_for_of_binding_shadow.py— impacttests/test_indirect_call_function_expression_shadow.py— impacttests/test_indirect_call_nested_closure_shadow.py— impacttests/test_indirect_dispatch.py— impacttests/test_indirect_dispatch_assign_return.py— impacttests/test_indirect_dispatch_getattr.py— impacttests/test_inferred_confidence_rubric.py— impacttests/test_inherited_field_receivers.py— impacttests/test_java_member_calls.py— impacttests/test_java_type_resolution.py— impact- … and 56 more
Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.
Formal verification
Could not verify: Could not verify \_extract\_generic.
The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
· 1 grounded finding(s) anchored inline below; 52 more finding(s) on lines outside this diff (see the check run).
| "line": "L2"}] | ||
|
|
||
|
|
||
| def test_a_kotlin_call_binds_to_a_java_declaration(): |
There was a problem hiding this comment.
test_a_kotlin_call_binds_to_a_java_declaration()
fans out to 6 callees (efferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
08124af to
e636769
Compare
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 5 advisory finding(s) below merit a look before merge.
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
Adds Kotlin receiver-typed member-call resolution: a new _resolve_kotlin_member_calls pass looks each greeter.greet() receiver up in the per-file kotlin_type_table (or treats a capitalized receiver as the type itself for companion/object calls), resolves through the single declaring class, and emits a calls edge — EXTRACTED when the type is named in source, INFERRED (0.85) when it comes from the table — since the shared cross-file pass skips member calls and left these unresolved. Parks a receiver whose type is declared nowhere in the corpus on the caller for a later merged graph rather than dropping it, and skips builtin and ambiguous multi-definition types. Widens Kotlin's cross-repo suffix set and definition matching to the whole JVM family (.java, .scala, .groovy, .gradle) so a Kotlin call resolves against interop declarations on the shared classpath.
Worth a look
- Duplicate Kotlin member names resolve arbitrarily —
graphify/extract.py:4650· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Kotlin member-call resolver arbitrarily picks one overloaded method —
graphify/extract.py:4661· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Capitalized Kotlin variables bypass the type table —
graphify/extract.py:4662· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Kotlin member-call dedupe suppresses calls when any non-call edge already connects the same nodes —
graphify/extract.py:4663· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Plain Kotlin constructor parameters are treated as class properties —
graphify/extractors/engine.py:865· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 2237 functions depend on the 539 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 587 callers, 44 callees - new:
_rebuild_code()— 115 callers, 51 callees - new:
_extract_generic()— 18 callers, 27 callees - new:
extract_js()— 85 callers, 4 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
dispatch_command()— 2 callers, 124 callees - new:
extract_objc()— 27 callers, 9 callees - new:
_get_extractor()— 26 callers, 6 callees - …and 46 more — each is listed as a finding
Verification — 2237 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 2067 function(s) in the blast radius were not formally verified this run
Test selection
Test selection
109 of 269 test file(s) selected (41%) via static blast radius.
tests/test_astro_extraction.py— impacttests/test_astro_import_ids.py— impacttests/test_build.py— impacttests/test_builtin_global_type_refs.py— impacttests/test_case_sensitive_resolution.py— impacttests/test_cjs_module_extension.py— impacttests/test_cpp_nested_and_cli.py— impacttests/test_cpp_objc_cross_file_calls.py— impacttests/test_cross_extension_reexport_self_cycle.py— impacttests/test_cross_language_call_resolution.py— impacttests/test_cross_repo_member_calls.py— impact, changed-testtests/test_csharp_call_site_generic_args.py— impacttests/test_csharp_enum_members.py— impacttests/test_csharp_field_generic_args.py— impacttests/test_csharp_generic_callsites.py— impacttests/test_csharp_interface_dispatch.py— impacttests/test_csharp_member_calls.py— impacttests/test_csharp_member_nodes.py— impacttests/test_csharp_object_creation.py— impacttests/test_csharp_partial_classes.py— impacttests/test_csharp_type_resolution.py— impacttests/test_definition_file_portability.py— impacttests/test_detect.py— impacttests/test_dotnet.py— impacttests/test_duplicate_annotation_edges.py— impacttests/test_extract.py— impacttests/test_extract_cache_location.py— impacttests/test_file_label_disambiguation.py— impacttests/test_file_node_id_spec.py— impacttests/test_forwarding_review_findings.py— impacttests/test_global_add_tag_inference.py— impacttests/test_global_graph.py— impacttests/test_go_builtin_call_targets.py— impacttests/test_go_qualified_resolution.py— impacttests/test_import_extension_resolution.py— impacttests/test_import_self_loops.py— impacttests/test_imported_export_forwarding.py— impacttests/test_incremental.py— impacttests/test_indirect_call_arrow_single_param_shadow.py— impacttests/test_indirect_call_catch_binding_shadow.py— impacttests/test_indirect_call_external_import_shadow.py— impacttests/test_indirect_call_for_of_binding_shadow.py— impacttests/test_indirect_call_function_expression_shadow.py— impacttests/test_indirect_call_nested_closure_shadow.py— impacttests/test_indirect_dispatch.py— impacttests/test_indirect_dispatch_assign_return.py— impacttests/test_indirect_dispatch_getattr.py— impacttests/test_inferred_confidence_rubric.py— impacttests/test_inherited_field_receivers.py— impacttests/test_issue_3405_python_resolution.py— impact- … and 59 more
Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.
Formal verification
Could not verify: Could not verify \_extract\_generic.
The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
· 1 grounded finding(s) anchored inline below; 53 more finding(s) on lines outside this diff (see the check run).
| "line": "L2"}] | ||
|
|
||
|
|
||
| def test_a_kotlin_call_binds_to_a_java_declaration(): |
There was a problem hiding this comment.
test_a_kotlin_call_binds_to_a_java_declaration()
fans out to 6 callees (efferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
|
Rebased onto Two of the five findings were real. Both are fixed with a test that fails on the parent commit. 1. Plain Kotlin constructor parameters were treated as class properties — class App(raw: Raw) {
private val raw = Wrapper(raw)
fun run() { raw.doThing() } // before: bound to Raw.doThing
}Dropping plain parameters outright would have been wrong in the other direction — 2. Capitalized receivers bypassed the type table — val Greeter = Other()
Greeter.greet() // before: bound to class Greeter
The other three are unchanged from the previous rounds and remain deliberate:
Verification: |
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 3 advisory finding(s) below merit a look before merge.
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
Adds Kotlin receiver-typed member-call resolution so a call like greeter.greet() whose method lives in another file now emits a calls edge, resolving the receiver through the per-file kotlin_type_table (or treating a capitalized unbound receiver as the type itself for companion/object/static calls). Edges are marked EXTRACTED at score 1.0 when the receiver names the type in source and INFERRED at 0.85 when the type comes from the table; ambiguous multi-definition types bail as a god-node guard, and receivers whose type is declared nowhere in the corpus are parked on the caller for the merge pass. Extends the Kotlin cross-repo suffix set to the full JVM family (.java, .scala, .groovy, .gradle) so classpath interop counts as a valid answer.
Worth a look
- Duplicate Kotlin member names are resolved arbitrarily —
graphify/extract.py:4669· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Existing non-call edges suppress required Kotlin call edges —
graphify/extract.py:4673· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Kotlin local receiver types ignore scope and declaration order —
graphify/extractors/engine.py:6291· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 2240 functions depend on the 542 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 587 callers, 44 callees - new:
_rebuild_code()— 115 callers, 51 callees - new:
_extract_generic()— 18 callers, 27 callees - new:
extract_js()— 85 callers, 4 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
dispatch_command()— 2 callers, 124 callees - new:
extract_objc()— 27 callers, 9 callees - new:
_get_extractor()— 26 callers, 6 callees - …and 46 more — each is listed as a finding
Verification — 2240 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 2070 function(s) in the blast radius were not formally verified this run
Test selection
Test selection
109 of 269 test file(s) selected (41%) via static blast radius.
tests/test_astro_extraction.py— impacttests/test_astro_import_ids.py— impacttests/test_build.py— impacttests/test_builtin_global_type_refs.py— impacttests/test_case_sensitive_resolution.py— impacttests/test_cjs_module_extension.py— impacttests/test_cpp_nested_and_cli.py— impacttests/test_cpp_objc_cross_file_calls.py— impacttests/test_cross_extension_reexport_self_cycle.py— impacttests/test_cross_language_call_resolution.py— impacttests/test_cross_repo_member_calls.py— impact, changed-testtests/test_csharp_call_site_generic_args.py— impacttests/test_csharp_enum_members.py— impacttests/test_csharp_field_generic_args.py— impacttests/test_csharp_generic_callsites.py— impacttests/test_csharp_interface_dispatch.py— impacttests/test_csharp_member_calls.py— impacttests/test_csharp_member_nodes.py— impacttests/test_csharp_object_creation.py— impacttests/test_csharp_partial_classes.py— impacttests/test_csharp_type_resolution.py— impacttests/test_definition_file_portability.py— impacttests/test_detect.py— impacttests/test_dotnet.py— impacttests/test_duplicate_annotation_edges.py— impacttests/test_extract.py— impacttests/test_extract_cache_location.py— impacttests/test_file_label_disambiguation.py— impacttests/test_file_node_id_spec.py— impacttests/test_forwarding_review_findings.py— impacttests/test_global_add_tag_inference.py— impacttests/test_global_graph.py— impacttests/test_go_builtin_call_targets.py— impacttests/test_go_qualified_resolution.py— impacttests/test_import_extension_resolution.py— impacttests/test_import_self_loops.py— impacttests/test_imported_export_forwarding.py— impacttests/test_incremental.py— impacttests/test_indirect_call_arrow_single_param_shadow.py— impacttests/test_indirect_call_catch_binding_shadow.py— impacttests/test_indirect_call_external_import_shadow.py— impacttests/test_indirect_call_for_of_binding_shadow.py— impacttests/test_indirect_call_function_expression_shadow.py— impacttests/test_indirect_call_nested_closure_shadow.py— impacttests/test_indirect_dispatch.py— impacttests/test_indirect_dispatch_assign_return.py— impacttests/test_indirect_dispatch_getattr.py— impacttests/test_inferred_confidence_rubric.py— impacttests/test_inherited_field_receivers.py— impacttests/test_issue_3405_python_resolution.py— impact- … and 59 more
Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.
Formal verification
Could not verify: Could not verify \_extract\_generic.
The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
· 1 grounded finding(s) anchored inline below; 53 more finding(s) on lines outside this diff (see the check run).
| "line": "L2"}] | ||
|
|
||
|
|
||
| def test_a_kotlin_call_binds_to_a_java_declaration(): |
There was a problem hiding this comment.
test_a_kotlin_call_binds_to_a_java_declaration()
fans out to 6 callees (efferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
|
Third finding was real too — fixed in Kotlin local receiver types ignored scope — class App {
fun a() { val svc = Alpha(); svc.doThing() } // Alpha.doThing
fun b() { val svc = Beta(); svc.doThing() } // also Alpha.doThing — wrong
}
Parameters and locals now live in a per-caller table and travel on the raw call as Two tests: Declaration order within one body is unchanged and already covered: a local must be declared before use in Kotlin, and the sibling-branch case is pinned by The remaining two findings ( Verification: |
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 5 advisory finding(s) below merit a look before merge.
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
Adds Kotlin receiver-typed member-call resolution so a call like greeter.greet() whose method lives in another file now emits a calls edge instead of nothing. _resolve_kotlin_member_calls resolves the receiver through its per-caller receiver_type, then the file-level kotlin_type_table (properties and primary-constructor params), then a capitalized spelling treated as the type itself; it marks the edge EXTRACTED (1.0) when the type is named in source and INFERRED (0.85) when it came from a table, bails on ambiguous multi-definition targets, skips builtins, and parks receivers whose type this corpus declares nowhere for a later merged-graph pass. Extends the kotlin cross-repo suffix set to the whole JVM family (.java, .scala, .groovy, .gradle) so interop calls resolve across one classpath.
Worth a look
- Overloaded Kotlin methods resolve to an arbitrary target —
graphify/extract.py:4655· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Kotlin member resolver arbitrarily chooses one overload —
graphify/extract.py:4658· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Qualified Kotlin receiver types do not match bare class declarations —
graphify/extract.py:4676· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Kotlin two-segment nav now sets member_receiver, may regress in-file Foo.bar() resolution —
graphify/extractors/engine.py:5575· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Kotlin class-level receiver type falls back to function-scoped table only, class-bound names never emit receiver_type —
graphify/extractors/engine.py:6050· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 2241 functions depend on the 543 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 587 callers, 44 callees - new:
_rebuild_code()— 115 callers, 51 callees - new:
_extract_generic()— 18 callers, 27 callees - new:
extract_js()— 85 callers, 4 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
dispatch_command()— 2 callers, 124 callees - new:
extract_objc()— 27 callers, 9 callees - new:
_get_extractor()— 26 callers, 6 callees - …and 46 more — each is listed as a finding
Verification — 2241 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 2071 function(s) in the blast radius were not formally verified this run
Test selection
Test selection
109 of 269 test file(s) selected (41%) via static blast radius.
tests/test_astro_extraction.py— impacttests/test_astro_import_ids.py— impacttests/test_build.py— impacttests/test_builtin_global_type_refs.py— impacttests/test_case_sensitive_resolution.py— impacttests/test_cjs_module_extension.py— impacttests/test_cpp_nested_and_cli.py— impacttests/test_cpp_objc_cross_file_calls.py— impacttests/test_cross_extension_reexport_self_cycle.py— impacttests/test_cross_language_call_resolution.py— impacttests/test_cross_repo_member_calls.py— impact, changed-testtests/test_csharp_call_site_generic_args.py— impacttests/test_csharp_enum_members.py— impacttests/test_csharp_field_generic_args.py— impacttests/test_csharp_generic_callsites.py— impacttests/test_csharp_interface_dispatch.py— impacttests/test_csharp_member_calls.py— impacttests/test_csharp_member_nodes.py— impacttests/test_csharp_object_creation.py— impacttests/test_csharp_partial_classes.py— impacttests/test_csharp_type_resolution.py— impacttests/test_definition_file_portability.py— impacttests/test_detect.py— impacttests/test_dotnet.py— impacttests/test_duplicate_annotation_edges.py— impacttests/test_extract.py— impacttests/test_extract_cache_location.py— impacttests/test_file_label_disambiguation.py— impacttests/test_file_node_id_spec.py— impacttests/test_forwarding_review_findings.py— impacttests/test_global_add_tag_inference.py— impacttests/test_global_graph.py— impacttests/test_go_builtin_call_targets.py— impacttests/test_go_qualified_resolution.py— impacttests/test_import_extension_resolution.py— impacttests/test_import_self_loops.py— impacttests/test_imported_export_forwarding.py— impacttests/test_incremental.py— impacttests/test_indirect_call_arrow_single_param_shadow.py— impacttests/test_indirect_call_catch_binding_shadow.py— impacttests/test_indirect_call_external_import_shadow.py— impacttests/test_indirect_call_for_of_binding_shadow.py— impacttests/test_indirect_call_function_expression_shadow.py— impacttests/test_indirect_call_nested_closure_shadow.py— impacttests/test_indirect_dispatch.py— impacttests/test_indirect_dispatch_assign_return.py— impacttests/test_indirect_dispatch_getattr.py— impacttests/test_inferred_confidence_rubric.py— impacttests/test_inherited_field_receivers.py— impacttests/test_issue_3405_python_resolution.py— impact- … and 59 more
Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.
Formal verification
Could not verify: Could not verify \_extract\_generic.
The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
· 1 grounded finding(s) anchored inline below; 53 more finding(s) on lines outside this diff (see the check run).
| "line": "L2"}] | ||
|
|
||
|
|
||
| def test_a_kotlin_call_binds_to_a_java_declaration(): |
There was a problem hiding this comment.
test_a_kotlin_call_binds_to_a_java_declaration()
fans out to 6 callees (efferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
|
Thanks — one real defect out of the five, fixed in 2ffe2bf. Qualified receiver types (finding 3) — real, and worse than reported. No edge, and a package segment parked as if it were a type — so the miss travels into the merged graph as a fact and Three tests, all three failing on
Two-segment navigation regressing in-file
Probed both shapes directly (in-file Class-bound names never emitting Arbitrary overload pick (findings 1–2) — same finding as previous rounds, declined for the same reason: the Full suite on |
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 3 advisory finding(s) below merit a look before merge.
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
Adds cross-file Kotlin member-call resolution: _resolve_kotlin_member_calls types a call's receiver from the caller's params/locals, then the per-file kotlin_type_table of class bindings, then treats a capitalized bare receiver as the type itself, and emits a calls edge to the single class/object declaring that type — EXTRACTED when the type is named in source, INFERRED when it came from a table. Receivers whose type this corpus declares nowhere are parked on the caller for a later merged graph, and builtin/ambiguous (multi-definition) types are skipped rather than guessed. Widens Kotlin's cross-repo suffix set to the whole JVM family (.java, .scala, .groovy, .gradle) so Kotlin-over-Java interop resolves.
Worth a look
- companion/static call heuristic misfires on lowercase object singletons —
graphify/extract.py· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Qualified Kotlin receiver types do not match bare declarations —
graphify/extract.py:4677· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Collected Kotlin function parameter receiver types are never read —
graphify/extractors/engine.py:4848· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 2247 functions depend on the 549 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 587 callers, 44 callees - new:
_rebuild_code()— 115 callers, 51 callees - new:
_extract_generic()— 18 callers, 27 callees - new:
extract_js()— 85 callers, 4 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
dispatch_command()— 2 callers, 124 callees - new:
extract_objc()— 27 callers, 9 callees - new:
_get_extractor()— 26 callers, 6 callees - …and 46 more — each is listed as a finding
Verification — 2247 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 2077 function(s) in the blast radius were not formally verified this run
Test selection
Test selection
109 of 269 test file(s) selected (41%) via static blast radius.
tests/test_astro_extraction.py— impacttests/test_astro_import_ids.py— impacttests/test_build.py— impacttests/test_builtin_global_type_refs.py— impacttests/test_case_sensitive_resolution.py— impacttests/test_cjs_module_extension.py— impacttests/test_cpp_nested_and_cli.py— impacttests/test_cpp_objc_cross_file_calls.py— impacttests/test_cross_extension_reexport_self_cycle.py— impacttests/test_cross_language_call_resolution.py— impacttests/test_cross_repo_member_calls.py— impact, changed-testtests/test_csharp_call_site_generic_args.py— impacttests/test_csharp_enum_members.py— impacttests/test_csharp_field_generic_args.py— impacttests/test_csharp_generic_callsites.py— impacttests/test_csharp_interface_dispatch.py— impacttests/test_csharp_member_calls.py— impacttests/test_csharp_member_nodes.py— impacttests/test_csharp_object_creation.py— impacttests/test_csharp_partial_classes.py— impacttests/test_csharp_type_resolution.py— impacttests/test_definition_file_portability.py— impacttests/test_detect.py— impacttests/test_dotnet.py— impacttests/test_duplicate_annotation_edges.py— impacttests/test_extract.py— impacttests/test_extract_cache_location.py— impacttests/test_file_label_disambiguation.py— impacttests/test_file_node_id_spec.py— impacttests/test_forwarding_review_findings.py— impacttests/test_global_add_tag_inference.py— impacttests/test_global_graph.py— impacttests/test_go_builtin_call_targets.py— impacttests/test_go_qualified_resolution.py— impacttests/test_import_extension_resolution.py— impacttests/test_import_self_loops.py— impacttests/test_imported_export_forwarding.py— impacttests/test_incremental.py— impacttests/test_indirect_call_arrow_single_param_shadow.py— impacttests/test_indirect_call_catch_binding_shadow.py— impacttests/test_indirect_call_external_import_shadow.py— impacttests/test_indirect_call_for_of_binding_shadow.py— impacttests/test_indirect_call_function_expression_shadow.py— impacttests/test_indirect_call_nested_closure_shadow.py— impacttests/test_indirect_dispatch.py— impacttests/test_indirect_dispatch_assign_return.py— impacttests/test_indirect_dispatch_getattr.py— impacttests/test_inferred_confidence_rubric.py— impacttests/test_inherited_field_receivers.py— impacttests/test_issue_3405_python_resolution.py— impact- … and 59 more
Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.
Formal verification
Could not verify: Could not verify \_extract\_generic.
The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
· 1 grounded finding(s) anchored inline below; 53 more finding(s) on lines outside this diff (see the check run).
| "line": "L2"}] | ||
|
|
||
|
|
||
| def test_a_kotlin_call_binds_to_a_java_declaration(): |
There was a problem hiding this comment.
test_a_kotlin_call_binds_to_a_java_declaration()
fans out to 6 callees (efferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
The shared cross-file pass skips member calls, so `greeter.greet()` on a receiver whose class is declared in another file produced no edge at all — the Kotlin twin of the Swift gap in Graphify-Labs#1356. Kotlin had no receiver typing to fall back on: the engine exported no per-file type table for it, and the call site stamped no receiver. The extractor now builds `kotlin_type_table` from the four places a Kotlin name gets a declared type — a primary-constructor parameter, a property, a function parameter, and a local `val`/`var` binding (annotated, or constructed with a capitalized head). First binding wins, so a parameter named like a property cannot redirect the property's own calls. Two-segment `recv.method()` chains now stamp `member_receiver`, and every Kotlin raw_call carries `lang="kotlin"`. `_resolve_kotlin_member_calls` reads the table, takes the single class or object declaring that type, and emits the `calls` edge to its member. A capitalized receiver is the type itself (`Registry.register()`), which is exact and stays EXTRACTED; a table-typed one is INFERRED. Kotlin builtins are excluded so a local `class Regex` cannot answer for `kotlin.text.Regex`. Kotlin is excluded from the capitalized-receiver deferral: `Foo.bar()` resolves in-file today and the receiver type is only usable once the bare name misses locally, which already leaves the target unresolved. Three-segment chains stay with the fully-qualified pass (Graphify-Labs#2550).
A Kotlin receiver typed to a class this build declares nowhere is a call into another repository, not a mistake. The resolver held the receiver type and dropped the call, so graph.json — the only artifact merge-graphs and global add read — recorded nothing and no merge-time pass could recover it. Those calls are now parked on the caller node by name, and the merge pass binds them when the type resolves to exactly one declaration in another repo. A Kotlin entry accepts a `.java` declaration as well: the JVM classpath is one namespace, and a Kotlin module over a Java library is the common Android shape.
A corpus-wide type index let a same-named class in an unrelated language answer a Kotlin receiver, and it hid from the parking branch that nothing on the classpath declares the type; `.java` stays in, since interop makes it a real answer. The INFERRED score was 0.8, off the discrete scale the spec fixes.
…e shared resolver does
… wins _kotlin_local_var_types pushed children onto a LIFO stack in document order, so siblings popped reversed and the LAST same-named local won — the opposite of the documented "first binding wins". A call in one if-branch bound to the type declared in the sibling else-branch.
A `class_parameter` without `val`/`var` declares no member — it is in scope only in
initializers and `init` blocks — so `class App(raw: Raw) { val raw = Wrapper(raw) }`
bound every `raw.doThing()` in a method body to `Raw`, not `Wrapper`. The parameter's
name is now recorded as weak and a body property of the same name takes it back, while
plain parameters keep typing the receivers written in initializers.
…a type A capitalized receiver was taken to be the type itself without checking scope, so a local shadowing a class name (`val Greeter = Other()`) sent `Greeter.greet()` to the class instead of to `Other`. The table now answers first and the capitalized spelling is the fallback, which is what makes it a companion/`object` call in the first place.
…nction
The receiver table was flat per file, so `svc` bound in two function bodies was one
name and the second function inherited the first one's type:
fun a() { val svc = Alpha(); svc.doThing() } // Alpha.doThing
fun b() { val svc = Beta(); svc.doThing() } // also Alpha.doThing
Parameters and locals now live in a per-caller table and travel on the raw call as
`receiver_type`, the shape the Java and C# resolvers already read; class properties and
primary-constructor parameters stay file-wide, because they are. A parameter therefore
types the receivers in its own body without retyping a property's calls elsewhere.
`private val greeter: com.example.Greeter` and `fun run(greeter: com.example.Greeter)` both typed the receiver `com`, because the shared reference collector reports a dotted `user_type`'s first identifier. The call resolved to nothing and the package segment was parked as the receiver's type, so the miss travelled into the merged graph as a fact. The receiver-table paths now take the last segment, keeping the builtin filter so `kotlin.text.Regex` parks nothing rather than parking `kotlin`. The collector itself is untouched: it also drives the `references` edges, whose contexts want every segment.
2ffe2bf to
3694b1e
Compare
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 3 advisory finding(s) below merit a look before merge.
Graphify review — findings
Adds cross-file resolution of Kotlin receiver-typed member calls: a new _resolve_kotlin_member_calls resolver looks up the receiver's type via the caller's params/locals (receiver_type), then the file's kotlin_type_table, then the source spelling, and emits a calls edge to the sole class/object declaring that type — EXTRACTED when the type is named in source, INFERRED (0.85) when it comes from a table. Bails on ambiguous multi-definition types (god-node guard), skips builtins, and parks receivers whose type is declared nowhere in the corpus for a later merged-graph pass (#3152). Widens Kotlin's cross-repo suffix set to the JVM family (.java, .scala, .groovy, .gradle) so a Kotlin call can be answered by any classpath-mate declaration.
Worth a look
- Kotlin member resolver arbitrarily chooses one overload —
graphify/extract.py:4656· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Kotlin member resolver guesses one target for overloaded methods —
graphify/extract.py:4673· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Kotlin member resolver suppresses calls when any other edge already connects the nodes —
graphify/extract.py:4675· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 2247 functions depend on the 549 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 587 callers, 44 callees - new:
_rebuild_code()— 115 callers, 51 callees - new:
_extract_generic()— 18 callers, 27 callees - new:
extract_js()— 85 callers, 4 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
dispatch_command()— 2 callers, 124 callees - new:
extract_objc()— 27 callers, 9 callees - new:
_get_extractor()— 26 callers, 6 callees - …and 46 more — each is listed as a finding
Verification — 2247 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 2077 function(s) in the blast radius were not formally verified this run
Test selection
Test selection
109 of 269 test file(s) selected (41%) via static blast radius.
tests/test_astro_extraction.py— impacttests/test_astro_import_ids.py— impacttests/test_build.py— impacttests/test_builtin_global_type_refs.py— impacttests/test_case_sensitive_resolution.py— impacttests/test_cjs_module_extension.py— impacttests/test_cpp_nested_and_cli.py— impacttests/test_cpp_objc_cross_file_calls.py— impacttests/test_cross_extension_reexport_self_cycle.py— impacttests/test_cross_language_call_resolution.py— impacttests/test_cross_repo_member_calls.py— impact, changed-testtests/test_csharp_call_site_generic_args.py— impacttests/test_csharp_enum_members.py— impacttests/test_csharp_field_generic_args.py— impacttests/test_csharp_generic_callsites.py— impacttests/test_csharp_interface_dispatch.py— impacttests/test_csharp_member_calls.py— impacttests/test_csharp_member_nodes.py— impacttests/test_csharp_object_creation.py— impacttests/test_csharp_partial_classes.py— impacttests/test_csharp_type_resolution.py— impacttests/test_definition_file_portability.py— impacttests/test_detect.py— impacttests/test_dotnet.py— impacttests/test_duplicate_annotation_edges.py— impacttests/test_extract.py— impacttests/test_extract_cache_location.py— impacttests/test_file_label_disambiguation.py— impacttests/test_file_node_id_spec.py— impacttests/test_forwarding_review_findings.py— impacttests/test_global_add_tag_inference.py— impacttests/test_global_graph.py— impacttests/test_go_builtin_call_targets.py— impacttests/test_go_qualified_resolution.py— impacttests/test_import_extension_resolution.py— impacttests/test_import_self_loops.py— impacttests/test_imported_export_forwarding.py— impacttests/test_incremental.py— impacttests/test_indirect_call_arrow_single_param_shadow.py— impacttests/test_indirect_call_catch_binding_shadow.py— impacttests/test_indirect_call_external_import_shadow.py— impacttests/test_indirect_call_for_of_binding_shadow.py— impacttests/test_indirect_call_function_expression_shadow.py— impacttests/test_indirect_call_nested_closure_shadow.py— impacttests/test_indirect_dispatch.py— impacttests/test_indirect_dispatch_assign_return.py— impacttests/test_indirect_dispatch_getattr.py— impacttests/test_inferred_confidence_rubric.py— impacttests/test_inherited_field_receivers.py— impacttests/test_issue_3405_python_resolution.py— impact- … and 59 more
Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.
· 1 grounded finding(s) anchored inline below; 53 more finding(s) on lines outside this diff (see the check run).
| "line": "L2"}] | ||
|
|
||
|
|
||
| def test_a_kotlin_call_binds_to_a_java_declaration(): |
There was a problem hiding this comment.
test_a_kotlin_call_binds_to_a_java_declaration()
fans out to 6 callees (efferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
`val greeter = com.example.Greeter()` names the type only in the initializer, and the constructor head of a qualified call is a navigation expression, not an identifier, so the local stayed untyped and the member call on it resolved to nothing. The capitalized-head test now runs on the last segment, which is what keeps `repo.load()` from typing the local it binds.
|
One of the three findings on Qualified constructor calls (the
Lowercase The "Collected Kotlin function parameter receiver types are never read" — does not hold.
On the Full suite on |
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 4 advisory finding(s) below merit a look before merge.
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
Adds Kotlin receiver-typed member-call resolution: greeter.greet() where the method lives in another file now emits a calls edge by resolving the receiver through its type — caller parameters/locals first, then the file's kotlin_type_table class bindings, then a capitalized spelling treated as the type itself (companion/object/static). Marks the edge EXTRACTED (score 1.0) when the receiver names the type in source and INFERRED (0.85) when the type came from a table, bailing on ambiguous multi-definition types and skipping builtin receiver types; a receiver typed to a class this corpus declares nowhere is parked on the caller for a later merge rather than dropped. Widens Kotlin cross-repo call matching to the whole JVM family (.java, .scala, .groovy, .gradle) so a Kotlin call can be answered by a Java library declaration on the shared classpath.
Worth a look
- Kotlin cross-repo suffix list must include .kt for candidate matching —
graphify/cross_repo_calls.py· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Kotlin member resolver arbitrarily selects one overload —
graphify/extract.py:4657· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Non-call edges suppress required Kotlin call edges —
graphify/extract.py:4659· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Kotlin receiver types are stored file-wide across classes —
graphify/extractors/engine.py:4414· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 2251 functions depend on the 553 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 587 callers, 44 callees - new:
_rebuild_code()— 115 callers, 51 callees - new:
_extract_generic()— 18 callers, 27 callees - new:
extract_js()— 85 callers, 4 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
dispatch_command()— 2 callers, 124 callees - new:
extract_objc()— 27 callers, 9 callees - new:
_get_extractor()— 26 callers, 6 callees - …and 46 more — each is listed as a finding
Verification — 2251 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 2081 function(s) in the blast radius were not formally verified this run
Test selection
Test selection
109 of 269 test file(s) selected (41%) via static blast radius.
tests/test_astro_extraction.py— impacttests/test_astro_import_ids.py— impacttests/test_build.py— impacttests/test_builtin_global_type_refs.py— impacttests/test_case_sensitive_resolution.py— impacttests/test_cjs_module_extension.py— impacttests/test_cpp_nested_and_cli.py— impacttests/test_cpp_objc_cross_file_calls.py— impacttests/test_cross_extension_reexport_self_cycle.py— impacttests/test_cross_language_call_resolution.py— impacttests/test_cross_repo_member_calls.py— impact, changed-testtests/test_csharp_call_site_generic_args.py— impacttests/test_csharp_enum_members.py— impacttests/test_csharp_field_generic_args.py— impacttests/test_csharp_generic_callsites.py— impacttests/test_csharp_interface_dispatch.py— impacttests/test_csharp_member_calls.py— impacttests/test_csharp_member_nodes.py— impacttests/test_csharp_object_creation.py— impacttests/test_csharp_partial_classes.py— impacttests/test_csharp_type_resolution.py— impacttests/test_definition_file_portability.py— impacttests/test_detect.py— impacttests/test_dotnet.py— impacttests/test_duplicate_annotation_edges.py— impacttests/test_extract.py— impacttests/test_extract_cache_location.py— impacttests/test_file_label_disambiguation.py— impacttests/test_file_node_id_spec.py— impacttests/test_forwarding_review_findings.py— impacttests/test_global_add_tag_inference.py— impacttests/test_global_graph.py— impacttests/test_go_builtin_call_targets.py— impacttests/test_go_qualified_resolution.py— impacttests/test_import_extension_resolution.py— impacttests/test_import_self_loops.py— impacttests/test_imported_export_forwarding.py— impacttests/test_incremental.py— impacttests/test_indirect_call_arrow_single_param_shadow.py— impacttests/test_indirect_call_catch_binding_shadow.py— impacttests/test_indirect_call_external_import_shadow.py— impacttests/test_indirect_call_for_of_binding_shadow.py— impacttests/test_indirect_call_function_expression_shadow.py— impacttests/test_indirect_call_nested_closure_shadow.py— impacttests/test_indirect_dispatch.py— impacttests/test_indirect_dispatch_assign_return.py— impacttests/test_indirect_dispatch_getattr.py— impacttests/test_inferred_confidence_rubric.py— impacttests/test_inherited_field_receivers.py— impacttests/test_issue_3405_python_resolution.py— impact- … and 59 more
Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.
Formal verification
Could not verify: Could not verify \_extract\_generic.
The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
· 1 grounded finding(s) anchored inline below; 53 more finding(s) on lines outside this diff (see the check run).
| "line": "L2"}] | ||
|
|
||
|
|
||
| def test_a_kotlin_call_binds_to_a_java_declaration(): |
There was a problem hiding this comment.
test_a_kotlin_call_binds_to_a_java_declaration()
fans out to 6 callees (efferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
Fixes #3388. Two cherry-pickable commits: the single-repo resolver, then the park that
lets a merged graph finish what one build cannot.
Commit 1 — resolve Kotlin member calls through the receiver's declared type
Kotlin had no receiver typing at all, so
greeter.greet()across files produced no edge.This builds the per-file table (
kotlin_type_table) from every place a receiver's type iswritten, and adds
_resolve_kotlin_member_calls, which takes the single class/objectdeclaring that type and emits
callsto its member — EXTRACTED when the receiver namesthe type in source (
Registry.register()), INFERRED when the type came from the table.Table sources, all four needed:
class App(private val greeter: Greeter)declares no property, so nothing else in the walk ever names the typeprivate val greeter: Greeter = Greeter()fun run(greeter: Greeter)val greeter = Greeter()— the type is only implied by the capitalized call headDesign calls:
member_receiveris stamped only for the 2-segment navigation case, and Kotlin isexcluded from the capitalized-receiver deferral. A call either resolves in-file or
falls through to
raw_calls; deferring would move today's in-fileFoo.bar()hits intoraw_callsand regress them. Not deferring keeps in-file behaviour byte-identical andonly enriches the
raw_callsentry on an in-file miss — which is the only situationwhere the receiver type is of any use. A
>= 3-segment chain is an FQN and still goesto
_resolve_kotlin_qualified_calls(Kotlin: fully-qualified call expressions produce no calls edge (same-package control isolates the qualified form) #2550); the broaderlang="kotlin"tag cannotpoach its calls, since that pass requires
qualified_prefix.Without it
fun other(greeter: Other)clobbers the class's ownprivate val greeter: Greeterand redirects the property's calls toOther.greet. Thetable is flat per file, so a parameter shadowing a property has to lose.
callsonly — noreferencesfallback to the type node when no member matches(Swift has one). Smaller blast radius, and such an edge adds nothing the type-reference
walk already emits.
Kotlin imports a class name into scope rather than a module alias, so a capitalized
receiver genuinely is a type, not a namespace.
Commit 2 — park what this build cannot answer (#3152)
A receiver typed to a class with zero declarations in this corpus is parked on the
caller as a
metadata.unresolved_callsentry (names only, never node ids — those arerewritten by the #1529 remap and again by repo prefixing).
> 1declarations staysdropped: local ambiguity is not something merging can narrow. The merge pass then binds
the entry when exactly one declaration in another repo answers it.
_LANG_SUFFIXES["kotlin"]is{.kt, .kts, .java}. The JVM classpath is one namespace, soa Kotlin module calling a Java library in another repo is a genuine member call; excluding
.javawould drop the most common Android two-repo shape. The reverse (javaaccepting.kt) is left alone here — adding it could make an existing Java↔Java pair ambiguous andso remove an edge that lands today.
Precision costs, measured
share a name collapse to one entry. First-binding-wins makes the property the winner,
which is the safer of the two; the loser's calls go unresolved rather than mis-bound.
Log.d(),Build.VERSION) are in neither_KOTLIN_BUILTIN_TYPESnor_JAVA_BUILTIN_TYPES, so they park. In a merge they can bindto a same-named class another repo declares — the single-definition guard limits the
damage but does not rule it out.
Verification
tests/test_kotlin_receiver_member_calls.py— 10 cases, one per type source plus thenegatives that must stay unresolved (untyped
Anyreceiver, two same-named classes, abuiltin
Regexshadowed by a local class, FQN still reaching Kotlin: fully-qualified call expressions produce no calls edge (same-package control isolates the qualified form) #2550's pass). 6 of the 10fail on
v8.tests/test_cross_repo_member_calls.py— akotlin-primary-constructorarm in theper-language park→merge test, plus unit cases pinning that a Kotlin call binds to a Java
declaration and does not bind to a Swift one.
ruff check graphify testsclean.