//! Working with abstract syntax trees. //! //! In rowan, syntax trees are transient objects. That means that we create //! trees when we need them, and tear them down to save memory. In this //! architecture, hanging on to a particular syntax node for a long time is //! ill-advisable, as that keeps the whole tree resident. //! //! Instead, we provide a [`SyntaxNodePtr`] type, which stores information about //! the _location_ of a particular syntax node in a tree. It's a small type //! which can be cheaply stored, and which can be resolved to a real //! [`SyntaxNode`] when necessary. //! //! We also provide an [`AstNode`] trait for typed AST wrapper APIs over rowan //! nodes. use std::{ fmt, hash::{Hash, Hasher}, iter::successors, marker::PhantomData, }; use crate::{Language, SyntaxNode, SyntaxNodeChildren, TextRange}; /// The main trait to go from untyped [`SyntaxNode`] to a typed AST. The /// conversion itself has zero runtime cost: AST and syntax nodes have exactly /// the same representation: a pointer to the tree root and a pointer to the /// node itself. pub trait AstNode { type Language: Language; fn can_cast(kind: ::Kind) -> bool where Self: Sized; fn cast(node: SyntaxNode) -> Option where Self: Sized; fn syntax(&self) -> &SyntaxNode; fn clone_for_update(&self) -> Self where Self: Sized, { Self::cast(self.syntax().clone_for_update()).unwrap() } fn clone_subtree(&self) -> Self where Self: Sized, { Self::cast(self.syntax().clone_subtree()).unwrap() } } /// A "pointer" to a [`SyntaxNode`], via location in the source code. /// /// ## Note /// Since the location is source code dependent, this must not be used /// with mutable syntax trees. Any changes made in such trees causes /// the pointed node's source location to change, invalidating the pointer. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct SyntaxNodePtr { kind: L::Kind, range: TextRange, } impl SyntaxNodePtr { /// Returns a [`SyntaxNodePtr`] for the node. /// /// Panics if the provided node is mutable pub fn new(node: &SyntaxNode) -> Self { assert!(!node.is_mutable(), "tree is mutable"); Self { kind: node.kind(), range: node.text_range() } } /// Like [`Self::try_to_node`] but panics instead of returning `None` on /// failure. pub fn to_node(&self, root: &SyntaxNode) -> SyntaxNode { self.try_to_node(root).unwrap_or_else(|| panic!("can't resolve {self:?} with {root:?}")) } /// "Dereferences" the pointer to get the [`SyntaxNode`] it points to. /// /// Returns `None` if the node is not found, so make sure that the `root` /// syntax tree is equivalent to (i.e. is build from the same text from) the /// tree which was originally used to get this [`SyntaxNodePtr`]. /// /// Also returns `None` if `root` is not actually a root (i.e. it has a /// parent). /// /// NOTE: If this function is called on a mutable tree, it will panic /// /// The complexity is linear in the depth of the tree and logarithmic in /// tree width. As most trees are shallow, thinking about this as /// `O(log(N))` in the size of the tree is not too wrong! pub fn try_to_node(&self, root: &SyntaxNode) -> Option> { assert!(!root.is_mutable(), "tree is mutable"); if root.parent().is_some() { return None; } successors(Some(root.clone()), |node| node.child_or_token_at_range(self.range)?.into_node()) .find(|it| it.text_range() == self.range && it.kind() == self.kind) } /// Casts this to an [`AstPtr`] to the given node type if possible. pub fn cast>(self) -> Option> { if !N::can_cast(self.kind) { return None; } Some(AstPtr { raw: self }) } /// Returns the kind of the syntax node this points to. pub fn kind(&self) -> L::Kind { self.kind } /// Returns the range of the syntax node this points to. pub fn text_range(&self) -> TextRange { self.range } } /// Like [`SyntaxNodePtr`], but remembers the type of node. /// /// ## Note /// As with [`SyntaxNodePtr`], this must not be used on mutable /// syntax trees, since any mutation can cause the pointed node's /// source location to change, invalidating the pointer pub struct AstPtr { raw: SyntaxNodePtr, } impl AstPtr { /// Returns an [`AstPtr`] for the node. /// /// Panics if the provided node is mutable pub fn new(node: &N) -> Self { // The above mentioned panic is handled by SyntaxNodePtr Self { raw: SyntaxNodePtr::new(node.syntax()) } } /// Like `Self::try_to_node` but panics on failure. pub fn to_node(&self, root: &SyntaxNode) -> N { self.try_to_node(root).unwrap_or_else(|| panic!("can't resolve {self:?} with {root:?}")) } /// Given the root node containing the node `n` that `self` is a pointer to, /// returns `n` if possible. Panics if `root` is mutable. See [`SyntaxNodePtr::try_to_node`]. pub fn try_to_node(&self, root: &SyntaxNode) -> Option { // The above mentioned panic is handled by SyntaxNodePtr N::cast(self.raw.try_to_node(root)?) } /// Returns the underlying [`SyntaxNodePtr`]. pub fn syntax_node_ptr(&self) -> SyntaxNodePtr { self.raw.clone() } /// Casts this to an [`AstPtr`] to the given node type if possible. pub fn cast>(self) -> Option> { if !U::can_cast(self.raw.kind) { return None; } Some(AstPtr { raw: self.raw }) } } impl fmt::Debug for AstPtr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("AstPtr").field("raw", &self.raw).finish() } } impl Clone for AstPtr { fn clone(&self) -> Self { Self { raw: self.raw.clone() } } } impl PartialEq for AstPtr { fn eq(&self, other: &AstPtr) -> bool { self.raw == other.raw } } impl Eq for AstPtr {} impl Hash for AstPtr { fn hash(&self, state: &mut H) { self.raw.hash(state) } } impl From> for SyntaxNodePtr { fn from(ptr: AstPtr) -> SyntaxNodePtr { ptr.raw } } #[derive(Debug, Clone)] pub struct AstChildren { inner: SyntaxNodeChildren, ph: PhantomData, } impl AstChildren { fn new(parent: &SyntaxNode) -> Self { AstChildren { inner: parent.children(), ph: PhantomData } } } impl Iterator for AstChildren { type Item = N; fn next(&mut self) -> Option { self.inner.find_map(N::cast) } } pub mod support { use super::{AstChildren, AstNode}; use crate::{Language, SyntaxNode, SyntaxToken}; pub fn child(parent: &SyntaxNode) -> Option { parent.children().find_map(N::cast) } pub fn children(parent: &SyntaxNode) -> AstChildren { AstChildren::new(parent) } pub fn token(parent: &SyntaxNode, kind: L::Kind) -> Option> { parent.children_with_tokens().filter_map(|it| it.into_token()).find(|it| it.kind() == kind) } } #[cfg(test)] mod tests { use crate::{GreenNodeBuilder, Language, SyntaxKind, SyntaxNode}; use super::SyntaxNodePtr; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] struct TestLanguage; impl Language for TestLanguage { type Kind = SyntaxKind; fn kind_from_raw(raw: SyntaxKind) -> Self::Kind { raw } fn kind_to_raw(kind: Self::Kind) -> SyntaxKind { kind } } fn build_immut_tree() -> SyntaxNode { // Creates a single-node tree let mut builder = GreenNodeBuilder::new(); builder.start_node(SyntaxKind(0)); builder.finish_node(); SyntaxNode::::new_root(builder.finish()) } #[test] #[should_panic = "tree is mutable"] fn ensure_mut_panic_on_create() { // Make a mutable version let tree = build_immut_tree().clone_for_update(); SyntaxNodePtr::new(&tree); } #[test] #[should_panic = "tree is mutable"] fn ensure_mut_panic_on_deref() { let tree = build_immut_tree(); let tree_mut = tree.clone_for_update(); // Create on immutable, convert on mutable let syn_ptr = SyntaxNodePtr::new(&tree); syn_ptr.to_node(&tree_mut); } }