Skip to content

fix(grammar): an empty argument list on a page action or flow data source means no arguments - #1750

Merged
ako merged 1 commit into
mainfrom
fix/1516-widget-action-empty-call-args
Oct 27, 2026
Merged

ako merged 1 commit into
mainfrom
fix/1516-widget-action-empty-call-args

Conversation

@ako

@ako ako commented Oct 27, 2026

Copy link
Copy Markdown
Owner

Closes #1516 (mirror of mendixlabs#1606).

actionbutton … (Action: call microflow M.F()) was a syntax error ("mismatched input ')' expecting the start of a statement"), although the same call parses in a flow body and the widget action parses without the brackets.

Cause and fix

microflowArgsV3 (mdl/grammar/domains/MDLPage.g4) required at least one argument. That one rule is the argument list of all six page-action alternatives (call microflow, call nanoflow, show page, and their MDL-DEPR020 spellings) and both flow data sources, so X() failed at every site that takes an action or a flow data source. It now takes LPAREN ( … )? RPAREN, the shape workflowCallArguments and the flow-body callArgumentList already have.

The sibling fragmentArgs had the same shape, with a worse symptom: use fragment Hdr() was not a syntax error — the page body's generic widget alternative re-read it as two widgets (type use named fragment, and type Hdr), check without a project passed it, and check -p / exec refused it with MDL-WIDGET25. Same fix.

No visitor change: buildMicroflowArgsV3 and buildFragmentArgs return nil for an empty list, so X() builds exactly the AST X builds. describe keeps writing no brackets. The change only lets refused scripts parse, so no language-version gate applies.

Audit (mxcli check, X() baseline → fixed; the bare and with-arguments forms pass both before and after everywhere)

Site X()
button call microflow / call nanoflow / show page, and the deprecated spellings FAIL → PASS
… X() with (…) / X() returns (…), create object E then show page X() FAIL → PASS
container OnClick, textbox OnChange, a pluggable action property FAIL → PASS
DataSource: microflow X() / nanoflow X() FAIL → PASS
alter page set Action = … X() / set DataSource = microflow X() FAIL → PASS
navigation menu item and menu document item OnClick FAIL → PASS
fragment argument value, building-block Action: override FAIL → PASS
use fragment Frag() wrong AST (two bogus widgets) → right AST
flow body call microflow X() PASS → PASS (bare call microflow X; stays an error, on purpose)
use building block X(), snippetcall (Params: ()) FAIL → FAIL (not call argument lists; left alone)

Verified

  • TestEmptyCallArgumentListMeansNoArguments covers 23 sites, comparing X() with X as whole statements (DeepEqual) and their deprecation codes; its control is that X(arg = …) builds a different statement. TestFlowBodyCallStillRequiresItsBrackets pins the flow-body form.
  • Revert proof: with the grammar reverted and regenerated, all 23 sites fail (22 parse errors; use fragment on statement equality).
  • Real 11.13 app: baseline reports 13 syntax errors on the bug-test script; fixed, check -p --references passes, exec creates everything, mx check 0 errors. A page written entirely with () and its bracket-less twin store identical BSON (apart from $ID/GUID/name, 7 vs 7 ParameterMappings), describe identically, and re-executing the describe output reports "Unchanged page".
  • All 81 non-.fail doctype-tests and 998 bug-tests through both binaries: the only changed result is the new bug-test (fail → pass).
  • Full go test ./..., go vet, gofmt and CI's doc checks pass with this batch's three branches merged onto main. Bug-test widget-action-1516-empty-call-args.mdl; finding recorded; mxcli syntax page.action and docs-site create-page updated.
  • Not verified: opening the result in Studio Pro (the proof is identical BSON to the bracket-less form).

Noticed, not fixed

Whenever a specific page-body rule fails to match, the generic IDENTIFIER widget alternative plus an optional empty property list can silently re-read the input as unknown widgets — check without a project passes such pages, and only -p reports MDL-WIDGET25. Other near-miss spellings may hit the same trap.

🤖 Generated with Claude Code

A page action calling a parameterless flow with an empty argument list,
`Action: call microflow M.Ping()`, failed check with "mismatched input ')'
expecting the start of a statement", while the flow body accepts `()` and
the widget accepted the call without brackets (mendixlabs#1606).

Cause: microflowArgsV3 in MDLPage.g4 required at least one argument. It
is the argument rule of every page action (call microflow/nanoflow, show
page and their MDL-DEPR020 spellings) and both flow data sources, so the
same error hit Action, OnClick, OnChange, pluggable action properties,
alter page set Action/DataSource, navigation and menu-document items,
fragment arguments and building-block overrides. fragmentArgs had the
same shape; there `use fragment Hdr()` was not even an error without a
project: it parsed as two generic widgets (`use` named `fragment`, and
`Hdr` with an empty property list), and check -p reported MDL-WIDGET25.

Fix: both rules accept an empty list inside the brackets, as the flow
body's callArgumentList and workflowCallArguments already do. No visitor
change is needed: the builders return nil for no arguments, so `X()`
builds exactly the statement `X` builds. Additive, so no mdl-version
gate. describe still writes the bracket-less form. The page.action
syntax topic and the create-page reference say so; the syntax-docs guard
keeps the reference on the describe form with a corrected reason.

Verification:
- mdl/visitor/visitor_empty_call_args_test.go: 23 sites, X() vs X as
  whole statements and deprecation codes, a real argument list as the
  control. Reverting the grammar fails all 23 (22 parse errors, and use
  fragment on statement equality).
- Real 11.13.0 app: a page written with () and its bracket-less twin
  store identical BSON once $ID and the page name are set aside, and
  describe identically without brackets; describe -> exec reports
  Unchanged page; mx check 0 errors.
  mdl-examples/bug-tests/widget-action-1516-empty-call-args.mdl.
- All 81 non-.fail doctype-tests and all bug-tests check as before; only
  the new bug test changes (fail -> pass).

Not changed on purpose: `use building block X()` (an override list) and
`snippetcall (Params: ())` (a property map) stay errors with messages
that name what is expected; a flow-body call still requires its ().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ako
ako merged commit e5d7fc1 into main Oct 27, 2026
33 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[up#1606] Action: call microflow X() on a widget does not parse; the same call parses in a flow body

1 participant