ADR 0001: nvim-treesitter highlights.scm as capture source ADR 0002: language-agnostic method extraction via child_by_field_name ADR 0003: method-level n-gram clustering before inference ADR 0004: frequency filter with min_coverage threshold ADR 0005: import extraction per cluster ADR 0006: argument pattern extraction via AST node classification ADR 0007: JSON output for LLM prompt injection ADR 0008: BEX ensemble for grammar inference
2.3 KiB
2. Language-agnostic method extraction via child_by_field_name("body")
Date: 2026-07-03
Status: Accepted
Context
To analyze method-level behavioral conventions, we must extract the body of each function/method from the AST. The standard tree-sitter approach is node.child_by_field_name("body"), but this named field is not universal across all language grammars.
We need one code path that works for all 10 supported languages without per-language branches.
Decision
Use node.child_by_field_name("body") as the primary extraction method. When it returns None, fall back to scanning the node's children for any child with a type containing body, block, or compound_statement.
Parent nodes are further filtered to only include nodes whose type contains function or method — avoiding class bodies, loop bodies, and conditional blocks.
This logic lives in _find_method_bodies() in code.py:
def walk(node):
body = node.child_by_field_name("body")
if not body:
for child in node.children:
ctype = child.type.lower()
if "body" in ctype or "block" in ctype or ctype == "compound_statement":
body = child; break
if body:
ptype = node.type.lower()
if "function" in ptype or "method" in ptype:
bodies.append(body)
for child in node.children: walk(child)
Consequences
Positive:
- Works for 9/10 grammars via
child_by_field_name("body")alone (Python, Go, Rust, JS, TS, Ruby, Java, C, C++). - Kotlin fallback works because the fwcd Kotlin grammar uses
function_bodyas a child node type. - Zero per-language case analysis — just pattern matching on type strings.
Negative:
- Fallback relies on string matching (
"body" in ctype) which could produce false positives if future grammar versions introduce new body-like types. - C/C++
function_definitionusesdeclaratorfield for the function name, notname— affects name extraction but not body extraction.
Alternatives Considered
- Grammar-specific field names: Map each language to its body field name. Rejected because it creates a maintenance burden and violates the zero-adapters constraint.
- Top-down sibling traversal: Walk from node start to next sibling to find the body. Fragile across grammars with different compound statement structures.