# `Statifier.Validator.Checks.Send`
[🔗](https://github.com/riddler/statifier-ex/blob/v2.0.0/lib/statifier/validator/checks/send.ex#L1)

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`/`delayexpr` half of that constraint.
- `{:send_delay_and_internal_target}` - the other half of the same
  constraint: `delay` or `delayexpr` alongside a literal
  `target="_internal"` (or `target="#_internal"`, decision 6). Detectable
  only when `target` is a literal attribute - with `targetexpr` the 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.exs` and
  `test/scxml_tests/mandatory/scxml_event_processor/test354_test.exs`
  both carry `<send event="..."><content>123</content></send>`, and both
  carry the `send_elements` feature atom. Enforcing the constraint at
  error severity would reject two mandatory tests the moment that atom
  is flipped. No corpus file specifies `<content>` without `event`, so
  the pairwise checks above reject nothing that exists.
- 6.2.3 contradicts 6.2.2's own attribute table. The `event` row 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 no `type` satisfies "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.

# `check`

```elixir
@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.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
