Skip to content

feat(extractor+resolver): Import Graph Linking — cross-file call resolution (#13)#48

Merged
zaebee merged 5 commits into
mainfrom
feat/issue-13-import-graph-linking
Jun 7, 2026
Merged

feat(extractor+resolver): Import Graph Linking — cross-file call resolution (#13)#48
zaebee merged 5 commits into
mainfrom
feat/issue-13-import-graph-linking

Conversation

@zaebee

@zaebee zaebee commented Jun 7, 2026

Copy link
Copy Markdown
Owner

Summary

Implements Pass 2 of the resolver: per-file Local Name Map (LNM) via import_map stored in FILE node metadata.

Extractor (python_extractor.py)

  • Creates a FILE node per file with import_map in metadata
  • Parses import X, import X as Y, from X import Y [as Z]
  • Resolves relative imports (from . import base, from ..core import models) via _resolve_relative_module()
  • Wildcards (from X import *) are safely skipped (no crash)
  • Emits structural IMPORTS edges from FILE node to base module

Resolver (resolver/engine.py)

  • _file_imports index: normalized_file_path → {alias: target_fqn} (from FILE node metadata)
  • _suffix_map index: suffix → [node_ids] — O(1) lookup for src/-layout prefix mismatch
  • _map_to_node_fqn(): handles both mismatch directions (import has extra prefix OR node has extra layout prefix)
  • _resolve_global_call consults import map before global symbol index

Impact

72.8% → 58.9% unresolved on this project's own graph — 492 newly resolved edges.

Top unresolved internal calls (pipeline.run, MermaidCompiler.compile) now resolve correctly via import map.

Remaining unresolved are builtins (str, len, next) and instance method calls on local variables (runner.invoke, self._conn.execute) — those need Type Propagation (future issue).

Test plan

  • FILE node created with import_map in metadata
  • import os{"os": "os"}
  • import json as js{"js": "json"}
  • from cgis.pipeline import X{"X": "cgis.pipeline.X"}
  • from cgis.pipeline import X as Y{"Y": "cgis.pipeline.X"}
  • from typing import Dict, List → both mapped
  • from . import base relative (1 dot) → correct FQN
  • from ..core import models relative (2 dots) → correct FQN
  • from module import * → no crash, IMPORTS edge emitted
  • Resolver: direct import resolves call
  • Resolver: aliased import resolves call
  • Resolver: mod.func() module-prefixed call resolves
  • Resolver: IMPORTS structural edge emitted
  • Resolver: src/ layout normalization
  • Resolver: wildcard import stays unresolved (safe)
  • All 134 tests pass, mypy strict clean

Closes #13

🤖 Generated with Claude Code

…lution (#13)

Closes #13.

## Extractor changes (python_extractor.py)
- Creates a FILE node per file with import_map in metadata
- Parses import_statement (import X, import X as Y)
- Parses import_from_statement (from X import Y [as Z], relative imports)
- Handles relative imports (leading dots) via _resolve_relative_module()
- Wildcards (from X import *) are safely skipped
- Emits structural IMPORTS edges from FILE node to base module

## Resolver changes (engine.py)
- _file_imports index: normalized file_path → {alias: target_fqn}
- _suffix_map index: suffix → [node_ids] (O(1) src/-layout lookup)
- _map_to_node_fqn(): handles two prefix-mismatch directions:
    1. Import has extra prefix: cgis.resolver.X → resolver.X (strip from import)
    2. Node has extra layout prefix: cgis.pipeline.X → src.cgis.pipeline.X
- _resolve_global_call: import map consulted before global symbol index

## Impact
- 72.8% → 58.9% unresolved edges on this project's own graph
- 492 edges newly resolved via import-based linking
- Top previously-unresolved internal calls (pipeline.run, MermaidCompiler.compile)
  now link correctly via import map

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request implements import graph extraction and linking for Python files, adding support for parsing absolute, aliased, and relative imports, as well as resolving global calls using file-level import maps. The review feedback highlights three key issues: parenthesized multi-line imports are currently ignored because the extractor does not descend into import_list nodes; non-aliased dotted imports (e.g., import a.b.c) are mapped incorrectly, leading to broken module-prefixed call resolutions; and the FQN mapping logic in the resolver should check the suffix map before stripping leading segments to avoid incorrect matches.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/cgis/extractors/python_extractor.py Outdated
Comment thread src/cgis/extractors/python_extractor.py Outdated
Comment thread src/cgis/resolver/engine.py Outdated
zaebee and others added 2 commits June 7, 2026 09:51
Refactor _process_import_from_statement into three focused helpers
(_parse_relative_import, _collect_imported_symbols) to bring cyclomatic
complexity from 14 to ~8. Move all inline imports to module level in
test_pipeline.py and test_resolver.py. Shorten three over-length docstrings
in test_python_extractor.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…map, suffix priority

- Flatten `import_list` node in _collect_imported_symbols to support
  `from X import (A, B)` parenthesized style (#1)
- Map non-aliased dotted imports to local name: `import a.b.c` → {"a": "a"}
  so resolver reconstructs `a.b.c.foo()` correctly, not `a.b.c.b.c.foo()` (#2)
- Check _suffix_map before strip-leading-segments in _map_to_node_fqn to
  prevent ambiguous strip from overriding precise src/-layout match (#3)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@zaebee

zaebee commented Jun 7, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request implements import graph linking to improve call graph resolution. It updates the Python extractor to parse import statements, generate FILE nodes containing an import_map, and emit IMPORTS edges. The resolver engine is enhanced to prioritize these import maps and utilize a suffix map to handle layout prefix mismatches (such as src/). Feedback focuses on two main issues: preventing a potential TypeError in the resolver if the import_map metadata is explicitly None, and robustly calculating leading dots in relative imports by using byte offsets instead of counting tree-sitter child nodes.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/cgis/resolver/engine.py Outdated
Comment thread src/cgis/extractors/python_extractor.py Outdated
zaebee and others added 2 commits June 7, 2026 10:03
… dots byte-len

- Extract nested ternary in _resolve_global_call into if/elif/else — reduces
  cognitive complexity from 16 to 15 and fixes SonarQube nested-conditional alert
- Guard import_map retrieval with `or {}` to handle explicit None in metadata
- Count relative-import leading dots via byte length (sub.end_byte - sub.start_byte)
  instead of iterating sub.children, which can be empty for terminal leaf nodes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…complexity

Moves direct-import and module-prefixed-call resolution out of
_resolve_global_call into a dedicated helper, bringing _resolve_global_call's
cognitive complexity from 16 to ~8 (well within SonarQube's 15 limit).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Jun 7, 2026

Copy link
Copy Markdown

@zaebee zaebee merged commit cfca9f3 into main Jun 7, 2026
1 check passed
@zaebee zaebee deleted the feat/issue-13-import-graph-linking branch June 7, 2026 10:08
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.

feat: Import Graph Linking (Pass 2) — cross-file call resolution

1 participant