Skip to content

AST extraction: cross-file call resolution is limited to Python imports only #298

Closed

Description

@cyxer000

Problem

walk_calls in extract.py resolves call targets using a file-local label_to_nid dict (L948-949):

tgt_nid = label_to_nid.get(callee_name.lower())

This means any call to a function/class defined in another file is silently dropped. Cross-file relationships — which are theoretically fully resolvable from AST — are almost entirely lost.

Current state

The only cross-file mechanism is _resolve_cross_file_imports() (L2039), which has significant limitations:

Limitation Detail
Python-only L2652: # Python only - uses Python parser internally
Import-level only Only resolves from .module import Name, does not trace actual call sites
Coarse granularity Connects all classes in the importing file to the imported entity, regardless of whether they actually use it
Stem-only module matching Matches by filename stem (L2083), ignores package paths like foo.bar.baz

All other languages (JS/TS, Go, Rust, Java, C/C++, etc.) have zero cross-file resolution — calls to imported symbols produce no edges at all.

Impact

For any multi-file codebase, the AST call graph is effectively incomplete. The semantic subagents (LLM) partially compensate, but:

  • They are non-deterministic and token-expensive
  • They miss structural relationships that AST could extract for free
  • Code-only corpora skip semantic extraction entirely, so cross-file calls are completely lost

Proposed approach

Build a global symbol table in a two-pass architecture:

  1. Pass 1 (existing): Per-file extraction — collect all nodes (label_to_nid) and raw call names
  2. Pass 2 (new): Merge all per-file symbol tables into a global label_to_nid, then re-resolve unresolved calls against the global table

This is the minimal change — walk_calls already extracts the callee name, it just fails to find a target because the lookup scope is too narrow. A global lookup table would immediately resolve cross-file calls for all languages without language-specific import handlers.

Additional improvements (optional, can be separate PRs)

  • Per-language import resolvers to map import X from './foo' to the actual file's symbols
  • Package-path-aware module matching (not just stem)
  • Narrowing _resolve_cross_file_imports granularity to only connect entities that actually reference the import

Environment

  • graphify version: installed via pip install graphifyy
  • Python: 3.14
  • OS: Windows 11

Activity

  1. added a commit that references this issue on Apr 15, 2026
    b162ae8
  2. safishamsi commented on Apr 16, 2026

    @safishamsi
    Member

    This is an accurate description of the current limitation. The cross-file call resolution in _resolve_cross_file_imports() is Python-only and import-level only -- it does not trace actual call sites, and all other languages have zero cross-file resolution from AST.

    The reason it works this way is that full cross-file call resolution requires building a per-language scope/symbol table across the entire corpus before emitting any edges, which is a significant amount of work to get right for each language. The current implementation trades completeness for speed and simplicity.

    The semantic subagents compensate for this on mixed corpora -- the LLM can reason across files and emit cross-file edges that AST misses. For code-only corpora (no LLM), the gap is real.

    A proper fix would require:

    1. A two-pass extraction: first pass builds a corpus-wide symbol table (exported symbols per file), second pass resolves call sites against it.
    2. Per-language import resolution (each language has different import semantics).

    This is on the roadmap but there is no timeline. For now, the LLM semantic pass is the intended mechanism for cross-file relationship discovery on mixed corpora.

  3. safishamsi commented on Apr 16, 2026

    @safishamsi
    Member

    Fixed in v0.4.18. Cross-file call resolution now works for Go, Rust, Zig, PowerShell, and Elixir. Each extractor now saves unresolved callees to raw_calls, which are resolved against the global node index after all files are extracted. pip install --upgrade graphifyy

  4. added a commit that references this issue on Apr 17, 2026
  5. added a commit that references this issue on Apr 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions