Skip to content

fix(php): resolve Class::method() static calls to the actual method - #3874

Open
rikurunico wants to merge 3 commits into
Graphify-Labs:v8from
rikurunico:fix/php-static-call-confidence
Open

rikurunico wants to merge 3 commits into
Graphify-Labs:v8from
rikurunico:fix/php-static-call-confidence

Conversation

@rikurunico

Copy link
Copy Markdown

Fixes #3872.

Root cause

scoped_call_expression (Helper::format()) was handled in the shared engine's PHP branch by using the scope text as the callee name:

elif node.type == "scoped_call_expression":
    # Static method call: Helper::format() → callee = "Helper"
    scope_node = node.child_by_field_name("scope")
    if scope_node:
        callee_name = _read_text(scope_node, source)

So Helper::format() was extracted as a bare call to "Helper", not "format". It also never set is_member_call, so it fell straight into the generic cross-file bare-name resolver, which matches by label — and Helper the class is itself a labeled node. The result: every PHP static call resolved to the class definition, never the actual method, and picked up whatever confidence the bare-name resolver's import-evidence gate produced (INFERRED 0.85, since same-namespace PHP classes never need a use import for each other).

Verified against a Laravel app: ChartColors::palette() calls across 8 files all resolved their calls edge target to the ChartColors class node, never .palette().

Fix

member_call_expression ($obj->method()) behavior is unchanged — still unresolved, as before this patch (no receiver-typed PHP resolver existed for instance calls; this fix only targets scoped :: calls).

Tests

  • Updated test_php_object_creation.py::test_existing_static_call_edges_are_unchanged → renamed/rewritten as test_static_call_resolves_to_the_actual_method_not_the_class, since the old assertion encoded the bug ("Baz" in target_label was true only because the target was the class).
  • Added tests/test_php_static_call_resolution.py: same-namespace no-import case (the exact PHP Class::method() static calls without an explicit 'use' import stay INFERRED instead of EXTRACTED #3872 repro), self::/static::, parent::, dynamic-scope no-phantom-edge, and an explicit no-regression check that $obj->method() stays unresolved.
  • uv run pytest tests/ -q: 6044 passed, 14 skipped (pre-existing/unrelated), 0 failed.

Real-world validation

Ran the patched extractor over a 1027-file Laravel app/ corpus (no crashes, 8 parallel workers): calls edge confidence distribution went from before (INFERRED-heavy for scoped calls) to 3383 EXTRACTED / 553 INFERRED, with EXTRACTED targets now landing on the actual called methods (spot-checked a random sample of 12).

scoped_call_expression (`Helper::format()`) used the scope text as the
callee name instead of the method name, so the shared cross-file
bare-name pass bound the call to whatever node shared the class's
label - almost always the class definition - never the actual method.
It also skipped member-call handling entirely, so the edge went
through the import-evidence confidence gate: same-namespace PHP
classes never need a `use` import for each other, so every
same-namespace static call was stuck at INFERRED 0.85 even though the
receiver class is named explicitly in source.

Capture the real method name as the callee and the scope as the
receiver, mark it a member call, and add a dedicated PHP resolver
(mirroring the existing Java/Swift/C++ member-call resolvers) that
binds self::/static:: to the enclosing class, parent:: through a
single inherits edge, and Class:: to the class's own method - all
EXTRACTED, since the receiver is named explicitly in source. A
dynamic scope ($var::method()) has no declared-type table to resolve
against and is left unresolved rather than guessed.

Fixes Graphify-Labs#3872
@github-actions

Copy link
Copy Markdown

Thanks for the pull request, @rikurunico. A maintainer will review it soon.

Want to talk it through while it is in review? Come join us on our Discord server. For longer-form discussion there is also GitHub Discussions.

A couple of things that speed up review: make sure the test suite passes on Python 3.10 and 3.13, and that the change keeps extraction deterministic.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Resolves PHP Class::method(), self::/static::, and parent:: static calls to the actual method node via a new _resolve_php_member_calls pass, replacing the old behaviour where scoped_call_expression bound the callee to the scope text and let the cross-file bare-name pass match it to the class definition itself. The engine now emits the real method name as the callee and the scope as a member-call receiver, so these calls land at EXTRACTED confidence instead of being gated on use-import evidence that same-namespace classes never carry. parent:: resolves only through a single inherits edge, and dynamic $var::method() scopes are deliberately left unresolved rather than guessed.

Worth a look

  • Lowercase PHP static class calls are dropped — graphify/extract.py:4642 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • PHP static resolver ignores case-insensitive method names — graphify/extract.py:4664 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Fully qualified PHP static calls are no longer resolved — graphify/extract.py:4642 · 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 — 2455 functions depend on the 518 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 713 callers, 48 callees
  • new: _rebuild_code() — 144 callers, 55 callees
  • new: _extract_generic() — 18 callers, 29 callees
  • new: extract_js() — 87 callers, 4 callees
  • new: extract_xaml() — 19 callers, 17 callees
  • new: main() — 98 callers, 3 callees
  • new: dispatch_command() — 2 callers, 125 callees
  • new: extract_objc() — 27 callers, 9 callees
  • …and 44 more — each is listed as a finding

Verification — 2455 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: 2280 function(s) in the blast radius were not formally verified this run

Test selection

Test selection

129 of 303 test file(s) selected (43%) via static blast radius.

  • tests/test_astro_extraction.py — impact
  • tests/test_astro_import_ids.py — impact
  • tests/test_build.py — impact
  • tests/test_builtin_global_type_refs.py — impact
  • tests/test_case_sensitive_resolution.py — impact
  • tests/test_cjs_module_extension.py — impact
  • tests/test_cobol_extractor.py — impact
  • tests/test_cpp_nested_and_cli.py — impact
  • tests/test_cpp_objc_cross_file_calls.py — impact
  • tests/test_cross_extension_reexport_self_cycle.py — impact
  • tests/test_cross_language_call_resolution.py — impact
  • tests/test_cross_repo_external_call_guards.py — impact
  • tests/test_cross_repo_member_calls.py — impact
  • tests/test_csharp_call_site_generic_args.py — impact
  • tests/test_csharp_enum_members.py — impact
  • tests/test_csharp_field_generic_args.py — impact
  • tests/test_csharp_generic_callsites.py — impact
  • tests/test_csharp_interface_dispatch.py — impact
  • tests/test_csharp_member_calls.py — impact
  • tests/test_csharp_member_nodes.py — impact
  • tests/test_csharp_object_creation.py — impact
  • tests/test_csharp_partial_classes.py — impact
  • tests/test_csharp_type_resolution.py — impact
  • tests/test_definition_file_portability.py — impact
  • tests/test_detect.py — impact
  • tests/test_dotnet.py — impact
  • tests/test_duplicate_annotation_edges.py — impact
  • tests/test_elixir_import_resolution.py — impact
  • tests/test_erlang_extractor.py — impact
  • tests/test_extract.py — impact
  • tests/test_extract_cache_location.py — impact
  • tests/test_extract_php_closures.py — impact
  • tests/test_file_label_disambiguation.py — impact
  • tests/test_file_node_id_spec.py — impact
  • tests/test_forwarding_review_findings.py — impact
  • tests/test_go_builtin_call_targets.py — impact
  • tests/test_go_import_repoint.py — impact
  • tests/test_go_interface_methods.py — impact
  • tests/test_go_qualified_resolution.py — impact
  • tests/test_import_extension_resolution.py — impact
  • tests/test_import_self_loops.py — impact
  • tests/test_imported_export_forwarding.py — impact
  • tests/test_incremental.py — impact
  • tests/test_indirect_call_arrow_single_param_shadow.py — impact
  • tests/test_indirect_call_block_scoped_shadow.py — impact
  • tests/test_indirect_call_catch_binding_shadow.py — impact
  • tests/test_indirect_call_external_import_shadow.py — impact
  • tests/test_indirect_call_for_of_binding_shadow.py — impact
  • tests/test_indirect_call_function_expression_shadow.py — impact
  • tests/test_indirect_call_nested_closure_shadow.py — impact
  • … and 79 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.

Docs that may be stale (advisory)

…and 10 more.

· 1 grounded finding(s) anchored inline below; 51 more finding(s) on lines outside this diff (see the check run).

Comment thread graphify/extract.py
…atic-call resolution

Addresses 3 findings from the automated review on PR Graphify-Labs#3874:

- Fully-qualified scopes (\App\Models\User::method() or
  App\Models\User::method()) were left parked as unresolved: the class
  lookup keyed on the full scope text, which never matches a class
  node's own unqualified label. Now normalizes to the last namespace
  segment before lookup.
- Method names were matched case-sensitively even though PHP method
  calls are case-insensitive; a call spelled with different case than
  the declaration silently produced no edge. The lookup key is now
  lowercased on both sides.
- A lowercase-first class name (legal PHP, just non-PSR) fell through
  the `receiver[:1].isupper()` gate into "dynamic scope" and was
  dropped entirely. The gate is now `not receiver.startswith("$")`,
  since a `$var::` scope is the only case actually requiring the
  declared-type table this resolver doesn't have.
@rikurunico

Copy link
Copy Markdown
Author

Addressed all 3 findings from the automated review in 7ed0e1d:

  • Fully qualified PHP static calls are no longer resolved — the class lookup keyed on the raw scope text, so \App\Models\User::method() / App\Models\User::method() never matched the class node's own unqualified label and got parked as unresolved. Added class_key(), which strips a leading \ and takes the last namespace segment before lookup.
  • PHP static resolver ignores case-insensitive method names — PHP method calls are case-insensitive at the call site; a call spelled with different case than the declaration produced no edge. The key() helper now lowercases both the declared method label and the called name before comparing.
  • Lowercase PHP static class calls are dropped — the receiver[:1].isupper() gate treated any non-uppercase-first scope as "dynamic," so a legal (if non-PSR) lowercase-first class name fell through to unresolved. Replaced the gate with not receiver.startswith("$") — a $var:: scope is the only case that genuinely needs a declared-type table this resolver doesn't have; everything else is a class reference regardless of case.

Added 3 regression tests (test_lowercase_class_name_still_resolves, test_fully_qualified_scope_resolves_by_last_segment, test_method_name_case_insensitivity) plus the existing suite: 6047 passed, 0 failed.

@graphify-labs graphify-labs Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Resolves PHP Class::method(), self::/static::/parent:: static calls to the receiver class's actual method node via the new _resolve_php_member_calls resolver, replacing the old behavior where scoped_call_expression was treated as a bare function call named after the scope and bound to the class definition instead of the method. The engine now captures the method as the callee and the scope as the receiver and flags it a member call, so binding happens with EXTRACTED confidence rather than the import-gated INFERRED path that stalled same-namespace calls. Dynamic $var::method() scopes and ambiguous class/method matches are left unresolved rather than guessed, and unqualified receivers with no matching class are parked as unresolved.

Worth a look

  • PHP static calls resolve by short class name across namespaces — graphify/extract.py:4594 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • PHP static resolver ignores namespaces for fully-qualified receivers — graphify/extract.py:4595 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • parent:: resolution requires exactly one inherits edge but PHP interfaces also produce inherits edges — 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
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 2462 functions depend on the 525 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 713 callers, 48 callees
  • new: _rebuild_code() — 144 callers, 55 callees
  • new: _extract_generic() — 18 callers, 29 callees
  • new: extract_js() — 87 callers, 4 callees
  • new: extract_xaml() — 19 callers, 17 callees
  • new: main() — 98 callers, 3 callees
  • new: dispatch_command() — 2 callers, 125 callees
  • new: extract_objc() — 27 callers, 9 callees
  • …and 44 more — each is listed as a finding

Verification — 2462 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: 2287 function(s) in the blast radius were not formally verified this run

Test selection

Test selection

129 of 303 test file(s) selected (43%) via static blast radius.

  • tests/test_astro_extraction.py — impact
  • tests/test_astro_import_ids.py — impact
  • tests/test_build.py — impact
  • tests/test_builtin_global_type_refs.py — impact
  • tests/test_case_sensitive_resolution.py — impact
  • tests/test_cjs_module_extension.py — impact
  • tests/test_cobol_extractor.py — impact
  • tests/test_cpp_nested_and_cli.py — impact
  • tests/test_cpp_objc_cross_file_calls.py — impact
  • tests/test_cross_extension_reexport_self_cycle.py — impact
  • tests/test_cross_language_call_resolution.py — impact
  • tests/test_cross_repo_external_call_guards.py — impact
  • tests/test_cross_repo_member_calls.py — impact
  • tests/test_csharp_call_site_generic_args.py — impact
  • tests/test_csharp_enum_members.py — impact
  • tests/test_csharp_field_generic_args.py — impact
  • tests/test_csharp_generic_callsites.py — impact
  • tests/test_csharp_interface_dispatch.py — impact
  • tests/test_csharp_member_calls.py — impact
  • tests/test_csharp_member_nodes.py — impact
  • tests/test_csharp_object_creation.py — impact
  • tests/test_csharp_partial_classes.py — impact
  • tests/test_csharp_type_resolution.py — impact
  • tests/test_definition_file_portability.py — impact
  • tests/test_detect.py — impact
  • tests/test_dotnet.py — impact
  • tests/test_duplicate_annotation_edges.py — impact
  • tests/test_elixir_import_resolution.py — impact
  • tests/test_erlang_extractor.py — impact
  • tests/test_extract.py — impact
  • tests/test_extract_cache_location.py — impact
  • tests/test_extract_php_closures.py — impact
  • tests/test_file_label_disambiguation.py — impact
  • tests/test_file_node_id_spec.py — impact
  • tests/test_forwarding_review_findings.py — impact
  • tests/test_go_builtin_call_targets.py — impact
  • tests/test_go_import_repoint.py — impact
  • tests/test_go_interface_methods.py — impact
  • tests/test_go_qualified_resolution.py — impact
  • tests/test_import_extension_resolution.py — impact
  • tests/test_import_self_loops.py — impact
  • tests/test_imported_export_forwarding.py — impact
  • tests/test_incremental.py — impact
  • tests/test_indirect_call_arrow_single_param_shadow.py — impact
  • tests/test_indirect_call_block_scoped_shadow.py — impact
  • tests/test_indirect_call_catch_binding_shadow.py — impact
  • tests/test_indirect_call_external_import_shadow.py — impact
  • tests/test_indirect_call_for_of_binding_shadow.py — impact
  • tests/test_indirect_call_function_expression_shadow.py — impact
  • tests/test_indirect_call_nested_closure_shadow.py — impact
  • … and 79 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.

Docs that may be stale (advisory)

…and 10 more.

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: no capturable inputs from the test suite; property tier: parameter `config` is annotated `LanguageConfig` — 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).

Comment thread graphify/extract.py
@safishamsi

Copy link
Copy Markdown
Collaborator

Thanks for this @rikurunico — the diagnosis is right and the resolver design (mirroring _resolve_java_member_calls, registered through LanguageResolver, PHP-scoped) is good. The headline Helper::sanitize() cross-file case genuinely works. I'd like one change before it lands, because right now only uppercase-receiver scoped calls actually reach your new resolver.

The engine's defer guard (engine.py ~6445) only routes a call into raw_calls when member_receiver[:1].isupper() (or is_this_field_call/_csharp_defer). So:

  • Helper::sanitize() -> H is upper -> deferred -> your resolver. Works.
  • self::, static::, parent::, $var::, and a lowercase-first class -> fall through to the pre-existing bare-name path (tgt_nid = label_to_nid.get(callee_name)), which is per-file and last-write-wins. So your self/static/parent logic is effectively dead code for the common single-file case, and a bare-name collision can mis-bind or fabricate.

The tests pass only because each fixture has a single node with the target label, so the bare-name path happens to hit the right one. Two concrete gaps this hides:

  • class A { function build(){ return self::helper(); } } class B { function helper(){} } — self::helper() binds A::build -> B::helper via bare-name (should be unresolved; A has no helper).
  • test_dynamic_scope_produces_no_phantom_edge looks like the $-guard works, but the resolver never runs there; add a same-file same-name method and $cls::save() will mis-bind.

Suggested fix: since the engine already knows the call is a scoped_call_expression, set a flag (e.g. is_scoped_call) in the PHP branch and add it to the defer condition at ~6445 so every :: call routes to the resolver regardless of receiver casing/sigil (force tgt_nid = None for scoped calls). Your resolver already handles self/static/parent/$-guard/case-insensitivity/FQN correctly, so it becomes the single source of truth. Then tighten the self::/$var:: fixtures to include a same-named decoy method in a different class so they actually prove enclosing-class scoping and no-fabrication.

Everything else (parent:: via a single inherits edge with graceful degrade, cross-file singleton resolution, determinism, no new incremental marker needed) checks out. Happy to re-review once scoped calls always go through the resolver. Marking changes-requested.

…ppercase receivers

engine.py's defer guard only deferred a scoped_call_expression into raw_calls
when member_receiver[:1].isupper() (or is_this_field_call/_csharp_defer), so
self::, static::, parent::, $var::, and lowercase-first class receivers fell
through to the pre-existing per-file bare-name path instead of reaching
_resolve_php_member_calls. That path is last-write-wins across the whole file
and can mis-bind a same-named method on an unrelated class.

Tag scoped_call_expression with is_scoped_call and add it to the defer
condition unconditionally, so every `::` call is deferred to raw_calls and
resolved by the dedicated resolver regardless of receiver casing or sigil.

Also tighten the self::/static:: and $var:: fixtures with a same-named decoy
method on another class, so they actually prove enclosing-class scoping
and no-fabrication instead of passing on a bare-name coincidence.

Addresses review feedback on PR Graphify-Labs#3874.
@rikurunico

Copy link
Copy Markdown
Author

Pushed the fix @safishamsi flagged: tagged `scoped_call_expression` with `is_scoped_call` and added it unconditionally to the defer condition in `engine.py`, so every `::` call (self/static/parent/$var/lowercase-class) now routes to `_resolve_php_member_calls` instead of only uppercase receivers. Also tightened the self::/static:: and $var:: fixtures with a same-named decoy method on another class, plus a new test proving `self::` doesn't fall back to a bare-name match. All PHP tests (56 passed, 4 skipped) still green.

@graphify-labs graphify-labs Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Resolves PHP Class::method(), self::/static::, and parent:: static calls to the receiver class's actual method node with EXTRACTED confidence via a new php_member_calls resolver, replacing the old behaviour where scoped_call_expression bound the callee to the scope text and the cross-file bare-name pass matched it to the class definition itself at INFERRED confidence. The engine now captures the method name as the callee and the scope as the receiver, flags scoped calls as member calls, and defers them so the resolver can bind them. parent:: resolves only through a single inherits edge, $var::method() dynamic scopes are left unresolved, and an unmatched named class parks the call rather than guessing.

Worth a look

  • Fully-qualified PHP receivers are collapsed to basename and become ambiguous — graphify/extract.py:4601 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Inherited static methods are not resolved for self/static PHP calls — 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
  • Uppercase .PHP files are skipped by the PHP member-call resolver — graphify/extract.py:4635 · 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 — 2465 functions depend on the 528 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 713 callers, 48 callees
  • new: _rebuild_code() — 144 callers, 55 callees
  • new: _extract_generic() — 18 callers, 29 callees
  • new: extract_js() — 87 callers, 4 callees
  • new: extract_xaml() — 19 callers, 17 callees
  • new: main() — 98 callers, 3 callees
  • new: dispatch_command() — 2 callers, 125 callees
  • new: extract_objc() — 27 callers, 9 callees
  • …and 44 more — each is listed as a finding

Verification — 2465 functions in the blast radius were not formally verified this run (proofs are advisory here).

Health delta baseline: last indexed commit 4df8d4d (diverged from this PR's base — delta is approximate).

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: 2290 function(s) in the blast radius were not formally verified this run

Test selection

Test selection

129 of 303 test file(s) selected (43%) via static blast radius.

  • tests/test_astro_extraction.py — impact
  • tests/test_astro_import_ids.py — impact
  • tests/test_build.py — impact
  • tests/test_builtin_global_type_refs.py — impact
  • tests/test_case_sensitive_resolution.py — impact
  • tests/test_cjs_module_extension.py — impact
  • tests/test_cobol_extractor.py — impact
  • tests/test_cpp_nested_and_cli.py — impact
  • tests/test_cpp_objc_cross_file_calls.py — impact
  • tests/test_cross_extension_reexport_self_cycle.py — impact
  • tests/test_cross_language_call_resolution.py — impact
  • tests/test_cross_repo_external_call_guards.py — impact
  • tests/test_cross_repo_member_calls.py — impact
  • tests/test_csharp_call_site_generic_args.py — impact
  • tests/test_csharp_enum_members.py — impact
  • tests/test_csharp_field_generic_args.py — impact
  • tests/test_csharp_generic_callsites.py — impact
  • tests/test_csharp_interface_dispatch.py — impact
  • tests/test_csharp_member_calls.py — impact
  • tests/test_csharp_member_nodes.py — impact
  • tests/test_csharp_object_creation.py — impact
  • tests/test_csharp_partial_classes.py — impact
  • tests/test_csharp_type_resolution.py — impact
  • tests/test_definition_file_portability.py — impact
  • tests/test_detect.py — impact
  • tests/test_dotnet.py — impact
  • tests/test_duplicate_annotation_edges.py — impact
  • tests/test_elixir_import_resolution.py — impact
  • tests/test_erlang_extractor.py — impact
  • tests/test_extract.py — impact
  • tests/test_extract_cache_location.py — impact
  • tests/test_extract_php_closures.py — impact
  • tests/test_file_label_disambiguation.py — impact
  • tests/test_file_node_id_spec.py — impact
  • tests/test_forwarding_review_findings.py — impact
  • tests/test_go_builtin_call_targets.py — impact
  • tests/test_go_import_repoint.py — impact
  • tests/test_go_interface_methods.py — impact
  • tests/test_go_qualified_resolution.py — impact
  • tests/test_import_extension_resolution.py — impact
  • tests/test_import_self_loops.py — impact
  • tests/test_imported_export_forwarding.py — impact
  • tests/test_incremental.py — impact
  • tests/test_indirect_call_arrow_single_param_shadow.py — impact
  • tests/test_indirect_call_block_scoped_shadow.py — impact
  • tests/test_indirect_call_catch_binding_shadow.py — impact
  • tests/test_indirect_call_external_import_shadow.py — impact
  • tests/test_indirect_call_for_of_binding_shadow.py — impact
  • tests/test_indirect_call_function_expression_shadow.py — impact
  • tests/test_indirect_call_nested_closure_shadow.py — impact
  • … and 79 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.

Docs that may be stale (advisory)

…and 10 more.

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: no capturable inputs from the test suite; property tier: parameter `config` is annotated `LanguageConfig` — 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).

Comment thread graphify/extract.py
})


def _resolve_java_member_calls(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression — _resolve_java_member_calls()

fans out to 6 callees (efferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

False positive — _resolve_java_member_calls wasn't touched by this PR at all (it's the pre-existing Java resolver this PHP fix mirrors in its docstring reference only). The 6-callee count is just where it happened to land in the diff context; no lines inside that function changed. Not applicable here.

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.

PHP Class::method() static calls without an explicit 'use' import stay INFERRED instead of EXTRACTED

2 participants