Spec 6.2.2's mutual-exclusion constraints on <send>'s attributes and
children, each reported at the <send> element's own location:
{:send_event_and_eventexpr}- "event: Must not occur with 'eventexpr'."{:send_target_and_targetexpr}- "target: Must not occur with 'targetexpr'."{:send_type_and_typeexpr}- "type: Must not occur with 'typeexpr'."{:send_id_and_idlocation}- "id: Must not occur with 'idlocation'."{:send_delay_and_delayexpr}- "delay: Must not occur with 'delayexpr' or when the attribute 'target' has the value '_internal'."- the
delay/delayexprhalf of that constraint.
- the
{:send_delay_and_internal_target}- the other half of the same constraint:delayordelayexpralongside a literaltarget="_internal"(ortarget="#_internal", decision 6). Detectable only whentargetis a literal attribute - withtargetexprthe value is not known until execute time, so this check stays silent there rather than guessing.{:send_namelist_and_content}/{:send_param_and_content}- 6.2.3: "A conformant document MUST NOT specify 'namelist' or<param>with<content>."
lib/statifier/document/send.ex makes every one of these pairs
simultaneously representable precisely so this check can report the
shape rather than lowering refusing to build it - the same division of
labour Checks.Invoke has for its own five constraints. Collect-all: a
<send> that trips more than one constraint reports every one it trips,
not just the first.
Deliberately not enforced: 6.2.3's "A conformant SCXML document MUST
specify exactly one of 'event', 'eventexpr' and <content>" as a
three-way constraint. Only the event/eventexpr pairwise exclusivity
above is checked; <content> alongside event passes.
Read literally, that sentence makes <send event="e"><content>...</content></send>
nonconformant, and 6.2.6 restates the same split ("the document author
can specify the message content in one of two mutually exclusive ways"),
so it is not a drafting slip in a single clause. The omission rests on
two things that outweigh the letter rather than on reading it away:
- The W3C's own mandatory conformance tests violate it.
test/scxml_tests/mandatory/send/test179_test.exsandtest/scxml_tests/mandatory/scxml_event_processor/test354_test.exsboth carry<send event="..."><content>123</content></send>, and both carry thesend_elementsfeature atom. Enforcing the constraint at error severity would reject two mandatory tests the moment that atom is flipped. No corpus file specifies<content>withoutevent, so the pairwise checks above reject nothing that exists. - 6.2.3 contradicts 6.2.2's own attribute table. The
eventrow reads "If the type is http://www.w3.org/TR/scxml/#SCXMLEventProcessor, either this attribute or 'eventexpr' must be present", and that processor is the default type (6.2.5) - so a<content>-only<send>with notypesatisfies "exactly one" and violates the event-presence requirement at the same time. The letter is unsatisfiable for a content-bearing send over the default processor, and the WG's test suite resolves it the way this check does.
ADR-0033's warning tier is the natural home for a document-directed MUST the engine has defined behavior for, and it was considered; a warning that fires on the W3C's own conformance suite is noise rather than diligence, so this stays silent. Do not "fix" the omission by reflex on finding 6.2.6: the behavior is forced by the corpus, and changing it needs the two facts above rebutted first.
The same clause also forbids zero of the three, and that half is
unchecked too: a <send/> with neither event, eventexpr, nor
<content> is accepted and produces an effect with event: nil. No
corpus file does this, so unlike the three-way constraint it has no
evidence against it - a fair warning-tier candidate if one is ever
wanted, left alone here because nothing needs it yet.
The walk reaches every block a <send> can appear in: a state's
onentry/onexit, its own (and its <initial> element's) transitions,
<if>/<foreach> bodies nested in any of those, and every <invoke>'s
<finalize> block - mirroring Checks.Assign's reach plus
Checks.Invoke's finalize walk, since a <send> inside <finalize>
is 6.5.2's other forbidden case (Checks.Invoke.forbidden/1) and still
has to satisfy 6.2.2's own constraints regardless of where it sits.
Summary
Functions
Walks every <send> in the document and returns one error per 6.2.2/6.2.3
constraint it violates. Returns [] when every <send> in the document
satisfies all eight.
Functions
@spec check( document :: Statifier.Document.t(), context :: Statifier.Validator.Context.t() ) :: [ Statifier.Validator.Error.t() ]
Walks every <send> in the document and returns one error per 6.2.2/6.2.3
constraint it violates. Returns [] when every <send> in the document
satisfies all eight.