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.8 KiB
6. Argument pattern extraction via AST node classification
Date: 2026-07-03
Status: Accepted
Context
A behavioral token like assertEquals tells the LLM that the function is called, but not how. Two codebases both use assertEquals — one writes assertEquals(expected, actual) and the other writes assertEquals(actual, expected) with swapped argument order. An LLM guessing the wrong order writes broken tests.
The highlights.scm captures tell us that a function is called. We need the argument structure — number of arguments, their types, and the common patterns.
Decision
For each behavioral capture node, walk up to its parent call_expression (or equivalent), find the argument list node, and classify each argument by structural role.
Argument classification is language-agnostic:
| Classification | Matches |
|---|---|
lit |
string, number, boolean, null |
var |
identifiers, names |
call |
nested call expressions, method invocations |
lambda |
lambda expressions, blocks, do-blocks |
kwarg |
keyword/named arguments |
expr |
binary/unary/ternary/operator expressions |
template |
string interpolation, template literals |
other |
anything else (fallback) |
Argument list node detection uses a tiered approach:
child_by_field_name("arguments")— works for Python, JS, TS, Java, Go, Ruby, Rust.- Fallback: scan children for
argument_list,arguments,call_suffix(Kotlin),template_string(JS tagged templates). - Kotlin special case:
call_suffixmay contain a directlambda_expressionchild (forevery { ... }syntax) or avalue_arguments → value_argumentchain (forfunc(a, b)syntax).
Results are aggregated per cluster into a summary showing min/max/common arg counts and the top argument-type patterns.
Consequences
Positive:
- Reveals argument ordering conventions:
assertEquals: n=2 [lit,var]means expected-first. - Reveals calling convention variance:
verify: n=0 [] | n=1 [lambda] | n=1 [var]means three styles coexist. - No per-language branches — the tiered arglist detection handles all 10 grammars.
Negative:
kwargdetection only covers named arguments, not default values or spread operators.- Nested destructuring patterns fall into
otherbucket — no granularity for complex argument shapes. otheris a catch-all that can hide meaningful distinctions we haven't classified yet.
Alternatives Considered
- Extract raw argument text: Language-agnostic but fragile — variable names change per test, producing high variance and low signal.
- No argument extraction: The LLM sees
assertEqualsbut doesn't know argument order. Leads to wrong code. - Per-language argument extractors: Would be more precise but violate the zero-adapters constraint.