Skip to content

fix: resolve CALLS edge targets to qualified names at parse time#19

Closed
madinal wants to merge 3 commits intotirth8205:mainfrom
madinal:fix/resolve-calls-edge-targets
Closed

fix: resolve CALLS edge targets to qualified names at parse time#19
madinal wants to merge 3 commits intotirth8205:mainfrom
madinal:fix/resolve-calls-edge-targets

Conversation

@madinal
Copy link
Contributor

@madinal madinal commented Mar 17, 2026

Summary

CALLS edges were broken in three ways, causing callers_of, callees_of, impact radius, and dependent-file queries to silently return empty or incomplete results:

  1. Bare target names — CALLS edges stored bare function names (func_name) while all graph queries match by qualified name (file.py::func_name). Added parse-time resolution using import mappings and cached module-to-file-path lookup (Python + JS/TS). Unresolvable targets (method calls, builtins) gracefully fall back to bare names.

  2. Decorated functions invisible to same-file resolution_collect_file_scope only checked direct root.children types, missing functions wrapped in decorated_definition AST nodes (any function with a decorator). These were absent from defined_names, so same-file call resolution silently failed for them.

  3. Multiple call sites collapsed into one edgeupsert_edge() deduped on (kind, source, target, file_path) without line, so multiple calls to the same function from the same caller were collapsed into a single edge. Adding line to the lookup preserves each distinct call site.

Test plan

  • test_calls_edge_same_file_resolution — same-file calls are qualified
  • test_calls_edge_cross_file_resolution — imported call targets resolve to the defining file
  • test_unresolved_calls_stay_bare — method calls remain bare (no false resolution)
  • test_calls_edge_decorated_function_resolution — decorated functions resolve as same-file call targets
  • test_multiple_calls_to_same_function — multiple calls on different lines produce distinct edges
  • test_upsert_edge_preserves_multiple_call_sites — DB layer stores both edges
  • All existing tests pass unchanged

🤖 Generated with Claude Code

madinal and others added 3 commits March 17, 2026 16:38
CALLS edges stored bare function names as targets (e.g. `func_name`)
while all graph queries match by qualified name (`file.py::func_name`),
causing callers_of, callees_of, impact radius, and find_dependents to
silently return empty results for call relationships.

Pre-scan each file's imports and definitions before the AST walk, then
resolve call targets using import mappings and language-aware
module-to-file-path resolution (cached across files). Unresolvable
targets (method calls, builtins) gracefully fall back to bare names.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Decorated functions (e.g. @decorator above a def) produce a
decorated_definition AST node that wraps the function_definition.
_collect_file_scope only checked direct root.children types against
func_types/class_types, so decorated functions were missing from
defined_names and same-file call resolution silently failed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
upsert_edge() deduped on (kind, source, target, file_path) without
line, so multiple calls to the same function from the same caller were
collapsed into a single edge. Adding line to the lookup preserves each
distinct call site.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@madinal madinal force-pushed the fix/resolve-calls-edge-targets branch from e470b76 to a36012c Compare March 17, 2026 20:14
Copy link
Owner

@tirth8205 tirth8205 left a comment

Choose a reason for hiding this comment

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

Very useful improvement — qualified names for CALLS edges + handling of decorators is great.
Critical fix needed:
• Current module file cache key (language:module) ignores caller directory → relative imports from different folders will collide. Include file_path parent in the cache key.
Once fixed this is merge-ready.
Thanks for the PR — planning to merge in the next few days!

tirth8205 added a commit that referenced this pull request Mar 20, 2026
Squash-merge of PR #19 by @madinal with fixup:
- Include caller directory in module file cache key to prevent
  relative import collisions across different directories

Closes #20

Co-Authored-By: madinal <madinal@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tirth8205
Copy link
Owner

Merged to main via manual squash-merge with cache key fix. Thank you @madinal!

@tirth8205 tirth8205 closed this Mar 20, 2026
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.

2 participants