CodeGraph/docs
Section 2.5.1

Symbol Stack & Scoping Hierarchy

Tracking container nesting context during AST traversal using symbolStack, path.setData invariants, and deterministic ID formation.

Source: server/src/parser/extractors/SymbolExtractor.ts

The Symbol Stack Mechanism

As Babel traverses nested syntax trees, inner symbols need to know their enclosing parent container (for example, a method needs to link to its enclosing class). Rather than building a deeply nested tree, CodeGraph maintains a flat symbols: ParsedSymbol[] array where each symbol carries aparentSymbolId pointer.

This is coordinated by symbolStack: ParsedSymbol[]:

Lifecycle Visitor Pattern
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private createContainerVisitor<T extends SupportedSymbolNode>( parsedFile: ParsedFile, symbolKind: SymbolKind, getExtraParams?: (path: NodePath<T>) => SymbolExtraParams ) { return { enter: (path: NodePath<T>) => { const extraParams = getExtraParams?.(path); const symbol = this.extractSymbol({ path, parsedFile, symbolKind, ...extraParams }); if (symbol) { this.symbolStack.push(symbol); path.setData("symbolPushed", true); // Guard invariant } }, exit: (path: NodePath<T>) => { if (path.getData("symbolPushed")) { this.symbolStack.pop(); } } }; }

Deterministic Symbol ID Generation

Every symbol ID is guaranteed to be stable and reproducible across runs:

Deterministic ID Formula
1
2
3
4
// ID Format: ${filePath}:${startLine}:${startColumn}:${name} private buildSymbolId(parsedFile: ParsedFile, name: string, location: SymbolLocation): string { return `${parsedFile.filePath}:${location.startLine}:${location.startColumn}:${name}`; }
path.setData Stack Invariant
The path.setData("symbolPushed", true) pattern guarantees that the exact same AST path node that pushed a container onto the stack is the one that pops it on exit. This prevents stack corruption if an anonymous or unnamable container returned undefined on enter.