Skip to main content

squonk_ast/ast/
ext.rs

1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 Moderately AI Inc.
3
4//! Extension-point plumbing (ADR-0009): the `Spanned` / `Extension` traits and the uninhabited `NoExt` default.
5
6use crate::vocab::Span;
7use std::fmt::Debug;
8use std::hash::Hash;
9
10/// Return the source span for an AST node.
11///
12/// Concrete node impls are generated by `squonk-sourcegen`; M1 only defines the
13/// trait so extension nodes can promise span-aware behaviour.
14pub trait Spanned {
15    /// Return the span for this value.
16    fn span(&self) -> Span;
17}
18
19/// The stock parser extension type.
20///
21/// This type is uninhabited, so `Other(NoExt)` variants are statically dead and
22/// do not increase stock AST node sizes.
23#[derive(Clone, Debug, PartialEq, Eq, Hash)]
24#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
25#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
26pub enum NoExt {}
27
28impl Spanned for NoExt {
29    fn span(&self) -> Span {
30        match *self {}
31    }
32}
33
34/// A custom AST extension node.
35///
36/// The extension design also names `Render` and `Visit` bounds, but M1 deliberately omits
37/// them here to avoid an AST -> renderer/generated-code layering cycle. The
38/// renderer and visitors add those bounds at their own call sites.
39pub trait Extension: Clone + Debug + Eq + Hash + Spanned {}
40
41// Blanket on purpose — ADR-0009 fixes `Extension` as a bound-alias forever (behaviour
42// lands on `Render`/`Visit` at call sites, never here); new behaviour = a new opt-in trait.
43impl<T: Clone + Debug + Eq + Hash + Spanned> Extension for T {}