//! Abstract Syntax Tree, layered on top of untyped `SyntaxNode`s mod generated; mod traits; mod tokens; mod extensions; mod expr_extensions; pub mod edit; pub mod make; use std::marker::PhantomData; use crate::{ syntax_node::{ NodeOrToken, SyntaxElement, SyntaxElementChildren, SyntaxNode, SyntaxNodeChildren, SyntaxToken, }, SmolStr, SyntaxKind, }; pub use self::{ expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp}, extensions::{ AttrKind, FieldKind, PathSegmentKind, SelfParamKind, SlicePatComponents, StructKind, TypeBoundKind, VisibilityKind, }, generated::*, tokens::*, traits::*, }; /// 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: AstElement { fn can_cast(kind: SyntaxKind) -> bool where Self: Sized; fn cast_or_return(syntax: SyntaxNode) -> Result where Self: Sized; fn cast(syntax: SyntaxNode) -> Option where Self: Sized, { ::cast_or_return(syntax).ok() } fn syntax(&self) -> &SyntaxNode; fn into_syntax(self) -> SyntaxNode; } #[test] fn assert_ast_is_object_safe() { fn _f(_: &dyn AstNode, _: &dyn NameOwner) {} } /// Like `AstNode`, but wraps tokens rather than interior nodes. pub trait AstToken: AstElement { fn can_cast(token: SyntaxKind) -> bool where Self: Sized; fn cast_or_return(syntax: SyntaxToken) -> Result where Self: Sized; fn cast(syntax: SyntaxToken) -> Option where Self: Sized, { ::cast_or_return(syntax).ok() } fn syntax(&self) -> &SyntaxToken; fn into_syntax(self) -> SyntaxToken; fn text(&self) -> &SmolStr { self.syntax().text() } } /// Like `AstNode`, but wraps either nodes or tokens rather than interior nodes. pub trait AstElement: std::fmt::Display { fn can_cast_element(kind: SyntaxKind) -> bool where Self: Sized; fn cast_or_return_element(syntax: SyntaxElement) -> Result where Self: Sized; fn cast_element(syntax: SyntaxElement) -> Option where Self: Sized, { ::cast_or_return_element(syntax).ok() } fn syntax_element(&self) -> NodeOrToken<&SyntaxNode, &SyntaxToken>; fn into_syntax_element(self) -> SyntaxElement; } /// An iterator over `SyntaxNode` children of a particular AST type. #[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.by_ref().find_map(N::cast) } } fn child_opt(parent: &P) -> Option { children(parent).next() } fn children(parent: &P) -> AstChildren { AstChildren::new(parent.syntax()) } /// An iterator over `SyntaxToken` children of a particular AST type. #[derive(Debug, Clone)] pub struct AstChildTokens { inner: SyntaxElementChildren, ph: PhantomData, } impl AstChildTokens { fn new(parent: &SyntaxNode) -> Self { AstChildTokens { inner: parent.children_with_tokens(), ph: PhantomData } } } impl Iterator for AstChildTokens { type Item = N; fn next(&mut self) -> Option { self.inner.by_ref().filter_map(|x| x.into_token()).find_map(N::cast) } } fn child_token_opt(parent: &P) -> Option { child_tokens(parent).next() } fn child_tokens(parent: &P) -> AstChildTokens { AstChildTokens::new(parent.syntax()) } /// An iterator over `SyntaxNode` children of a particular AST type. #[derive(Debug, Clone)] pub struct AstChildElements { inner: SyntaxElementChildren, ph: PhantomData, } impl AstChildElements { fn new(parent: &SyntaxNode) -> Self { AstChildElements { inner: parent.children_with_tokens(), ph: PhantomData } } } impl Iterator for AstChildElements { type Item = N; fn next(&mut self) -> Option { self.inner.by_ref().find_map(N::cast_element) } } #[allow(dead_code)] fn child_element_opt(parent: &P) -> Option { child_elements(parent).next() } #[allow(dead_code)] fn child_elements(parent: &P) -> AstChildElements { AstChildElements::new(parent.syntax()) } #[test] fn test_doc_comment_none() { let file = SourceFile::parse( r#" // non-doc mod foo {} "#, ) .ok() .unwrap(); let module = file.syntax().descendants().find_map(Module::cast).unwrap(); assert!(module.doc_comment_text().is_none()); } #[test] fn test_doc_comment_of_items() { let file = SourceFile::parse( r#" //! doc // non-doc mod foo {} "#, ) .ok() .unwrap(); let module = file.syntax().descendants().find_map(Module::cast).unwrap(); assert_eq!("doc", module.doc_comment_text().unwrap()); } #[test] fn test_doc_comment_of_statics() { let file = SourceFile::parse( r#" /// Number of levels static LEVELS: i32 = 0; "#, ) .ok() .unwrap(); let st = file.syntax().descendants().find_map(StaticDef::cast).unwrap(); assert_eq!("Number of levels", st.doc_comment_text().unwrap()); } #[test] fn test_doc_comment_preserves_indents() { let file = SourceFile::parse( r#" /// doc1 /// ``` /// fn foo() { /// // ... /// } /// ``` mod foo {} "#, ) .ok() .unwrap(); let module = file.syntax().descendants().find_map(Module::cast).unwrap(); assert_eq!("doc1\n```\nfn foo() {\n // ...\n}\n```", module.doc_comment_text().unwrap()); } #[test] fn test_doc_comment_preserves_newlines() { let file = SourceFile::parse( r#" /// this /// is /// mod /// foo mod foo {} "#, ) .ok() .unwrap(); let module = file.syntax().descendants().find_map(Module::cast).unwrap(); assert_eq!("this\nis\nmod\nfoo", module.doc_comment_text().unwrap()); } #[test] fn test_doc_comment_single_line_block_strips_suffix() { let file = SourceFile::parse( r#" /** this is mod foo*/ mod foo {} "#, ) .ok() .unwrap(); let module = file.syntax().descendants().find_map(Module::cast).unwrap(); assert_eq!("this is mod foo", module.doc_comment_text().unwrap()); } #[test] fn test_doc_comment_single_line_block_strips_suffix_whitespace() { let file = SourceFile::parse( r#" /** this is mod foo */ mod foo {} "#, ) .ok() .unwrap(); let module = file.syntax().descendants().find_map(Module::cast).unwrap(); assert_eq!("this is mod foo ", module.doc_comment_text().unwrap()); } #[test] fn test_doc_comment_multi_line_block_strips_suffix() { let file = SourceFile::parse( r#" /** this is mod foo */ mod foo {} "#, ) .ok() .unwrap(); let module = file.syntax().descendants().find_map(Module::cast).unwrap(); assert_eq!( " this\n is\n mod foo\n ", module.doc_comment_text().unwrap() ); } #[test] fn test_comments_preserve_trailing_whitespace() { let file = SourceFile::parse( "\n/// Representation of a Realm. \n/// In the specification these are called Realm Records.\nstruct Realm {}", ) .ok() .unwrap(); let def = file.syntax().descendants().find_map(StructDef::cast).unwrap(); assert_eq!( "Representation of a Realm. \nIn the specification these are called Realm Records.", def.doc_comment_text().unwrap() ); } #[test] fn test_where_predicates() { fn assert_bound(text: &str, bound: Option) { assert_eq!(text, bound.unwrap().syntax().text().to_string()); } let file = SourceFile::parse( r#" fn foo() where T: Clone + Copy + Debug + 'static, 'a: 'b + 'c, Iterator::Item: 'a + Debug, Iterator::Item: Debug + 'a, ::Item: Debug + 'a, for<'a> F: Fn(&'a str) {} "#, ) .ok() .unwrap(); let where_clause = file.syntax().descendants().find_map(WhereClause::cast).unwrap(); let mut predicates = where_clause.predicates(); let pred = predicates.next().unwrap(); let mut bounds = pred.type_bound_list().unwrap().bounds(); assert_eq!("T", pred.type_ref().unwrap().syntax().text().to_string()); assert_bound("Clone", bounds.next()); assert_bound("Copy", bounds.next()); assert_bound("Debug", bounds.next()); assert_bound("'static", bounds.next()); let pred = predicates.next().unwrap(); let mut bounds = pred.type_bound_list().unwrap().bounds(); assert_eq!("'a", pred.lifetime_token().unwrap().text()); assert_bound("'b", bounds.next()); assert_bound("'c", bounds.next()); let pred = predicates.next().unwrap(); let mut bounds = pred.type_bound_list().unwrap().bounds(); assert_eq!("Iterator::Item", pred.type_ref().unwrap().syntax().text().to_string()); assert_bound("'a", bounds.next()); let pred = predicates.next().unwrap(); let mut bounds = pred.type_bound_list().unwrap().bounds(); assert_eq!("Iterator::Item", pred.type_ref().unwrap().syntax().text().to_string()); assert_bound("Debug", bounds.next()); assert_bound("'a", bounds.next()); let pred = predicates.next().unwrap(); let mut bounds = pred.type_bound_list().unwrap().bounds(); assert_eq!("::Item", pred.type_ref().unwrap().syntax().text().to_string()); assert_bound("Debug", bounds.next()); assert_bound("'a", bounds.next()); let pred = predicates.next().unwrap(); let mut bounds = pred.type_bound_list().unwrap().bounds(); assert_eq!("for<'a> F", pred.type_ref().unwrap().syntax().text().to_string()); assert_bound("Fn(&'a str)", bounds.next()); }