How code-review-graph Extracts CALLS, IMPORTS_FROM, and INHERITS Edges

The code-review-graph parser extracts CALLS, IMPORTS_FROM, and INHERITS edges by traversing language-specific ASTs using dedicated resolvers that emit typed Edge objects when they encounter call expressions, import statements, and class inheritance declarations.

The open-source code-review-graph repository provides a language-agnostic tool that transforms source code into a queryable graph structure. By walking the abstract syntax tree (AST) of every source file, the parser identifies three fundamental relationship types that power downstream analysis like visualisation, blast-radius detection, and dead-code identification.

Parser Architecture Overview

The extraction pipeline in code_review_graph/parser.py coordinates a five-step process to build the graph:

  1. File discovery – Loads every file matching supported extensions (Python, TypeScript, Java, C++, etc.).
  2. Language dispatch – Selects a resolver module based on file extension (e.g., jedi_resolver for Python, ts_resolver for TypeScript, java_resolver for Java).
  3. AST construction – Each resolver builds an AST using the appropriate library (Jedi for Python, TypeScript-compiler-API for TS, JavaParser for Java).
  4. Visitor traversal – A visitor walks the tree and invokes edge-emission helpers (_emit_call, _emit_import, _emit_inherit).
  5. Edge emission – These helpers append an Edge object to the in-memory graph defined in code_review_graph/graph.py, which persists after all files are processed.

How Each Edge Type Is Extracted

CALLS Edges

CALLS edges represent function or method invocations. In each language-specific resolver, the visitor looks for call-expression nodes such as Python's Call, TypeScript's NewExpression, or Java's MethodInvocation.

When a resolver successfully identifies a callee, it instantiates Edge(kind="CALLS", source=<caller-qualified>, target=<callee-qualified>). For example, in code_review_graph/jedi_resolver.py, the resolver uses Jedi's name resolution to determine the fully qualified name of both the caller and the target function before emitting the edge.

IMPORTS_FROM Edges

IMPORTS_FROM edges model dependency relationships between files or modules. Resolvers inspect import-statement nodes including Python's Import and ImportFrom, JavaScript's import ... from ..., Java's import declarations, or C++ include directives.

The edge connects the importer file to the module or file supplying the symbols: Edge(kind="IMPORTS_FROM", source=<importer-file>, target=<imported-module-or-file>). The target represents the source of the imported symbols rather than individual imported names, creating a file-to-file dependency link suitable for build-graph analysis.

INHERITS Edges

INHERITS edges capture class hierarchy relationships. When a resolver encounters a class definition containing base-class lists, it emits inheritance edges.

For Python, this means inspecting ClassDef.bases; for Java, checking extends and implements clauses; for TypeScript, examining heritageClauses. The resulting Edge(kind="INHERITS", source=<child-class-qualified>, target=<parent-class-qualified>) connects the derived class to its parent or implemented interface.

Language-Specific Resolvers

The parser delegates AST traversal to modular resolvers located in the code_review_graph/ directory:

  • jedi_resolver.py – Handles Python source files using the Jedi library to resolve names and extract the three edge types from Python's AST.
  • ts_resolver.py – Processes TypeScript and JavaScript files via the TypeScript-compiler-API, identifying call expressions, ES6 modules, and class heritage.
  • java_resolver.py – Extracts edges from Java source using JavaParser, handling method invocations, import statements, and class inheritance declarations.

Each resolver implements the same visitor pattern but uses language-specific node types to identify relationships, ensuring the graph model remains uniform across languages.

Querying Extracted Edges

After parsing completes, you can query the in-memory graph using the API defined in code_review_graph/graph.py:

from code_review_graph import parse, EdgeKind

# Parse a repository (or a single file) and obtain the full graph

graph = parse(root_path="/path/to/repo")

# 1️⃣ All CALLS edges

calls = graph.edges_of_kind(EdgeKind.CALLS)
print(f"Found {len(calls)} CALLS relationships")
for e in calls[:5]:
    print(f"{e.source} → {e.target}")

# 2️⃣ All IMPORTS_FROM edges

imports = graph.edges_of_kind(EdgeKind.IMPORTS_FROM)
print(f"Found {len(imports)} IMPORTS_FROM relationships")

# 3️⃣ All INHERITS edges

inherits = graph.edges_of_kind(EdgeKind.INHERITS)
print(f"Found {len(inherits)} INHERITS relationships")

The Edge dataclass provides a consistent interface for accessing source, target, and kind attributes regardless of which language resolver created the edge.

Summary

  • The parser in code_review_graph/parser.py orchestrates AST parsing across multiple languages using specialized resolver modules.
  • CALLS edges are emitted when resolvers identify function or method call expressions and successfully resolve the callee's qualified name.
  • IMPORTS_FROM edges connect importer files to their dependency sources by analyzing import statements and module declarations.
  • INHERITS edges represent class hierarchy relationships extracted from base-class lists in class definitions.
  • All edges use the uniform Edge dataclass defined in code_review_graph/graph.py, enabling downstream tools to treat relationships interchangeably across Python, TypeScript, Java, and other supported languages.

Frequently Asked Questions

How does code-review-graph handle name resolution for CALLS edges in Python?

The jedi_resolver.py module leverages the Jedi library's static analysis capabilities to resolve symbol names across the entire project. When the visitor encounters a Call node, Jedi performs name lookup to determine the fully qualified path of the target function, ensuring edges point to the actual definition rather than just the local alias or import name.

Can the parser extract edges from languages not explicitly supported?

The architecture supports adding new resolvers by implementing the visitor pattern for additional languages. You would create a new resolver module (e.g., cpp_resolver.py) that constructs an AST using an appropriate parser (like Tree-sitter or Clang), then implements _emit_call, _emit_import, and _emit_inherit helpers to populate Edge objects with the same three relationship types.

What is the difference between IMPORTS_FROM and CALLS edges?

IMPORTS_FROM edges represent static dependencies between files or modules at the import level, while CALLS edges represent dynamic execution flow between specific functions or methods. A file may import another module (IMPORTS_FROM) without necessarily calling any functions from it, or it may call functions from imported modules (requiring both an IMPORTS_FROM edge to the file and a CALLS edge to the specific function).

Where are the extracted edges stored after parsing?

Edges are stored in memory using the Edge dataclass defined in code_review_graph/graph.py and managed by the graph object returned by parse(). The graph persists as a Python object during runtime, allowing immediate querying via methods like edges_of_kind(). Persistence to disk or export to graph databases requires additional serialization logic not shown in the core parser implementation.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →