squonk-ast 2.0.0

Dialect-agnostic SQL abstract syntax tree for the squonk toolkit
Documentation
// SPDX-License-Identifier: MIT
// Copyright (c) 2026 Moderately AI Inc.

//! Extension-point plumbing (ADR-0009): the `Spanned` / `Extension` traits and the uninhabited `NoExt` default.

use crate::vocab::Span;
use std::fmt::Debug;
use std::hash::Hash;

/// Return the source span for an AST node.
///
/// Concrete node impls are generated by `squonk-sourcegen`; M1 only defines the
/// trait so extension nodes can promise span-aware behaviour.
pub trait Spanned {
    /// Return the span for this value.
    fn span(&self) -> Span;
}

/// The stock parser extension type.
///
/// This type is uninhabited, so `Other(NoExt)` variants are statically dead and
/// do not increase stock AST node sizes.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
pub enum NoExt {}

impl Spanned for NoExt {
    fn span(&self) -> Span {
        match *self {}
    }
}

/// A custom AST extension node.
///
/// The extension design also names `Render` and `Visit` bounds, but M1 deliberately omits
/// them here to avoid an AST -> renderer/generated-code layering cycle. The
/// renderer and visitors add those bounds at their own call sites.
pub trait Extension: Clone + Debug + Eq + Hash + Spanned {}

// Blanket on purpose — ADR-0009 fixes `Extension` as a bound-alias forever (behaviour
// lands on `Render`/`Visit` at call sites, never here); new behaviour = a new opt-in trait.
impl<T: Clone + Debug + Eq + Hash + Spanned> Extension for T {}