Language-Specific Type Inference in code-graph-rag: Python, Rust, and Java Explained

The code-graph-rag system implements dedicated type-inference engines for Python, Rust, and Java—PythonTypeInferenceEngine, RustTypeInferenceEngine, and JavaTypeInferenceEngine—each translating Tree-Sitter AST nodes into identifier-to-type maps that power call-edge resolution and cross-language retrieval.

This article examines how code-graph-rag performs language-specific type inference across three major programming languages. The system extracts a language-agnostic code graph (CG) from repositories, then enriches it with precise type information through specialized engines that understand each language's unique type system characteristics.


Python Type Inference Engine

The Python type inference implementation resides in [codebase_rag/parsers/py/type_inference.py](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/parsers/py/type_inference.py). The PythonTypeInferenceEngine class handles variable, expression, and AST mix-ins to produce accurate type maps.

Core Inference Pipeline

The engine executes six sequential transformations (lines 88–104):

Step Method Purpose
1 _infer_parameter_types Walks function signatures, stores annotated parameter types
2 _traverse_single_pass Single-pass body traversal propagating types through assignments and attribute accesses
3 _infer_instance_attributes_from_init Extracts self.x = … assignments inside __init__ methods
4 _infer_property_return_types Follows property decorators to assign return types to virtual attributes
5 _infer_class_annotation_types Reads class-level __annotations__ dictionaries
6 _expand_chained_attribute_types Expands locals with aliasing and chained attribute resolution

The single-pass traversal avoids O(5·N) rescans, a critical performance optimization for large Python codebases.

from pathlib import Path
from codebase_rag.parsers.py.type_inference import PythonTypeInferenceEngine

engine = PythonTypeInferenceEngine(
    import_processor,         # ImportProcessor instance

    function_registry,       # FunctionRegistryTrieProtocol implementation

    Path("/my/repo"),        # repository root

    "my_project",            # project name

    ast_cache,               # ASTCacheProtocol

    queries,                 # LanguageQueries mapping

    module_qn_to_path,       # dict[str, Path]

    class_inheritance,       # dict[str, list[str]]

    simple_name_lookup,      # SimpleNameLookup

    lambda: js_type_inference_engine,  # deferred JS engine for mixed projects

)

# caller_node is a Tree-Sitter node representing the function definition

var_map = engine.build_local_variable_type_map(caller_node, "my_module")
print(var_map)   # {'cfg': 'Config', 'service': 'UserService', ...}

Rust Type Inference Engine

The Rust type inference system is defined in [codebase_rag/parsers/rs/type_inference.py](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/parsers/rs/type_inference.py). The RustTypeInferenceEngine handles Rust's ownership system, generics, and iterator chains through specialized collection structures.

Key Data Structures and Methods

Function Lines Purpose
build_local_variable_type_map 19–26 Entry point: parameters first, then body traversal for let bindings
_collect_parameters 52–66 Maps parameters to bare type names via _bare_type_name
_collect_bindings / _collect_let_binding 68–79 Processes let x: T = … and struct literal initializations
build_field_type_map 28–47 Maps struct fields to bare types for receiver dispatch
collect_element_entries 93–110 Records collection element types (e.g., Vec<Worker> → Worker)
collect_closure_param_bindings 78–89 Generates bindings for closure parameters in generic pipelines
collect_generic_bounds 68–84 Extracts trait bounds enabling trait-based method dispatch

The engine provides deref-unwrapping via _guard_inner_type and generic return-type lookup, ensuring calls like cmd.apply() bind to correct implementation methods.

from codebase_rag.parsers.rs.type_inference import RustTypeInferenceEngine

rust_engine = RustTypeInferenceEngine()
var_map = rust_engine.build_local_variable_type_map(caller_node, module_qn)

# var_map => {'cmd': 'Command', 'workers': 'Vec<Worker>', ...}

# Collect iterator element types for chain analysis

element_entries = rust_engine.collect_element_entries(fn_node)

# element_entries -> [('workers', 'Worker', 0, 150, 30), ...]

Java Type Inference Engine

The Java type inference implementation lives in [codebase_rag/parsers/java/type_inference.py](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/parsers/java/type_inference.py). The JavaTypeInferenceEngine leverages fully-qualified names (FQN) for cross-file resolution.

Three-Stage Resolution Architecture

  1. FQN lookup map construction – _build_fqn_lookup_map (lines 72–97) creates a reverse index from simple class names to fully-qualified module QNs, supporting both src/main/java/... and flat project layouts.

  2. Variable type mapping – build_variable_type_map (lines 99–111) gathers local variable types using JavaVariableAnalyzerMixin.

  3. Method call resolution – resolve_java_method_call (lines 13–22) delegates to _do_resolve_java_method_call, which uses JavaMethodResolverMixin to locate static and instance methods considering imports, inheritance, and the FQN map.

The mix-in architecture (JavaTypeResolverMixin, JavaMethodResolverMixin) encapsulates Java-specific resolution logic: type resolution respects import statements, package scopes, and inheritance hierarchies.

from pathlib import Path
from codebase_rag.parsers.java.type_inference import JavaTypeInferenceEngine

java_engine = JavaTypeInferenceEngine(
    import_processor,
    function_registry,
    Path("/my/repo"),
    "my_project",
    ast_cache,
    queries,
    module_qn_to_path,
    class_inheritance,
    simple_name_lookup,
)

var_map = java_engine.build_variable_type_map(scope_node, "com.example.service")
target = java_engine.resolve_java_method_call(
    call_node, var_map, "com.example.service", caller_qn
)

# target => ('com.example.service.UserService', 'findUser')

Integration with the Code Graph Construction Pipeline

During CG construction in [codebase_rag/graph_loader.py](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/graph_loader.py) and graph_updater.py, each parser registers its engine in the language-specific LanguageHandler registry. When traversal encounters a function node, the corresponding engine produces a local type map attached to node metadata.

This type map enables three downstream capabilities:


Summary


Frequently Asked Questions

How does the Python engine avoid expensive re-traversals?

The Python engine implements _traverse_single_pass (lines 88–104), which walks the function body once to propagate types through assignments and attribute accesses. This design eliminates O(5·N) rescans that would occur with separate passes for each inference category.

What makes Rust type inference particularly complex in code-graph-rag?

Rust's ownership system, generics, and iterator chains require specialized handling. The RustTypeInferenceEngine maintains separate collections for guard inner types, iterator element types, generic bounds, and closure parameter bindings—structures unnecessary in Python or Java due to Rust's unique type system characteristics.

How does Java cross-file type resolution work?

The Java engine constructs a fully-qualified name (FQN) lookup map via _build_fqn_lookup_map (lines 72–97). This reverse index maps simple class names to their complete module QNs, enabling resolution across both standard Maven/Gradle layouts (src/main/java/...) and flat project structures while respecting import statements and package scopes.

Can these engines handle mixed-language repositories?

Yes. The LanguageHandler registry allows each file's AST to be processed by its appropriate engine. The deferred engine pattern—seen in PythonTypeInferenceEngine's lambda: js_type_inference_engine parameter—enables cross-language linking when Python calls JavaScript or other supported languages.

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 →