What happens
A def calling another instruction with too few arguments is not refused. The missing
parameter is simply empty.
def w(p: str) {
apply {
file.write(p) # content is missing
return ok.done
}
}
Run against a real target, on a file holding the original content:
probe.w(p=/tmp/arity.txt) ok.done
shell(grep -qx 'the original content' /tmp/arity.txt) err.runtime
The file was overwritten with nothing, and the def reported success.
Why
internal/lang/eval.go checks one bound:
if len(c.Args) > len(def.Params) {
ev.fail("%s takes %d argument(s), got %d", c.Name, len(def.Params), len(c.Args))
}
Too many is refused; too few falls through, and the loop over c.Args never binds the
remaining parameters. A plan-level call is checked on both bounds
(internal/lang/parser.go:838) — so the same mistake is caught in a plan and silent in a
def, which is where defs compose.
Build
Refuse a call whose argument count is below the number of parameters without a default —
the required count the parser already computes. The message should match the plan's, so
the two paths read the same.
def.Params[i].Default exists, so "required" is a real notion here, not an invention.
Validation
- A failing test first: a def calling a two-parameter instruction with one argument is
refused at evaluation, naming the instruction and both counts.
- A call omitting a defaulted parameter still works — that is the case the current
laxity was presumably protecting, and the fix must not break it (docker.prune() relies
on it).
- The plan path is unchanged.
What happens
A def calling another instruction with too few arguments is not refused. The missing
parameter is simply empty.
Run against a real target, on a file holding
the original content:The file was overwritten with nothing, and the def reported success.
Why
internal/lang/eval.gochecks one bound:Too many is refused; too few falls through, and the loop over
c.Argsnever binds theremaining parameters. A plan-level call is checked on both bounds
(
internal/lang/parser.go:838) — so the same mistake is caught in a plan and silent in adef, which is where defs compose.
Build
Refuse a call whose argument count is below the number of parameters without a default —
the
requiredcount the parser already computes. The message should match the plan's, sothe two paths read the same.
def.Params[i].Defaultexists, so "required" is a real notion here, not an invention.Validation
refused at evaluation, naming the instruction and both counts.
laxity was presumably protecting, and the fix must not break it (
docker.prune()relieson it).