You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Argument-level validations in internal/generate/routes.go::appendParseArgumentStatements only inspect the immediatecall.Args list. When an argument is a nested *ast.CallExpr (e.g. Helper(form) passed as a positional argument to the receiver method), identifiers inside that nested call are not subject to the same cross-argument constraints.
Concrete case: form / multipart mutual exclusion
appendParseArgumentStatements (after #77) rejects template names that pass both form and multipart as call arguments — but only at the top level:
appendParseArgumentStatements recurses on the nested Helper(multipart) call (existing behavior at the arg switch), so both request.ParseForm() and request.ParseMultipartForm(...) end up emitted in the same handler. In practice these can co-exist when only one body type matches the request's Content-Type, but the user's intent is muddled and the generated handler does redundant work.
Why it slipped through
checkArguments in internal/muxt/definition.go already recurses on nested CallExpr to detect unknown identifiers (line ~334).
The mutual-exclusion check added in feat: add multipart call-site identifier #77 lives in internal/generate/routes.go and was written for a single-pass scan of the top-level args.
Other future cross-argument constraints (e.g. "identifier X requires identifier Y", "at most one of A/B/C") would face the same issue.
Proposed approach
Extract a shared helper that walks all identifier arguments in a call tree:
…then implement the form/multipart check (and any future identifier-pair constraint) as a single pass with this walker. The existing recursion in appendParseArgumentStatements already mirrors this shape — moving the check to use the same traversal would close the gap.
Severity
Low. Requires deliberately unusual template syntax (form and multipart in different positions of the same call tree), and the runtime impact is "extra work" rather than incorrect parsing — ParseForm/ParseMultipartForm only consume the body when the Content-Type matches their respective formats. Worth fixing for clarity, not urgency.
Acceptance criteria
Add a recursive walker over identifier arguments in a call tree.
form + multipart rejection covers nested calls.
New err_* testdata case asserting the rejection (e.g. err_multipart_with_form_in_nested_call.txt).
Consider whether other identifier-pair constraints should adopt the same walker.
Summary
Argument-level validations in
internal/generate/routes.go::appendParseArgumentStatementsonly inspect the immediatecall.Argslist. When an argument is a nested*ast.CallExpr(e.g.Helper(form)passed as a positional argument to the receiver method), identifiers inside that nested call are not subject to the same cross-argument constraints.Concrete case:
form/multipartmutual exclusionappendParseArgumentStatements(after #77) rejects template names that pass bothformandmultipartas call arguments — but only at the top level:The following template name slips past this check:
appendParseArgumentStatementsrecurses on the nestedHelper(multipart)call (existing behavior at theargswitch), so bothrequest.ParseForm()andrequest.ParseMultipartForm(...)end up emitted in the same handler. In practice these can co-exist when only one body type matches the request'sContent-Type, but the user's intent is muddled and the generated handler does redundant work.Why it slipped through
checkArgumentsininternal/muxt/definition.goalready recurses on nestedCallExprto detect unknown identifiers (line ~334).internal/generate/routes.goand was written for a single-pass scan of the top-level args.Proposed approach
Extract a shared helper that walks all identifier arguments in a call tree:
…then implement the form/multipart check (and any future identifier-pair constraint) as a single pass with this walker. The existing recursion in
appendParseArgumentStatementsalready mirrors this shape — moving the check to use the same traversal would close the gap.Severity
Low. Requires deliberately unusual template syntax (
formandmultipartin different positions of the same call tree), and the runtime impact is "extra work" rather than incorrect parsing —ParseForm/ParseMultipartFormonly consume the body when theContent-Typematches their respective formats. Worth fixing for clarity, not urgency.Acceptance criteria
form+multipartrejection covers nested calls.err_*testdata case asserting the rejection (e.g.err_multipart_with_form_in_nested_call.txt).Surfaced during the #77 review.