Skip to content

feat: generalize unresolvable call tracking for better observability (#3665) - #3885

Open
nikhilsaxena04 wants to merge 2 commits into
Graphify-Labs:v8from
nikhilsaxena04:feat/unresolvable-calls-3665
Open

nikhilsaxena04 wants to merge 2 commits into
Graphify-Labs:v8from
nikhilsaxena04:feat/unresolvable-calls-3665

Conversation

@nikhilsaxena04

Copy link
Copy Markdown
Contributor

Pull Request for Issue #3665 (Unresolvable Call Observability)

## What does this PR do?

Fixes #3665

This PR refactors how unresolvable calls are parked in `extract.py`. It replaces the hardcoded `_park_unresolved_member_call` with a generalized `_record_unresolved_call` function. The `unresolved_calls` metadata schema now stores a structured list of dictionaries tracking `callee`, `reason`, `lang`, `receiver_type`, and `line`. It also introduces a `unresolved_calls_truncated` counter to ensure the metadata length is capped and tracked.

## Type of change

- [ ] Bug fix
- [x] New feature
- [ ] Documentation
- [ ] Tests or CI
- [x] Refactor
- [ ] Security fix

## Verification & Invariants

This change protects the invariant that parked unresolved calls don't exceed the node metadata boundaries while maintaining a detailed, schema-compliant dictionary list across all languages for observability.

- [x] **Read** the [CONTRIBUTING.md](../CONTRIBUTING.md) guide.
- [x] **Reproduced** the issue and identified the invariant.
- [x] Made the **smallest fix** necessary.
- [x] Added a **regression test** (if bug fix) or isolated boundary test.
- [x] Kept the PR description **synchronized** with the final implementation.
- [x] Documented any **limitations / unsupported cases** explicitly.

## How was this tested?

```text
.venv/bin/pytest tests/test_cross_repo_member_calls.py tests/test_cross_repo_external_call_guards.py
graphify update .

@github-actions

Copy link
Copy Markdown

Thanks for the pull request, @nikhilsaxena04. 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.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).


Graphify review — findings

Renames _park_unresolved_member_call to _record_unresolved_call, making receiver_type optional and adding a required reason tag on each parked entry so caller sites (Swift, C++, C#, Java resolvers) now record why a call was left unresolved. Deduplication now keys on reason as well, and hitting _MAX_PARKED_CALLS_PER_NODE bumps an unresolved_calls_truncated counter on the node's metadata instead of silently discarding the call.

No blocking issues surfaced. 4 lower-confidence candidates did not survive cross-model review.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 2164 functions depend on the 257 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 716 callers, 48 callees
  • new: _rebuild_code() — 146 callers, 55 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: _get_extractor() — 26 callers, 6 callees
  • new: run_pipeline() — 8 callers, 13 callees
  • …and 34 more — each is listed as a finding

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

Health delta baseline: last indexed commit 4df8d4d, 2 commit(s) behind this PR's base.

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

Test selection

Test selection

130 of 304 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_tuple_type_refs.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
  • … and 80 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.

· 42 more finding(s) on lines outside this diff (see the check run).

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.

1 participant