Roadmap search
Versions, deliverables, workstreams, tasks, and pages
Analysis, query, and diagnostics facades - Design model
src/content/docs/platform-spec/compiler/compiler-mods/analysis-query-diagnostics-facade/design-model.mdx
import SpecArticleChrome from '@beskid/beskid-ui/platform-spec/SpecArticleChrome.astro';
<SpecArticleChrome />This article documents the design model for Analysis, query, and diagnostics facades.
Language alignment
Process pipelines express fluent queries that lower to bounded Rust walkers; Diagnostics raised here must use compiler-stable codes for parity with beskid_analysis / LSP.
Persistent entities
- Compilation instance — implicit handle to the compilation under construction (Compiler Mod SDK /
Beskid.Compiler.Compilation). - Syntax snapshot — immutable tree with stable node identities suitable for incremental keys.
- Capability tokens — host-granted permissions for I/O, diagnostics, and emit operations during mod execution.
Boundaries
- Mod SDK facades never bypass the host bridge for effects.
- Generation logic in the reference compiler remains Rust-internal; Beskid sees only the generated
Beskid.Compiler.*projection.
Anchored code paths
compiler/crates/beskid_analysis/src/analysis/— staged rules and semantic state.compiler/crates/beskid_analysis/src/resolve/— resolution products queryable from facades.compiler/crates/beskid_lsp/src/diagnostics.rs— diagnostic shaping for editor parity.
Diagnostic parity rules
Beskid.Compiler.Diagnostics (and internal meta emitters) must:
- Emit
SemanticDiagnosticvalues compatible withrun_rulesoutput (same severity enum, string diagnostic codes matching registered ids). - Allocate mod and mod-host codes only inside E1801–E1899 using the sub-ranges in Diagnostic code registry / design model; register each code in
compiler/crates/beskid_analysis/src/analysis/diagnostic_kinds.rswhen introduced. - Never bypass
span_to_sourcespan/ miette adapters used elsewhere so LSP label offsets remain accurate.
Query lowering and bounded walkers
Fluent Beskid.Compiler.Query plans must lower to explicit Rust walkers with documented upper bounds on work (max nodes visited, max depth). Hosts must abort with diagnostics (not panics) when bounds trip. Optional query_semantic_snapshot reads must declare the minimum staged snapshot version (ties to semantic.snapshot events in Compiler Mods area hub).
When the Rust compiler maintains compile-time composition / IoC state for user programs (Native dependency injection), that state must be materialized into the same versioned semantic snapshot family as other staged products (exported after composition.resolve) so mod analyzers can query it without special casing: Mod SDK query types project compiler-native graphs into Beskid-visible handles; they must not recompute or replace Rust-owned resolution unless a dedicated, separately specified capability allows a narrow, audited code path.
Beskid.Compiler.Query surface (normative)
Facade version 0.4.0 (see QueryFacadeVersion()). All functions lower to beskid_analysis::query walkers over a host-built SyntaxSnapshot.
| Type / function | Semantics |
|---|---|
NodeRef | { syntaxGenerationId, nodeId } — opaque, host-allocated, stable within a generation window |
NodeSpan | { start, end, lineStart, columnStart, lineEnd, columnEnd } for one node in one syntax generation |
QueryBounds | { maxNodes, maxDepth } — default host caps apply when zero |
SyntaxQuery | Cursor at start with bounds |
At / AtProgram | Entry from NodeRef or Program root |
Descendants | Pre-order traversal (same order as Rust Descendants) |
Children | Direct children only |
Parent / Ancestors | Immediate parent and root-to-parent chain via snapshot parent table |
Span / TrySpan | Required span lookup and optional span lookup for a NodeRef |
OfKind / FindFirst | Filter by Beskid.Syntax.Nodes.NodeKind |
As* | Host downcast to concrete mirrored type (one helper per kind with a shape type) |
Walk + SyntaxVisitor | Depth-first enter/exit (maps to AstWalker) |
SyntaxPipeline | Query-driven transformation plan over NodeRef selections |
Select + WhereKind | Build a deterministic selection set from a root query |
Replace / Remove / InsertBefore / InsertAfter | Minimal rewrite operations over selected NodeRefs |
Apply | Validates and commits a pipeline as one host operation |
Bound trip — When maxNodes or maxDepth is exceeded, the host must stop iteration, return no further nodes from that query, and emit diagnostic E1880 (QueryBoundsExceeded). Must not panic.
Traversal vs rewrite — Mod analyzers traverse with NodeRef only; Rewriter still receives concrete mirrored source/target types from the host after As* validation.
Span and pipeline invariants
Span(nodeRef)must succeed whennodeRef.syntaxGenerationIdmatches the active snapshot generation andnodeIdresolves; otherwise host emits E1882 and returns an empty/default result per facade contract.TrySpan(nodeRef)must never emit diagnostics and returnsnonefor stale or unknown refs.- Pipeline application is deterministic: operations are ordered by snapshot pre-order index, then by operation kind precedence (
Remove,Replace,InsertBefore,InsertAfter). - Hosts must reject conflicting operations over the same anchor with E1883 (
QueryPipelineConflict) instead of implicit overwrite behavior. - Hosts must reject generation-stale pipeline handles with E1884 (
QueryPipelineStaleGeneration) and keep the previous committed snapshot unchanged.