Skip to content

fix(extract): resolve cross-file Rust self calls for simple generic impls - #3653

Open
oleksii-tumanov wants to merge 1 commit into
Graphify-Labs:v8from
oleksii-tumanov:fix/rust-generic-self-calls
Open

oleksii-tumanov wants to merge 1 commit into
Graphify-Labs:v8from
oleksii-tumanov:fix/rust-generic-self-calls

Conversation

@oleksii-tumanov

Copy link
Copy Markdown
Contributor

Fixes #3652

Summary

  • Resolve cross-file Rust self.method() calls across simple alpha-equivalent generic inherent impl blocks.
  • Keep unsupported or ambiguous generic shapes fail-closed.
  • Preserve the required identity markers through the AST cache and incremental rebuilds.

Before and after

Before: a call in impl<U> Bucket<U> could not reach a method defined in another file's impl<T> Bucket<T> block.

After: Graphify links the call when the generic impl family has one declaration and an unambiguous method. More complex generic shapes still produce no guessed edge.

Testing

  • uv run pytest tests/ -q (5,785 passed, 14 skipped)
  • uv run ruff check graphify/cache.py graphify/cli.py graphify/extract.py graphify/extractors/rust.py graphify/watch.py tests/test_incremental.py tests/test_rust_self_member_calls.py tests/test_watch.py
  • uv run python -m tools.skillgen --check
  • uv run graphify update . --no-cluster

@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 1 advisory finding(s) below merit a look before merge.

Formal verification. 1 change(s) tested, no difference found (not proven).


Graphify review — findings

Adds generic-impl identity resolution for Rust self. member calls: _rust_simple_generic_impl_key persists a stable owner/arity marker (e.g. Bucket/1) for bare unbounded generic impls, and _resolve_rust_self_member_calls pools methods by that marker only when exactly one such declaration exists in the corpus, never falling back to bare-label pooling when marker context is absent or ambiguous. Bumps _AST_CACHE_SCHEMA to 4 and preserves the new _rust_impl_key/_rust_declaration_count markers on unchanged nodes during incremental rebuilds since they can't be reconstructed from labels.

Worth a look

  • Non-generic impl blocks reset shared impl_key to None, dropping generic identitygraphify/extractors/rust.py:452 · 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 — 2825 functions depend on the 878 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 646 callers, 45 callees
  • new: _rebuild_code() — 133 callers, 54 callees
  • new: detect() — 112 callers, 15 callees
  • new: save_semantic_cache() — 63 callers, 9 callees
  • new: load_cached() — 53 callers, 7 callees
  • new: extract_js() — 85 callers, 4 callees
  • new: extract_xaml() — 19 callers, 17 callees
  • new: file_hash() — 51 callers, 6 callees
  • …and 48 more — each is listed as a finding

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

Test selection

Test selection

156 of 286 test file(s) selected (55%) via static blast radius.

  • tests/test_affected_cli.py — impact
  • tests/test_agents_platform.py — impact
  • 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_cache.py — impact
  • tests/test_case_sensitive_resolution.py — impact
  • tests/test_charmap_encoding.py — impact
  • tests/test_chunking.py — impact
  • tests/test_cjs_module_extension.py — impact
  • tests/test_codebuddy.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_devin.py — impact
  • tests/test_dotnet.py — impact
  • tests/test_duplicate_annotation_edges.py — impact
  • tests/test_elixir_import_resolution.py — impact
  • tests/test_explain_cli.py — impact
  • tests/test_external_stub_endpoints.py — impact
  • tests/test_extract.py — impact
  • tests/test_extract_cache_location.py — impact
  • tests/test_extract_cli.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_global_add_tag_inference.py — impact
  • tests/test_go_builtin_call_targets.py — impact
  • tests/test_go_qualified_resolution.py — impact
  • tests/test_god_nodes_cli.py — impact
  • tests/test_hollow_chunks_arm_shrink_guard.py — impact
  • tests/test_hook_guard_token_match.py — impact
  • tests/test_hook_out_of_project_paths.py — impact
  • tests/test_hook_strict.py — impact
  • … and 106 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 dispatch\_command.

The verifier did not have enough to check dispatch\_command, 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: not verifiable: all 23 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly SystemExit — names the real obstacle, not a sampling gap)

No difference found (not proven): No behavior difference found in \_resolve\_rust\_self\_member\_calls (not a proof).

The verifier ran both versions of \_resolve\_rust\_self\_member\_calls on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: concolic exploration (CrossHair). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

Could not verify: Could not verify extract\_rust.

The verifier did not have enough to check extract\_rust, 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 `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_rebuild\_code.

The verifier did not have enough to check \_rebuild\_code, 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 `watch_path` is annotated `Path` — outside the synthesizable primitive/collection set

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

add_edge(func_nid, tgt, "references", line, context=ctx)

def walk(node, parent_impl_nid: str | None = None, parent_impl_type: str | None = None) -> None:
def walk(

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 regressionwalk()

fans out to 9 callees (efferent coupling).

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

safishamsi added a commit that referenced this pull request Sep 18, 2026
…the graph (#3644 follow-up)

#3644 persists HCL block attributes into node metadata, which is serialized to
graph.json verbatim and surfaced to the model over MCP (query/get_node) with no
sanitize_metadata pass. A hardcoded credential in a .tf file (password,
db_password, aws_secret_access_key, client_secret, a connection string, ...)
would leak. Redact the VALUE of any attribute whose key names a secret while
keeping the key visible, so instance_type/ami queries still work and a user can
still see THAT a secret is set. Recurses into map values so a nested secret in a
tags/connection map is redacted too.

Also notes Terraform block attributes in the _AST_CACHE_SCHEMA 4 comment (they
are newly-cached facts covered by the same bump #3653 introduced).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

Rust self calls are missing across files for simple generic impls

1 participant