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

> Discover language-specific type inference in code-graph-rag for Python Rust and Java. Understand how AST nodes map to types for robust call-edge resolution and cross-language retrieval.

- Repository: [Vitali Avagyan/code-graph-rag](https://github.com/vitali87/code-graph-rag)
- Tags: deep-dive
- Published: 2026-08-16

---

**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)](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.

```python
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)](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.

```python
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)](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.

```python
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)](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/graph_loader.py) and [`graph_updater.py`](https://github.com/vitali87/code-graph-rag/blob/main/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:

- **Call-edge resolution** – selecting correct callees based on inferred receiver types
- **Cross-language linking** – Python calls to Rust-implemented interfaces resolve when Rust engine supplies method signatures
- **RAG retrieval enhancement** – [[`codebase_rag/embedder.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/embedder.py)](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/embedder.py) uses inferred types for semantic search relevance scoring

---

## Summary

- **Python engine** (`PythonTypeInferenceEngine`): Six-phase pipeline with single-pass traversal, handling parameters, instance attributes, properties, class annotations, and chained attributes in [[`codebase_rag/parsers/py/type_inference.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/parsers/py/type_inference.py)](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/parsers/py/type_inference.py)

- **Rust engine** (`RustTypeInferenceEngine`): Specialized collection for bindings, fields, guard inner types, iterator elements, generic bounds, and closure parameters in [[`codebase_rag/parsers/rs/type_inference.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/parsers/rs/type_inference.py)](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/parsers/rs/type_inference.py)

- **Java engine** (`JavaTypeInferenceEngine`): FQN-based cross-file resolution with variable analysis and method resolution mix-ins in [[`codebase_rag/parsers/java/type_inference.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/parsers/java/type_inference.py)](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/parsers/java/type_inference.py)

- **Shared contract**: All engines receive Tree-Sitter `Node` and module QN, return `dict[str, str]` mapping identifiers to type strings

- **Pipeline integration**: Engines register via `LanguageHandler`, produce local type maps for call-edge resolution and RAG retrieval

---

## 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.