# How to Use C++ Semantic Mode for Complex Parsing in Code‑Graph‑RAG

> Master C++ semantic mode for complex parsing. Install the cpp extra, set CG_RAG_CPP_FRONTEND=libclang, and run the indexer for advanced symbol resolution and template parsing with Code-Graph-RAG.

- Repository: [Vitali Avagyan/code-graph-rag](https://github.com/vitali87/code-graph-rag)
- Tags: how-to-guide
- Published: 2026-08-20

---

**Enable C++ semantic mode by installing the `[cpp]` extra, setting `CG_RAG_CPP_FRONTEND=libclang`, and running the indexer—this activates LibClang‑based symbol resolution for fully‑qualified names and template‑aware parsing.**

The **code‑graph‑rag** open‑source project provides two C++ parsing strategies. For complex codebases—those with heavy template use, nested namespaces, or cross‑module dependencies—the **semantic mode** delivers accurate qualified names and type resolution that Tree‑Sitter alone cannot provide. This guide shows how to configure and verify C++ semantic parsing.

---

## Understanding the Two C++ Parsing Modes

| Mode | Backend | Capabilities | Best For |
|------|---------|--------------|----------|
| **Tree‑Sitter** | Syntactic only | Fast concrete syntax tree, no type resolution | Quick indexing of simple codebases |
| **Semantic (LibClang/Hybrid)** | LibClang + Clang AST | Fully‑qualified names, template instantiation, overload resolution, namespace tracking | Complex C++ with templates, cross‑language edges, precise call graphs |

The **Hybrid** frontend (default) attempts Tree‑Sitter first and falls back to LibClang when available. For guaranteed semantic parsing, force **LibClang** explicitly.

---

## Installing the C++ Semantic Frontend

The semantic parser requires the optional `cpp` extra, which pulls in `libclang` bindings.

```bash
pip install "code-graph-rag[cpp]"

```

On Linux systems, you may additionally need the development headers:

```bash
sudo apt-get install libclang-dev

```

Verify the installation by checking that `clang.cindex` imports successfully:

```python
python -c "import clang.cindex; print(clang.cindex.Config().lib)"

```

---

## Selecting the C++ Frontend via Environment Variable

The `CppFrontend` enum in [`codebase_rag/constants/languages.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/constants/languages.py) defines three valid values[^1]:

- `treesitter` — Pure syntactic parsing
- `libclang` — Full semantic resolution
- `hybrid` — Tree‑Sitter with LibClang fallback (default)

Set the environment variable before any indexing operation:

```bash
export CG_RAG_CPP_FRONTEND=libclang

```

Or within Python:

```python
import os
os.environ["CG_RAG_CPP_FRONTEND"] = "libclang"

```

The [`parser_loader.py`](https://github.com/vitali87/code-graph-rag/blob/main/parser_loader.py) module reads this variable and instantiates `CppHandler` with the corresponding backend[^2].

---

## Configuring and Running the Semantic Parser

### Step‑by‑Step Indexing Workflow

**1. Import and instantiate the handler directly (programmatic use):**

```python
from codebase_rag.parsers.handlers.cpp import CppHandler

handler = CppHandler()  # Respects CG_RAG_CPP_FRONTEND automatically

```

**2. Run the main indexer:**

```bash
python -m codebase_rag.main /path/to/cpp/repo --language cpp

```

The indexer emits a log message confirming the active frontend (see [`logs.py`](https://github.com/vitali87/code-graph-rag/blob/main/logs.py) for exact formatting). Expect output like:

```

Using LibClang C++ frontend

```

**3. Verify semantic resolution in output:**

After indexing, load the generated graph and inspect `qualified_name` attributes:

```python
from codebase_rag.graph_loader import load_graph
from pathlib import Path

graph = load_graph(Path("/tmp/cgr_graph"))

# Semantic mode produces fully-qualified names

func = graph.get_node_by_qn("engine::render::Pipeline::bind")
print(func["qualified_name"])  # → engine::render::Pipeline::bind

```

Without semantic mode, the same function would appear as merely `bind`.

---

## How Semantic Resolution Constructs Qualified Names

The semantic resolution pipeline in [`codebase_rag/parsers/handlers/cpp.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/parsers/handlers/cpp.py) operates as follows[^3]:

1. **AST node identification** — `CppHandler.extract_function_name` attempts basic extraction via `cpp_utils` helpers. For lambdas, it synthesizes names like `lambda_42_15`.

2. **Qualified‑name construction** — `CppHandler.build_function_qualified_name` delegates to `resolve_fqn_from_ast` when a semantic frontend is active. This utility traverses the Clang AST to locate enclosing namespaces, classes, and template parameters.

3. **Name concatenation** — The resolver concatenates scope components: `namespace::Class<T>::function`.

4. **Export detection** — `is_function_exported` checks `extern "C"` and visibility attributes using `cpp_utils.is_exported`, respecting semantic parser information.

5. **Base‑class handling** — `extract_base_class_name` processes both simple identifiers and `TS_TEMPLATE_TYPE` nodes for complex inheritance chains.

The core resolution logic resides in [`codebase_rag/utils/fqn_resolver.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/utils/fqn_resolver.py), which queries LibClang's `cursor.semantic_parent` chain to build complete identifiers.

---

## Complete Example: End‑to‑End Semantic Indexing

```python
import os
from pathlib import Path
from codebase_rag.main import run_indexer
from codebase_rag.graph_loader import load_graph

# Force semantic mode

os.environ["CG_RAG_CPP_FRONTEND"] = "libclang"

# Run indexing

repo = Path("/home/dev/graphics-engine")
run_indexer([str(repo)], languages=["cpp"])

# Inspect results

graph = load_graph(Path("/tmp/cgr_graph"))

# Find all methods in a specific namespace

methods = [
    n for n in graph.nodes()
    if n.get("qualified_name", "").startswith("gfx::vulkan::")
]

print(f"Found {len(methods)} Vulkan-gfx methods with qualified names")
for m in methods[:5]:
    print(f"  - {m['qualified_name']}")

```

---

## Using Semantic Search with C++ Results

The semantic search tool leverages embeddings generated by the semantic frontend. Once indexed, query the codebase:

```python
from codebase_rag.tools.semantic_search import semantic_code_search

results = semantic_code_search(
    ingestor=your_ingestor_instance,
    query="memory pool allocator initialization",
    top_k=10,
    project="graphics-engine"
)

for r in results:
    print(f"{r['function_qn']}: score {r['score']:.3f}")

```

Accurate `function_qn` values from semantic parsing ensure search results map to precise code locations.

---

## Summary

- **Install**: `pip install "code-graph-rag[cpp]"` to obtain LibClang bindings
- **Configure**: Set `CG_RAG_CPP_FRONTEND=libclang` for guaranteed semantic parsing (or use `hybrid`/`treesitter` alternatives)
- **Verify**: Check `qualified_name` attributes contain full namespace and class scoping
- **Key files**: [`languages.py`](https://github.com/vitali87/code-graph-rag/blob/main/languages.py) (enum definition), [`cpp.py`](https://github.com/vitali87/code-graph-rag/blob/main/cpp.py) (handler), [`fqn_resolver.py`](https://github.com/vitali87/code-graph-rag/blob/main/fqn_resolver.py) (resolution logic)

---

## Frequently Asked Questions

### What is the difference between Hybrid and LibClang modes?

**Hybrid mode** attempts Tree‑Sitter parsing first and falls back to LibClang only when the syntactic parser fails or when semantic information is explicitly required. **LibClang mode** forces the semantic frontend for all C++ files, ensuring consistent fully‑qualified naming at the cost of slightly slower indexing. Use LibClang when you need guaranteed accuracy for template‑heavy code.

### Why does my qualified name still lack namespace information?

This occurs when: (1) the `cpp` extra is not installed, (2) `CG_RAG_CPP_FRONTEND` is unset or set to `treesitter`, or (3) LibClang cannot locate system headers. Verify with `python -c "import clang.cindex"` and check that `libclang-dev` is installed on Linux systems.

### Can I mix semantic and non‑semantic parsing in the same project?

No— the frontend selection applies globally per indexing run. However, you can run separate indexing passes with different frontends and merge graphs if needed. The `CppHandler` initializes its strategy once at import time based on the environment variable.

### Does semantic mode support C++20 modules?

Yes. The LibClang frontend recognizes `export` declarations and module boundaries. The `extract_function_name` and `is_function_exported` methods in [`cpp.py`](https://github.com/vitali87/code-graph-rag/blob/main/cpp.py) handle visibility attributes and module linkage as reported by the Clang AST, though module‑specific naming conventions may require additional verification in your codebase.

[^1]: `CppFrontend` enum definition — [`codebase_rag/constants/languages.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/constants/languages.py) lines 101–106
[^2]: Frontend loading logic — [`codebase_rag/parsers/parser_loader.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/parsers/parser_loader.py)
[^3]: Qualified name construction — [`codebase_rag/parsers/handlers/cpp.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/parsers/handlers/cpp.py) lines 41–49