Skip to main content

elm_ast/
node.rs

1use std::hash::{Hash, Hasher};
2
3use crate::comment::Comment;
4use crate::span::Span;
5
6/// A syntax node with source span information attached.
7///
8/// This is the universal wrapper for AST nodes, analogous to:
9/// - `A.Located` in elm/compiler
10/// - `Node` in stil4m/elm-syntax
11/// - `Spanned` in many Rust parser crates
12///
13/// Each node carries optional leading comments — comments that appeared
14/// immediately before this node in source. This enables round-tripping
15/// of comments inside expressions (let-in, case-of, etc.).
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct Spanned<T> {
19    pub span: Span,
20    pub value: T,
21    /// Leading comments that appeared immediately before this node in source.
22    #[cfg_attr(
23        feature = "serde",
24        serde(default, skip_serializing_if = "Vec::is_empty")
25    )]
26    pub comments: Vec<Spanned<Comment>>,
27}
28
29// Manual Hash: exclude comments so hashing is based on span + value only.
30impl<T: Hash> Hash for Spanned<T> {
31    fn hash<H: Hasher>(&self, state: &mut H) {
32        self.span.hash(state);
33        self.value.hash(state);
34    }
35}
36
37impl<T> Spanned<T> {
38    /// Create a new spanned node with no leading comments.
39    pub fn new(span: Span, value: T) -> Self {
40        Self {
41            span,
42            value,
43            comments: Vec::new(),
44        }
45    }
46
47    /// Create a spanned node with a dummy span (for synthesized/constructed nodes).
48    pub fn dummy(value: T) -> Self {
49        Self {
50            span: Span::dummy(),
51            value,
52            comments: Vec::new(),
53        }
54    }
55
56    /// Map the inner value, preserving the span and comments.
57    pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Spanned<U> {
58        Spanned {
59            span: self.span,
60            value: f(self.value),
61            comments: self.comments,
62        }
63    }
64
65    /// Attach leading comments to this node.
66    pub fn with_comments(mut self, comments: Vec<Spanned<Comment>>) -> Self {
67        self.comments = comments;
68        self
69    }
70
71    /// Get a reference to the inner value.
72    pub fn inner(&self) -> &T {
73        &self.value
74    }
75
76    /// Get a mutable reference to the inner value.
77    pub fn inner_mut(&mut self) -> &mut T {
78        &mut self.value
79    }
80
81    /// Unwrap the spanned node, discarding the span.
82    pub fn into_inner(self) -> T {
83        self.value
84    }
85}