Fix unreachableTrigger false positives for cross-spec and conditional trigger emissions (#19)#35
Merged
Conversation
… trigger emissions (#19) Three fixes to the unreachable-trigger analysis, applied to both the Rust analyzer and the TypeScript extension analyzer: - Accept qualified trigger calls (`when: alias/Trigger(...)`) as a valid trigger form in the Rust checker. The language reference documents this under "Responding to external triggers"; previously the Rust CLI rejected it with an `allium.rule.invalidTrigger` error. - Resolve trigger reachability across `use` imports in the multi-file CLI. The cross-module context now carries a per-alias map of the trigger names each module provides or emits; `alias/Trigger` subscriptions are checked against the aliased module and flagged only when it determinately lacks the trigger. Aliases pointing outside the check set are never flagged, and unqualified triggers emitted by an imported module count as reachable. The single-file TS analyzer skips qualified subscriptions entirely since it cannot see the emitting module. - Register trigger emissions made on `if`/`else if`/`else` branches and inside `for` iterations of `ensures:` values. Rust recurses into Conditional/For expressions; TS adds a branch-call lane scoped to ensures clause extents. Both keep the leading-call-only convention. Fixes #19 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Fixes #19.
Three fixes to the unreachable-trigger analysis, applied to both the Rust analyzer (
crates/allium-parser/src/analysis.rs) and the TypeScript extension analyzer (extensions/allium/src/language-tools/analyzer.ts).1. Qualified trigger calls are a valid trigger form
when: alias/Trigger(...)is documented in the language reference ("Responding to external triggers"), and the TSisValidTriggerShapealready accepted it — but the Rustis_valid_triggerrejected it with anallium.rule.invalidTriggererror, makingallium checkexit non-zero on the issue's minimal repro (this was actually worse than the info-level diagnostics the issue reports).Expr::Callwith aQualifiedNamefunction is now a valid trigger.2. Cross-spec trigger reachability across
useimportsThe CLI's
CrossModuleContextnow carriesimported_triggers: per file, ause-alias → set of trigger names the aliased module provides (surfaces) or emits (rule ensures), computed by the newallium_parser::collect_trigger_outputs. The unreachable-trigger check (both the diagnostics lane and theanalysefindings lane) resolves:alias/Triggeragainst the aliased module — flagged only when the module is in the check set and determinately lacks the trigger, with a message naming the module:... but imported module 'emitter' does not provide or emit it.github.com/...coordinates) — never flagged, reachability is unknowableThe single-file TS analyzer cannot see other modules, so it now skips qualified subscriptions entirely (call name preceded by
/).3. Emissions inside
if/else if/elsebranches andforiterationsA trigger emitted as the leading call of a conditional branch or
forbody inside anensures:value now registers as an emission. (Contrary to the issue's grammar-gap theory, the Rust parser already parsed these fine — the collector just didn't recurse.) Rust:collect_leading_ensures_callrecurses intoConditional/For; TS: a branch-call lane scoped to ensures clause extents via the newensuresClauseRegionshelper, so branch calls in other clauses (e.g. aletconditional) don't register.foris included on both sides so the two implementations can't diverge on nesting.Verification
allium check emitter.allium listener.alliumexits 0 with noinvalidTrigger/unreachableTriggerforemitter/Pinged;analyselikewiseelse-branchSensorAdvertDecodedemission satisfies its listenernpm run lintcleanSpecs updated in
docs/project/specs/and the parity notes indocs/project/rust-checker-parity.md.🤖 Generated with Claude Code