Skip to content

fix: a constant in an XPath constraint — parse it, refuse it (CE0161), stop losing it (#1685) - #1739

Merged
ako merged 1 commit into
mainfrom
fix/1685-xpath-grammar-gaps
Oct 27, 2026
Merged

ako merged 1 commit into
mainfrom
fix/1685-xpath-grammar-gaps

Conversation

@ako

@ako ako commented Oct 26, 2026

Copy link
Copy Markdown
Owner

Closes #1685. Investigated #1297 (mirror of mendixlabs#1281): already fixed on main by cd93d84 — evidence below, no change.

#1685 — a constant in an XPath constraint: a parse error, and silently lost on re-render

The issue's expected outcome was wrong. It asked for @Module.Constant to be stored as a working XPath value. Measured with mx check, Mendix rejects a constant in an XPath constraint in every slot (CE0161): Integer, String and Boolean constants in a retrieve; a list view database source; an access rule (measured under Production security — with security Off mx check validates no access rule at all); a user task's targeting. An unknown constant is the same CE0161, not CE1613 (XPath never resolves the name). 11.15.0 agrees. Control: declare $Limit Integer = @Shop.C_Limit; then [Total > $Limit] builds clean. So this PR makes the form parse, and refuses it with the workaround — rather than a parse error at Shop, or a silent loss.

What failed on main:

  • The issue's script: mismatched input 'Shop' expecting ']'.
  • Three silent losses (reproduced on 11.13): a page data source where Total > @Shop.C_Limit stored [Total > ]; a stored [Total > @Shop.C_Limit] described as where Total > @;; a long quoted constraint under --no-check stored [Total > @]. Cause: the lenient re-parse (ParseXPathConstraint) returned ok with an attribute named @, and the page renderer had no constant case.

Fix:

  • Grammar (MDLPage.g4): xpathConstantRef : AT xpathQualifiedName as an alternative of xpathValueExpr — a value, never a path step; AT removed from xpathWord, so [Name = @] is now a syntax error. No existing XPath form changed meaning (visitor, xpathrefs, executor, conformance, linter, catalog, syntax packages pass).
  • Visitor buildXPathConstantRef (nil-safe on a recovered parse); page renderer case ConstantRefExpr.
  • New rule MDL-XPATH03 (validate_xpath_constants.go, in ValidateProgram): plain check and exec refuse a constant in an XPath slot without a project, naming the slot (walkXPathSlots) and the variable workaround; an excluded flow gets a warning.

Verified: visitor and validator tests, and a PedApp end-to-end test writing the constraint (via the executor, past the refusal) into a retrieve, a list view and an access rule, describing all three back, re-executing with no file changed, then rename constant — "Updated 3 reference(s)" (the rename-through-XPath test #1673 could not have). Reverted: grammar → the parse error and [Total > @]; renderer only → [Total > ]; check stubbed → every refusal test fails. Real 11.13 app: check and exec refuse with MDL-XPATH03; --no-check stores every slot verbatim; describe now prints the constraints and describe → exec is "5 documents already in sync"; rename constant "Updated 5 reference(s) in 4 document(s)" (mx check is the same 5× CE0161 before and after, as the measurement says). TestMxCheck_DoctypeScripts 02, 02b, 03, 08, 16-xpath, 24, navigation-offline-sync pass.

#1297 — two predicates on one XPath step: already fixed on main

Fixed by cd93d84 ("accept consecutive predicates on one XPath step", bug-test 1281-consecutive-xpath-step-predicates.mdl); the upstream reporter confirmed it fixed in v0.26.0. With the baseline binary (aa97a0a) on a fresh 11.13 app, check passes and these all exec and describe identically: the issue's two shapes (path continues after the step / ends on it), a page data source, an access rule, three predicates on a step, a nested two-predicate step, inside not(), sibling groups; describe → exec "5 documents in sync"; mx check 0 errors including under Production security. Only the removed quoted grant form where '[…]' still fails, unrelated to predicates.

All

Full go test ./..., go vet, gofmt; CI's doc checks pass locally. Bug-tests xpath-1685-constant-in-constraint.fail.mdl, xpath-1685-constant-through-variable.mdl; doctype section in 16-xpath-examples.mdl; finding recorded; xpath-constraints skill, MDL_QUICK_REFERENCE.md, mxcli syntax xpath, docs-site activity-types.md. Not verified: Studio Pro's UI; offline sync constraints (refused by the same rule, not measured).

Noticed, not fixed

  1. TestMxCheck_DoctypeScripts tolerates CE0161 globally (roundtrip_doctype_test.go ~417, "XPath serializer limitation") — the corpus cannot catch a CE0161 regression.
  2. doctype-tests/navigation-offline-sync.mdl is CE0161 at PhoneOffline on main too (hidden by that allowance; likely contains(Name, 'ab') in a sync constraint).
  3. doctype-tests/16-xpath-examples.mdl has a stale TODO saying nested predicates are unsupported.
  4. A constant used only in an XPath likely gets no CATALOG.REFS edge (moot now that it is refused).

🤖 Generated with Claude Code

…s it on re-render (#1685)

`retrieve $Items from Shop.Item where [Total > @Shop.C_Limit];` failed with
"mismatched input 'Shop' expecting ']'", which reads as a gap in MDL. Three
silent losses sat behind it: a data source written `where Total > @Shop.C_Limit`
stored `[Total > ]`; describe of a stored constraint holding a constant, on an
entity with an enumeration attribute, printed `where Total > @;`; and a long
one written in the quoted form was laid out and stored as `[Total > @]`.

Cause: the XPath grammar had no constant. `@` was admitted by the negated
xpathWord set as a one-word name, so the strict parse failed at the module
name, and ParseXPathConstraint (error listeners off) recovered past it and
returned ok with an attribute called "@" — every re-render of that parse
(enum enrichment, FormatXPathConstraint, check's member walk) dropped the
constant. The page-side xpathExprToString had no ConstantRefExpr case and
rendered "" for the expression-grammar form.

The issue expected the constant to be stored and built. Measured, mxbuild
rejects it: on 11.13.0, each case alone in a fresh app, CE0161 "Error(s) in
XPath constraint." for an Integer, String and Boolean constant in a retrieve,
in a list view's database source, in an access rule (production security —
with security off mx check validates no access rule, a garbage `[Total >]`
passes) and in a user task's targeting; an unknown constant is the same
CE0161, not CE1613. The retrieve is CE0161 on 11.15.0 too. The control,
`declare $Limit Integer = @Shop.C_Limit;` then `[Total > $Limit]`, is clean.

Fix: `@Module.Name` is an xpathValueExpr (xpathConstantRef — a value, not a
path step) and AT is out of xpathWord; the visitor builds ast.ConstantRefExpr
and the page renderer prints it, so every slot stores the constraint as
written and describe prints it back. `mxcli check` (ValidateProgram, so exec
too; no project needed) refuses a constant in any XPath slot as MDL-XPATH03
with the variable workaround; an excluded flow warns. walkXPathConstraints
gains a sibling that names the slot.

Verification: visitor, check and executor tests; the PedApp test writes the
constraint into a retrieve, a list view and an access rule, describes all
three back, re-executes the descriptions with no file changed, and renames
the constant through them ("Updated 3 reference(s)"). Reverting the grammar
fails every test with the reported parse error and `[Total > @]`; reverting
only the renderer case gives `[Total > ]`; stubbing the check fails the
refusal tests. Real 11.13.0 app: baseline describe printed `where Total > @;`
for both retrieves, fixed describe prints the constraints and describe -> exec
reports 5 documents in sync; `rename constant` updated 5 references in 4
documents, and mx check is the same five CE0161 before and after the rename
with the variable control clean. TestMxCheck_DoctypeScripts passes for the
microflow, nanoflow, page, security, xpath, workflow and offline-sync scripts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

XPath constraints cannot reference a constant: where [Total > @Mod.C] is a parse error

1 participant