--- id: wiki-2026-0508-ast-traversal title: AST Traversal category: 10_Wiki/Topics status: verified canonical_id: self aliases: [ast-walk, syntax-tree-traversal, visitor-pattern] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [ast, compilers, codemod, static-analysis] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: typescript framework: ts-morph --- # AST Traversal ## 매 한 줄 > **"매 code 의 tree 의 walk 한다"**. 매 AST (Abstract Syntax Tree) traversal 의 compiler/linter/codemod/IDE 의 foundation. 매 2026 의 tree-sitter (Atom, 2018; 매 GitHub 의 sole semantic search engine) + ts-morph + Babel + RustPython AST 의 dominant tooling. ## 매 핵심 ### 매 traversal strategies - **Pre-order DFS**: 매 visit parent → children. 매 default (most visitors). - **Post-order DFS**: 매 visit children → parent. 매 type inference, dead-code elim. - **BFS**: 매 level-by-level. 매 rare — scope analysis. - **Visitor pattern**: 매 node-type-keyed callbacks. 매 ESLint, Babel, ts-morph standard. ### 매 mutating vs read-only - **Read-only**: linter, complexity metrics, security scanner. - **Mutating**: codemod, formatter, transpiler. 매 immutability + new tree 의 produce 의 best-practice (Babel: `path.replaceWith`). ### 매 응용 1. ESLint rule — 매 pattern detection. 2. Codemod — jscodeshift, ts-morph, ast-grep. 3. Tree-sitter query — 매 IDE syntax highlight, code nav. 4. AST-based diffing — 매 difftastic, semantic diff. ## 💻 패턴 ### Babel visitor (JavaScript) ```js import { parse } from '@babel/parser'; import traverse from '@babel/traverse'; import generate from '@babel/generator'; import * as t from '@babel/types'; const code = `console.log("hello"); foo("world");`; const ast = parse(code); traverse(ast, { CallExpression(path) { const callee = path.node.callee; if (t.isMemberExpression(callee) && t.isIdentifier(callee.object, { name: 'console' })) { // strip console.* calls path.remove(); } } }); console.log(generate(ast).code); // foo("world"); ``` ### ts-morph (TypeScript refactor) ```ts import { Project, SyntaxKind } from 'ts-morph'; const project = new Project({ tsConfigFilePath: 'tsconfig.json' }); for (const sf of project.getSourceFiles()) { sf.forEachDescendant(node => { if (node.getKind() === SyntaxKind.CallExpression) { const ce = node.asKindOrThrow(SyntaxKind.CallExpression); if (ce.getExpression().getText() === 'fetch') { ce.replaceWithText(`httpClient.${ce.getText().replace('fetch', '')}`); } } }); } await project.save(); ``` ### Python — `ast` + NodeTransformer ```python import ast class StripPrint(ast.NodeTransformer): def visit_Expr(self, node): if (isinstance(node.value, ast.Call) and isinstance(node.value.func, ast.Name) and node.value.func.id == "print"): return None # remove return node src = "print('hi')\nx = 1" tree = ast.parse(src) StripPrint().visit(tree) ast.fix_missing_locations(tree) print(ast.unparse(tree)) # x = 1 ``` ### tree-sitter query (multi-language) ```scheme ; queries/python/calls.scm — find all decorated functions (decorated_definition (decorator (call function: (identifier) @decorator-name)) definition: (function_definition name: (identifier) @func-name)) ``` ```python import tree_sitter_python as tspython from tree_sitter import Language, Parser, Query PY = Language(tspython.language()) parser = Parser(PY) tree = parser.parse(b"@app.route('/')\ndef home(): pass") q = Query(PY, open("queries/python/calls.scm").read()) for node, name in q.captures(tree.root_node): print(name, node.text) ``` ### ast-grep (rule-based, polyglot) ```yaml # rule.yml id: no-console-log language: js rule: pattern: console.log($$$) fix: '' ``` ```bash ast-grep scan -r rule.yml --update ``` ### Rust — syn (proc-macro / codegen) ```rust use syn::{visit::Visit, ItemFn, File}; struct FnCounter(usize); impl<'ast> Visit<'ast> for FnCounter { fn visit_item_fn(&mut self, i: &'ast ItemFn) { self.0 += 1; syn::visit::visit_item_fn(self, i); } } let src = std::fs::read_to_string("src/lib.rs").unwrap(); let file: File = syn::parse_file(&src).unwrap(); let mut c = FnCounter(0); c.visit_file(&file); println!("functions: {}", c.0); ``` ### ESLint custom rule ```js module.exports = { meta: { type: 'problem', schema: [] }, create(ctx) { return { 'CallExpression[callee.name="eval"]'(node) { ctx.report({ node, message: 'eval is forbidden' }); } }; } }; ``` ### Pre-order vs post-order (manual walk) ```ts function walkPre(node: Node, fn: (n: Node) => void) { fn(node); for (const c of node.children) walkPre(c, fn); } function walkPost(node: Node, fn: (n: Node) => void) { for (const c of node.children) walkPost(c, fn); fn(node); } ``` ## 매 결정 기준 | Goal | Tool | |---|---| | JS/TS lint rule | ESLint visitor | | TS large refactor | ts-morph | | Python codemod | LibCST (preserves whitespace) > ast | | Polyglot pattern search | ast-grep, Semgrep | | IDE / syntax highlight | tree-sitter | | Rust macro | syn / quote | | Babel plugin | @babel/traverse | **기본값**: 매 ts-morph (TS refactor) · ast-grep (polyglot scan) · LibCST (Python codemod). ## 🔗 Graph - 부모: [[Static-Analysis]] - 변형: [[Visitor-Pattern]] - 응용: [[ESLint]] · [[Refactoring_Best_Practices|Refactoring]] - Adjacent: [[Architectural-Constraint-Enforcement]] · [[Architecture_Refactor]] ## 🤖 LLM 활용 **언제**: 매 codebase 의 LLM-driven structural query — 매 ast-grep + Claude 의 hybrid (LLM 의 generate pattern, ast 의 execute). **언제 X**: 매 LLM 의 raw text find/replace — 매 AST-aware tool 의 사용. ## ❌ 안티패턴 - **Regex on code**: 매 multiline construct 의 break — 매 AST 의 사용. - **Mutating during traversal**: 매 visitor 의 reentrancy bug — 매 collect-then-apply. - **Ignore comments/whitespace**: 매 codemod 의 lose comments — 매 LibCST/Recast 의 사용. - **Single-pass dependence**: 매 transformation order 의 fragile — 매 idempotent 의 design. ## 🧪 검증 / 중복 - Verified (Babel docs; ts-morph guide; tree-sitter playground; *Crafting Interpreters* — Nystrom). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — Babel/ts-morph/tree-sitter/ast-grep patterns |