Repository navigation
m4 avg_func_args counts $1 references, not declared positions: m4 lacks shell's _args_findall_max_groups #2784
Description
Found by the 46-language audit in docs/args_rule_contract.md, written for #2773.
The contract
argsmatches the parameters a callable declares.Corollary 3: in a language with no formal parameter list,
$1/$@in a body is the only
parameter surface there is -- but a count of those references measures how often the body reads
its arguments, not how many it takes.
Measured
keyword-rosetta/data/m4, args against func_start, over the code stream:
| file | m4_defines |
args |
why |
|---|---|---|---|
a.m4 |
3 | 5 | probe_test and probe_safety each reference $1 twice |
b.m4 |
3 | 4 | one macro references $1 twice |
c.m4 |
3 | 3 | exact |
main.m4 |
4 | 6 | two macros reference $1 twice |
| total | 13 | 18 (+38%) | 5 duplicate references |
m4_define(probe_test, [AT_SETUP($1) AT_CHECK($1)]) # 1 parameter, args records 2language-crucible/data/m4: 70 args against 39 func_start (a/f 1.79).
The cause and the fix
"args": re.compile(r"\$[0-9]+|\$[@*#]"), # m4 -- no helpershell has exactly the same rule shape and exactly the same problem, and already solves it:
"args": re.compile(r"(\$(?:[1-9]|\{#?[1-9][0-9]*…\}|@|\*|#)|\"\$@\"|\"\$\*\")"),
"_args_findall_max_groups": {1},_args_findall_max_groups makes _calculate_block_metrics take the highest position
referenced inside the block rather than the number of references, so $1 … $1 … $3 is 3
parameters, not 3 references. m4 declares no such helper, so _calculate_block_metrics falls
through to the generic counter and avg_func_args inherits the reference count.
Two pieces of work, and they are separable:
- Per-function -- give m4
_args_findall_max_groups(and a capture group to hang it on).
Mechanical, mirrors shell exactly.$@/$*need whatever shell's helper already does for them. - File-level
struct_args-- the raw rule count has no per-block context to take a maximum
over, so it stays a reference count.shell,makefileandyaccall share this and all
currently read 13 on the rosetta shells only because those shells happen to reference each
position once. Worth deciding explicitly whether that is accepted (and ledgered under
args-no-parameter-surface-morphology) or whether the positional family needs a per-macro
dedupe -- m4 is the language that exposes it, but the question is the family's.
Corpus pairing
args is planted at 13 (one parameter per probe, SPEC.md). keyword-rosetta/data/m4 needs no
authoring change for part 1 -- the fix should land it on 13 exactly.
Part of #2669 (rosetta[m4] tracking issue).
Activity
fix(core-engine): args counts declared parameters, not call sites (ob…
fix(core-engine): args counts declared parameters, not call sites (ob…
fix(core-engine): args counts declared parameters, not call sites (ob…
squid-protocol commentedon Sep 6, 2026
Narrowing this — half of it was already settled and I missed it.
keyword-rosetta's
deviation_ledger.jsoncarriesm4-parameters-are-use-sites(dispositionintended-morphology, validated 2026-09-04):So the headline +38% is not a defect and this issue should not be read as asking for m4's file-level
argsto reach 13. I filed it without checking the ledger; that verdict stands and I am not proposing to overturn it.What is still genuinely open is the part the ledger does not address:
avg_func_args.shellhas the same morphology (no parameter list,$1in the body is the parameter) and the same file-level behaviour, but it declares_args_findall_max_groups, which makes_calculate_block_metricstake the highest position referenced in a block rather than the number of references. m4 declares no such helper, so it falls through to the generic counter:That is a per-function arity error, not a morphology question —
$1 … $1is one parameter referenced twice under any reading, including the ledger's. Two macros in the rosetta shells hit it, andlanguage-crucible/data/m4reads 70 args against 39func_start.Revised scope: give m4 a capture group and
_args_findall_max_groups, mirroring shell. Leave the file-level count alone — it stays ledgered as intended morphology, andkeyword-rosetta/data/m4needs no re-bless.Retitled accordingly. The 'Corpus pairing' section of the original description no longer applies.
[-]args counts `$1` REFERENCES in m4 (+38%), not declared positions: m4 lacks shell's _args_findall_max_groups[/-][+]m4 avg_func_args counts `$1` references, not declared positions: m4 lacks shell's _args_findall_max_groups[/+]docs(args-contract): correct two audit findings that keyword-rosetta …