Statifier.Validator.Checks.InitialTargets (Statifier v2.0.0)

Copy Markdown View Source

Check 3 (spec 3.2, 3.3, 3.6): every initial reference - a state's initial attribute, a state's <initial> element, and the document's own root initial attribute - resolves and lands on a legal target. Three reasons, in this precedence:

  1. {:initial_on_atomic_state, id}: a state carrying initial or initial_element that has nothing to default into - an empty states list, or a :parallel, :final, or :history kind. Fires first and suppresses every other reason for that state: an atomic state's initial cannot be resolved, checked for descendancy, or anything else, so reporting more than this one error would be two errors for one mistake.
  2. {:unresolved_initial, id}: an id in State.initial or Document.initial absent from Context.states. An <initial> element's own transition targets are not re-checked here for existence - check 2 already owns that; this check only asks Context.states the same question check 2 already answered, so it can skip the descendancy test below rather than re-reporting.
  3. {:initial_not_descendant, id, parent_id}: a resolved target - from State.initial or from an <initial> element's transitions - that is not among the containing state's named descendants (Context.descendant?/3, ancestry all the way up, not direct-child membership - spec 3.3/3.6 both say "descendants", which is where v1 is wrong). This check has no analog for Document.initial: spec 3.11's "additional requirement" ("all the states MUST be descendants of the containing <state> or <parallel> element") is written for <state> initial and for a <transition> inside <initial>/<history> only - <scxml> is named in neither. The root's own initial (spec 3.2.1) is typed only as "a legal state specification" (spec 3.11), which for a single resolved id demands nothing beyond existing - enterStates (Appendix D) walks up from any resolved target and adds its ancestors regardless of how deep it sits, so a document-level initial naming a descendant several levels down is legal and enters that descendant with every ancestor auto-added. There used to be a fourth reason here, {:initial_not_top_level, id}, requiring a resolved Document.initial id to be a direct child of <scxml>; it is gone because the spec never asked for that restriction - check_document_initial/2 below now stops at existence.

Summary

Functions

Returns, per state and for the document's own root initial attribute, one of :initial_on_atomic_state (the initial reference has nothing to default into and suppresses every other reason for that state), :unresolved_initial (the id does not resolve in Context.states), or :initial_not_descendant (a resolved target is not a descendant of the containing state - never fired for Document.initial, which has no containing state to be a descendant of). Returns [] when every initial reference in the document resolves and lands on a legal target.

Functions

check(document, context)

@spec check(
  document :: Statifier.Document.t(),
  context :: Statifier.Validator.Context.t()
) :: [
  Statifier.Validator.Error.t()
]

Returns, per state and for the document's own root initial attribute, one of :initial_on_atomic_state (the initial reference has nothing to default into and suppresses every other reason for that state), :unresolved_initial (the id does not resolve in Context.states), or :initial_not_descendant (a resolved target is not a descendant of the containing state - never fired for Document.initial, which has no containing state to be a descendant of). Returns [] when every initial reference in the document resolves and lands on a legal target.