Spec 4.3.2: "In a conformant SCXML document, <else> MUST occur after all
<elseif> tags." Statifier.Lowering.Builders.build_if/2 has already
partitioned an <if>'s children into branches by the time a
%Statifier.Document.If{} reaches the validator, so this check reads
branch order directly rather than re-parsing <elseif>/<else> element
names.
A branch's cond is nil only for the <else> branch. cond is
required on both <if> (4.3.1) and <elseif> (4.4.1), and
Statifier.Lowering.Builders.build_if/2 and build_elseif/2 report
missing_attribute and build no node at all when it is absent, so a
cond-less non-<else> branch never reaches the validator. This check
still keys off cond: nil rather than an explicit "is an <else>" flag,
because that is the shape Statifier.Document.If.Branch actually carries;
it just does not have a second case to distinguish. Once the first
nil-cond branch in document order is seen, every branch after it is
illegal:
- another
nil-cond branch is a second<else>, reported{:if_duplicate_else}at that branch's ownlocation - a
cond-carrying branch is an<elseif>after an<else>, reported{:if_elseif_after_else}at that branch's ownlocation
Classifying by the offending branch's own shape (rather than, say,
flagging the first nil-cond branch for "not being last") is what keeps
the two-<else>s case reporting exactly once, at the second <else>,
instead of also flagging the first as "not last".
This check walks every state's own onentry/onexit blocks, every
state's own transitions (both plain and a :history state's default),
and every state's <initial> element's own transition - the same three
places Statifier.Validator.Checks.Assign walks - and recurses into every
<if> nested inside another <if>'s branch content (SCION's
if_else/test0 nests two deep), since spec 4.3.2 explicitly allows
nested <if> as executable content. It recurses into a <foreach>'s own
content the same way, so an <if> written inside a loop body (test153)
is not invisible to this check either.
Summary
Functions
Walks every <if> reachable through a state's onentry/onexit blocks or
its own (and its <initial> element's) transitions, recursing into nested
<if>s, and returns an :if_elseif_after_else or :if_duplicate_else
error for each branch that follows the first nil-cond branch of its own
<if>. Returns [] when every <if> in the document keeps its <else>
(if any) last and singular.
Functions
@spec check( document :: Statifier.Document.t(), context :: Statifier.Validator.Context.t() ) :: [ Statifier.Validator.Error.t() ]
Walks every <if> reachable through a state's onentry/onexit blocks or
its own (and its <initial> element's) transitions, recursing into nested
<if>s, and returns an :if_elseif_after_else or :if_duplicate_else
error for each branch that follows the first nil-cond branch of its own
<if>. Returns [] when every <if> in the document keeps its <else>
(if any) last and singular.