51 lines
2.3 KiB
Markdown
51 lines
2.3 KiB
Markdown
|
|
# 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`:
|
||
|
|
|
||
|
|
```python
|
||
|
|
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_body` as 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_definition` uses `declarator` field for the function name, not `name` — 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.
|