How code-review-graph Resolves Cross-Language Dependencies and Imports
The code-review-graph system resolves cross-language dependencies by normalizing import statements through language-specific tokenization and querying a SQLite-backed graph database to match symbols across language boundaries.
The open-source tool code-review-graph (available at tirth8205/code-review-graph) constructs a unified dependency graph across heterogeneous codebases. Understanding how it handles cross-language dependencies and imports requires examining the scoped resolution engine that maps imports from one language to concrete symbols in another while maintaining strict language boundaries.
Language-Agnostic Import Resolution
The core resolution logic lives in code_review_graph/scoped_resolver.py. This module implements a multi-phase pipeline that transforms raw import statements into resolvable graph queries.
Token Normalization with _fold
The resolver first splits import targets into path segments using language-specific separators. The _fold helper function normalizes tokens by applying case-folding rules appropriate to each language—lower-casing segments for case-insensitive languages like PHP while preserving case for Rust or C#. Normalization uses separators such as . (Python), \ (PHP), and / (JavaScript/TypeScript) to break import paths into comparable token sequences.
Safe Suffix Validation
Before querying the database, the resolver validates imports against _safe_import_suffixes to eliminate ambiguous partial matches. Each language maintains distinct rules: Rust requires at least two path segments, PHP treats the entire path case-insensitively, and C# follows namespace conventions. This whitelist ensures that import foo in Python does not spuriously match a Rust module named foo when insufficient context exists.
SQLite Graph Queries
The resolver loads language metadata by executing SELECT DISTINCT file_path, language FROM nodes against the SQLite graph database. It constructs a method map keyed by the tuple (language, class_key, method_key), then queries the nodes table for candidates where qualified_name and language match the normalized import tokens.
Cross-Language Matching Strategy
Cross-language resolution relies on composite keys that include the source language identifier. Because the method map is keyed by language, a PHP class named User never resolves to a Rust type named User. The resolver performs final candidate filtering by comparing folded token sequences:
_fold(left, language) == _fold(right, language)
When multiple candidates survive filtering, the resolver applies a language-matching preference hierarchy, selecting the candidate whose file language matches the import statement's source language before falling back to best-effort suffix matching.
Key Source Files and Architecture
| File | Purpose |
|---|---|
code_review_graph/scoped_resolver.py |
Core resolution engine implementing _fold, suffix validation, and database lookups. |
code_review_graph/parser.py |
Populates the graph with IMPORTS_FROM edges using tree-sitter parsers. |
code_review_graph/graph.py |
Wraps the SQLite database, exposing the nodes table and edge creation methods. |
code_review_graph/python_resolver.py |
Supplies Python-specific import resolution hints to the scoped resolver. |
code_review_graph/tsconfig_resolver.py |
Handles TypeScript path aliasing by parsing tsconfig.json mappings. |
Practical Resolution Examples
The following example demonstrates parsing a mixed-language project and querying cross-language import edges:
from code_review_graph.parser import CodeParser
from code_review_graph.graph import GraphStore
# Initialize the parser and graph store
parser = CodeParser()
graph = GraphStore()
# Parse a directory containing Python, JavaScript, and PHP files
parser.parse_path("/path/to/project", graph)
# Retrieve cross-language import edges from a Python file
python_file = "/path/to/project/app/main.py"
imports = graph.edges_from(python_file, kind="IMPORTS_FROM")
for edge in imports:
print(f"{edge.source} -> {edge.target} ({edge.language})")
Alternatively, invoke the resolution pipeline via CLI:
python -m code_review_graph.main /path/to/project --resolve-imports
Summary
- Normalization pipeline: The
_foldfunction inscoped_resolver.pytokenizes imports using language-specific separators and case rules. - Language isolation: Composite keys
(language, class_key, method_key)prevent cross-language symbol collision. - Database integration: Resolution queries the SQLite
nodestable forqualified_nameandlanguagematches. - Multi-language support: Specialized resolvers for Python, TypeScript, PHP, Rust, and C# feed language-specific rules into the generic engine.
Frequently Asked Questions
How does code-review-graph prevent false matches between languages with similar naming conventions?
The system isolates symbols by including the source language as a primary key component in the method map. When comparing candidates, the resolver executes _fold(left, language) == _fold(right, language), ensuring that case-folding and tokenization rules apply within a single language context, preventing a PHP class from resolving to a Rust struct of the same name.
Which database table stores the language metadata for import resolution?
The nodes table stores language metadata in the language column alongside file_path and qualified_name. The resolver queries this table with SELECT DISTINCT file_path, language FROM nodes to build the initial language index before resolving import paths.
What languages receive special handling in the safe suffix validation logic?
Rust, C#, and PHP receive explicit handling in the _safe_import_suffixes logic. Rust imports must contain at least two segments, PHP paths undergo case-insensitive normalization, and C# follows namespace-specific validation rules to ensure valid cross-references.
Can the resolver handle TypeScript path aliases defined in tsconfig.json?
Yes. The code_review_graph/tsconfig_resolver.py module parses tsconfig.json files to extract path mappings and base URL configurations. These mappings feed into the scoped resolver, allowing imports like import { foo } from '@/utils' to resolve correctly to their physical file locations across language boundaries.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →