Repository navigation
fix: references to members/associations that do not exist pass check (#1690, #1646, #1697) - #1738
Merged
Merged
Conversation
…1690) `retrieve $C from $O/Ord_Cust;` - an association path written with the association's short name - passed `check -p --references` and exec and was stored as the association ".Ord_Cust": mx check CE1613 "The selected association '.Ord_Cust' no longer exists.", and describe printed `$O/.Ord_Cust`, which does not parse. Cause: addRetrieveAction joined the AST's empty Module to the name, and nothing on the check side looked at a bare retrieve association. MDL-RETRIEVE03's qualifyAssoc guessed the start entity's module - a second answer to the same question. Fix: one resolver, resolveBareAssociation (bare_association.go), a function of plain data - the name, the start entity's generalization chain, and an association catalog with both ends of every association. A bare name resolves when exactly one association of that name has an end on the chain (either end; a specialization inherits) and is stored qualified; none or several is refused, with the qualified spelling to write. The builder feeds it the project as the earlier statements left it, check --references the project plus the associations declared before the flow (validate_flow_member_refs.go), so the two agree; exec also refuses a bare name it cannot resolve rather than writing ".Name". The describe speller and MDL-RETRIEVE03 ask the same resolver, so a re-run of the script reports "Unchanged microflow". Verification: retrieve_bare_association_1690_test.go (PedApp, check and exec agree; stored unit bytes; describe; re-run unchanged; refusals; speller). Reverting the fix fails every case with ".Ord_Cust" stored and check/exec accepting. Real 11.13.0 app: the reported repro went from CE1613 to 0 errors with "Shop.Ord_Cust" stored and described; owner side, TO side, specialization, cross-module inherited and Account/UserRoles -> System.UserRoles all resolve and build at 0 errors; a missing and an ambiguous name are refused by check, exec and exec --no-check. No doctype or bug-test script changes verdict. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…heck and exec (#1646) `find $l by Numbr = '42'`, and filter / sort / sum by a member the list's entity does not have, passed `check -p` and exec; mx check then reported CE1613 "The selected attribute 'Shop.Order.Numbr' no longer exists." once per activity. An aggregate by an INHERITED attribute was broken the same way: `sum $sl by Total` was written as "Shop.SubOrd.Total" (CE1613). Cause: the builder's member resolution (resolveMemberChange via resolveListOperationMember, the sort branch, the aggregate) fell back to writing `<entity>.<name>` when the name resolved to nothing, and the aggregate never walked the generalization chain. check --references judged CREATE/CHANGE members and retrieve constraints, but no list operation member. Fix: one chain walk, memberVerdictOver (member_verdict.go), over a per-link interface - read from the backend by the builder, from the script over the backend by check - so the script overlay changes how a link is read, never the rule. The builder refuses a member established as missing with listMemberRefusal, the message check reports, so exec without the preflight says what check says; resolveMemberOnEntity now delegates to the same walk. The aggregate qualifies an attribute with the entity that declares it. A sort may read createdDate/changedDate where the root stores them; an aggregate may not (CE0226, measured). Verification: list_member_refs_1646_test.go (PedApp, check and exec agree): the four reported shapes plus maximum, bare `owner` and an aggregate over createdDate are refused; own/inherited attributes, bare and inherited cross-module associations, System.owner, sorts on a stored system date, an untyped list and a member the script adds first stay accepted; the inherited aggregate stores the declaring entity. Reverting the fix fails the refusal tests with check and exec accepting and the aggregate storing B1646.SubOrd.Total. Real 11.13.0 app: the repro went from 4 x CE1613 to refused by check and exec; the controls build at 0 errors. No doctype or bug-test script changes verdict. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…id (#1697) `toString($User/id)`, `$A/id = $B/id` and `$User/Bogus` passed `check -p --references` - the type pass said "Expression types OK" - and exec wrote them; mx check reported CE0117 "Error(s) in expression." (mirror of mendixlabs#1650). Cause: nothing resolved the member steps of an expression's `$Var/...` path against the variable's entity. The type pass infers an unknown attribute as KindUnknown and stays silent, and it is not part of exec's preflight anyway. Fix: the reference pass - which check --references and exec's preflight both run - follows every `$Var/...` path in a flow statement's expressions through the same member verdict as #1646, across association hops: an attribute the entity (or anything it extends) does not have, a bare association, an association that does not exist or does not start at the entity are reported, and `id` gets the compare-the-objects / CommunityCommons.getGUID hint. The hop's target entity may follow the association qualified or bare (the builder qualifies a bare one, #829). Paths are collected by reflecting over each statement's fields, so a statement kind added later is covered; nested bodies are left to the typed walk. Verification: expression_member_refs_1697_test.go (PedApp): the twelve shapes measured as CE0117 on 11.13.0 are reported, the ten measured clean are not (plus the bare target entity step), the System.User repro and a loop/IF body. Reverting the fix fails every report case. Real 11.13.0 app: the repro went from 2 x CE0117 to refused by check and exec; the controls build at 0 errors. Every CE0117 shape was measured in an app with no other error - a CE1613 elsewhere suppresses them all. No doctype or bug-test script changes verdict. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ences # Conflicts: # mdl/executor/validate_member_refs.go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1690. Closes #1646. Closes #1697 (mirror of mendixlabs#1650).
References to a member or association that does not exist passed
checkandexec; onlymx checknoticed — and in #1690 the model was written broken. One commit per issue on one shared core.Shared core:
memberVerdictOver(newmember_verdict.go) is the single walk over an entity's generalization chain. Exec's builder reads each link from the backend (backendMemberModel), check from the script with the backend as fallback (scriptMemberModel) — the overlay changes how a link is read, never the rule. The existing CREATE/CHANGE member check (resolveMemberOnEntity, #1048) now delegates to it. Inherited members, System members and members of modules that are not loaded are not refused.Measurement trap worth knowing: while any CE1613 exists in an app,
mx checkreports no CE0117 and no CE0226. Every control was re-measured in an otherwise clean app — one earlier "clean" result was masked (an aggregate overcreatedDateis CE0226, now refused).#1690 — a bare association in a retrieve was stored with an empty module
retrieve $C from $O/Ord_Cust;passed check and exec and was stored as.Ord_Cust(CE1613; describe printed$O/.Ord_Cust, which does not parse). Cause: the builder builts.Source.Module + "." + s.Source.Name; check never looked at a bare retrieve association, and MDL-RETRIEVE03 guessed the start entity's module. Fix: a bare name resolves when exactly one association of that name has an end on the start entity's generalization chain (either end; specializations inherit) and is stored qualified; no match or several are refused by check and exec with the qualified spelling to write, and exec refuses a bare name it cannot resolve rather than writing.Name. Describe's speller and MDL-RETRIEVE03 use the same resolver, so a re-run is Unchanged.Verified: PedApp tests (check and exec must agree): stored bytes, describe, re-run; owner side, TO side, specialization, inherited cross-module association,
Administration.Account/UserRoles→System.UserRoles; script-declared association; missing, not-an-end, ambiguous, unknown start entity. Reverted: every case stores.Ord_Cust. 11.13.0: before CE1613 andAssociationId ".Ord_Cust"; after 0 errors,"Shop.Ord_Cust", describe$O/Shop.Ord_Cust.#1646 — find / filter / sort / aggregate by a member the entity lacks
Cause: the builder fabricated
<entity>.<name>for an unresolved member (resolveMemberChangefallback viaresolveListOperationMember, sort, aggregate). Fix: the builder refuses a member established as missing with check's own message (listMemberRefusal, asserted identical); check'slistMemberuses the same verdict. Sibling fixed: the aggregate never walked the generalization chain —sum $sl by TotalwithTotalinherited was stored asShop.SubOrd.Total(CE1613) although the member exists; it now qualifies with the declaring entity. A sort may readcreatedDate/changedDatewhere the root stores them (clean); an aggregate cannot (CE0226, measured) — refused, naming CE0226.Verified: refusals for the four reported shapes,
maximumby a missing member, bareowner(hintSystem.owner), aggregate overcreatedDate; controls (own/inherited attributes, bare and inherited cross-module associations #1637,System.owner#1629, sorts on stored system dates, an untyped call-result list, a member the script adds with ALTER first). Reverted: refusals accepted, aggregate storesSubOrd.Total. 11.13.0: before 4× CE1613; after refused; an 11-statement control microflow builds at 0 errors.#1697 —
$Var/<member>in an expression for a member the entity lacks (incl.id)Cause: nothing resolved an expression path's member steps (the type pass infers an unknown attribute as unknown, silently). Fix (check side, in the reference pass, which exec's preflight also runs):
validate_flow_member_refs.gofollows each$Var/...path through the same verdict across association hops and reports a missing attribute, a bare association, an association that does not exist or does not start at the entity, andid(hint: compare the objects, orCommunityCommons.getGUID). Paths are collected by reflecting over each statement's fields, so new statement kinds are covered. The builder still writes expressions verbatim, soexec --no-checkdoes not refuse these.Verified: the 12 shapes measured as CE0117 on 11.13.0 are reported; 11 measured-clean shapes are not (incl.
$S/createdDateon a specialization, a bare hop target). Reverted: every report case fails. 11.13.0: before 2× CE0117; after refused by check and exec.All
.faildoctype scripts give identical output with baseline and fixed binaries (check, andcheck -p <blank 11.13> --references); 863 bug-test scripts swept — the one change (829-cross-module-assoc-expression, a bare hop target) was fixed.go test ./...,go vet, gofmt; CI's doc checks pass locally. Bug-testsreferences-{1690,1646,1697}-*.mdl; findings recorded;write-microflows/reference/data-operations.md, docs-siteactivity-types.md.validate_member_refs.go(fix: check misses CE1406 for Hidden entities of Protected modules and on call results (#1699, #1701) #1734 addsscto awalkTypedFlowcall; this PR adds a line inside its callback) — the combination was resolved in a scratch merge and passesmdl/executor+ the conformance gate; once both land, these checks also judgecall microflowresults.Noticed, not fixed
$O/System.owner/Nameis CE0117 (the builder does not insert theSystem.Userstep).walkTypedFlowdoes not descend into enum-split / inheritance-split case bodies, so every member check is silent insidecasebodies.walkTypedFlowdoes not type a bare-association retrieve's result.exec --no-check, CREATE/CHANGE with a missing member and a bad expression member are still written as-is.🤖 Generated with Claude Code