Skip to content
Beskid Platform specification

Beskid

Jump to a Beskid service

Beskid

Jump to a Beskid service

Analysis, query, and diagnostics facades - Design model

Platform spec article

Analysis, query, and diagnostics facades - Design model

Spec standingStandard

Owner
Piotr Mikstacki
Submitter
Piotr Mikstacki

This article documents the design model for Analysis, query, and diagnostics facades.

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.

  • 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.
  • 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.
  • 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.

Beskid.Compiler.Diagnostics (and internal meta emitters) must:

  1. Emit SemanticDiagnostic values compatible with run_rules output (same severity enum, string diagnostic codes matching registered ids).
  2. 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.rs when introduced.
  3. Never bypass span_to_sourcespan / miette adapters used elsewhere so LSP label offsets remain accurate.

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.

Facade version 0.4.0 (see QueryFacadeVersion()). All functions lower to beskid_analysis::query walkers over a host-built SyntaxSnapshot.

Type / functionSemantics
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
SyntaxQueryCursor at start with bounds
At / AtProgramEntry from NodeRef or Program root
DescendantsPre-order traversal (same order as Rust Descendants)
ChildrenDirect children only
Parent / AncestorsImmediate parent and root-to-parent chain via snapshot parent table
Span / TrySpanRequired span lookup and optional span lookup for a NodeRef
OfKind / FindFirstFilter by Beskid.Syntax.Nodes.NodeKind
As*Host downcast to concrete mirrored type (one helper per kind with a shape type)
Walk + SyntaxVisitorDepth-first enter/exit (maps to AstWalker)
SyntaxPipelineQuery-driven transformation plan over NodeRef selections
Select + WhereKindBuild a deterministic selection set from a root query
Replace / Remove / InsertBefore / InsertAfterMinimal rewrite operations over selected NodeRefs
ApplyValidates 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(nodeRef) must succeed when nodeRef.syntaxGenerationId matches the active snapshot generation and nodeId resolves; otherwise host emits E1882 and returns an empty/default result per facade contract.
  • TrySpan(nodeRef) must never emit diagnostics and returns none for 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.