grammar-inference-engine/docs/adr/0006-argument-pattern-extraction.md
tobjend e23922a1b7
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
feat: adaptive multi-assignment clustering; add ADRs 1-10
- Multi-assignment clustering (no greedy 'used' set)
- Adaptive ngram fallback (shrink when (other) > 60%)
- Add docs/adr/ with 10 architecture decision records
- Fix ADR 1 (query modification description)
- Fix ADR 3 (multi-assignment + adaptive shrink)
- Fix ADR 5 (import sort order clarification)
- Fix ADR 6 (remove Kotlin call_suffix references)
- New ADR 9 (adaptive clustering rationale)
- New ADR 10 (universal package mapping via relpath)
2026-07-03 22:58:09 +02:00

54 lines
2.8 KiB
Markdown

# 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:
1. `child_by_field_name("arguments")` — works for Python, JS, TS, Java, Go, Ruby, Rust.
2. Fallback: scan children for `argument_list`, `arguments`, `template_string` (JS tagged templates).
The argument iterator is a simple generic traversal: yield all named children of the arglist node. No per-language special cases. This works for positional args, keyword args, lambdas inside argument lists, and template expressions.
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.
- Zero per-language branches — generic tiered detection and iteration handles all 10 grammars.
**Negative:**
- `kwarg` detection only covers named arguments, not default values or spread operators.
- Nested destructuring patterns fall into `other` bucket — no granularity for complex argument shapes.
- `other` is 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 `assertEquals` but doesn't know argument order. Leads to wrong code.
- **Per-language argument extractors**: Would be more precise but violate the zero-adapters constraint.