Repository navigation
feat(core): refuse a build phase no listener is wired for; ban expected/actual - #422
Merged
Merged
Conversation
martin-fleck-at
force-pushed
the
feat/phase-and-error-name-checks
branch
2 times, most recently
from
October 13, 2026 15:44
22f93c6 to
e8a764d
Compare
…ed/actual An AST extension or a build-phase pass could declare any DocumentState, and one outside the phases the pipeline wires was accepted and never fired, which reads as the derivation never running. And an error class declaring both `expected` and `actual` is read by vitest as an assertion failure, which only a comment on ConflictError recorded. - Type `phase` on AstExtension and BuildPhasePass as a union of the wired phases, with a readonly `all()` namespace beside each, as IntegrityPhase - Rename the field from `state` to `phase`, the word the pipeline and the Langium hooks use, and the services' protected bucket members with it - Make both `register` methods throw on a phase outside `all()`, for a cast or a JavaScript caller the type cannot stop - Wire the pipeline's listeners from `all()`, so the wired set and the accepted set are one list - Add the ast-grep rule error-expected-and-actual over source and test trees, matching quoted, computed and abstract members too, and shrink ConflictError's comment to the constraint - Name that rule and the connection-logger rule in conventions, so CONTRIBUTING can describe check:ast-grep as the rules conventions name Relates to #178
martin-fleck-at
force-pushed
the
feat/phase-and-error-name-checks
branch
from
October 13, 2026 15:56
e8a764d to
6c025bc
Compare
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.
AstExtensionandBuildPhasePassdeclared their phase as anyDocumentState, and a registration at a phase the pipeline wires no listener for was accepted and never fired. Both now declarephaseas a union of the wired phases,AstExtensionPhaseandBuildPassPhase, each with anall()namespace that the pipeline wires its listeners from and thatregisterrefuses a value outside of, asIntegrityPhasealready does. An error class declaring bothexpectedandactualis read by vitest as an assertion failure; a new ast-grep rule,error-expected-and-actual, holds that over source and test trees, and conventions name it. conventions also gains the line for the existingconnection-without-loggerrule, so CONTRIBUTING can describecheck:ast-grepas the rules conventions names.Breaking changes
AstExtension.stateandBuildPhasePass.stateare renamed tophase.phaseis typedAstExtensionPhaseorBuildPassPhase; aDocumentState-typed variable no longer assigns to it.phaseBuckets,phaseBucketsForandgetPhaseBucket, formerlystate….registercall, in aconst, a factory or an array, needs anAstExtensionorBuildPhasePassannotation, oras constonphase; written inline it compiles as before.Decisions and their cost
BuildPhasePasshad the same hole and the same private wiring list; guarding one and filing the other would leave the class half fixed.phase, one word.IntegrityRulealready saysphase, the Langium hooks areonBuildPhaseandonDocumentPhase, andDocumentStatestays the name of what a phase is a value of. Cost: the rename above.Error. The throw fires at construction and nothing reacts to it at runtime; a code would be surface for nobody, and the sibling integrity guard is plain.extendNode,extendDocumentandrunPasseskeepphase: DocumentState; an unwired phase only finds an empty bucket there, so narrowing them would be one more break with nothing behind it.BuildPassPhase, besideBuildPhasePass.BuildPhasePassPhaserepeats the word, andBuildPassPhasereads as the phase of a build pass. The two are close in spelling, but one is the item interface and the other a union of enum values, so swapping them fails to compile rather than misbehaves.['expected']creates the same runtime property, so the rule matches those forms; a static member is flagged too, since excluding it buys nothing.How I know it works
phasetype back toDocumentStatereddens the types test with an unused@ts-expect-error.errors.tsfailsast-grep scan; the rule's test file holds fifteen invalid and six valid shapes, including the parameter-property, quoted, computed, abstract, declared, accessor and no-heritage cases, and the hierarchy split as not flagged.Part of #178.