syntax/ast/
traits.rs

1//! Various traits that are implemented by ast nodes.
2//!
3//! The implementations are usually trivial, and live in generated.rs
4use either::Either;
5
6use crate::{
7    SyntaxElement, SyntaxToken, T,
8    ast::{self, AstChildren, AstNode, AstToken, support},
9    syntax_node::SyntaxElementChildren,
10};
11
12pub trait HasName: AstNode {
13    fn name(&self) -> Option<ast::Name> {
14        support::child(self.syntax())
15    }
16}
17
18pub trait HasVisibility: AstNode {
19    fn visibility(&self) -> Option<ast::Visibility> {
20        support::child(self.syntax())
21    }
22}
23
24pub trait HasLoopBody: AstNode {
25    fn loop_body(&self) -> Option<ast::BlockExpr> {
26        support::child(self.syntax())
27    }
28
29    fn label(&self) -> Option<ast::Label> {
30        support::child(self.syntax())
31    }
32}
33
34pub trait HasArgList: AstNode {
35    fn arg_list(&self) -> Option<ast::ArgList> {
36        support::child(self.syntax())
37    }
38}
39
40pub trait HasModuleItem: AstNode {
41    fn items(&self) -> AstChildren<ast::Item> {
42        support::children(self.syntax())
43    }
44}
45
46pub trait HasGenericParams: AstNode {
47    fn generic_param_list(&self) -> Option<ast::GenericParamList> {
48        support::child(self.syntax())
49    }
50
51    fn where_clause(&self) -> Option<ast::WhereClause> {
52        support::child(self.syntax())
53    }
54}
55pub trait HasGenericArgs: AstNode {
56    fn generic_arg_list(&self) -> Option<ast::GenericArgList> {
57        support::child(self.syntax())
58    }
59}
60
61pub trait HasTypeBounds: AstNode {
62    fn type_bound_list(&self) -> Option<ast::TypeBoundList> {
63        support::child(self.syntax())
64    }
65
66    fn colon_token(&self) -> Option<SyntaxToken> {
67        support::token(self.syntax(), T![:])
68    }
69}
70
71pub trait HasAttrs: AstNode {
72    fn attrs(&self) -> AstChildren<ast::Attr> {
73        support::children(self.syntax())
74    }
75    fn has_atom_attr(&self, atom: &str) -> bool {
76        self.attrs().filter_map(|x| x.as_simple_atom()).any(|x| x == atom)
77    }
78
79    /// Returns all attributes of this node, including inner attributes that may not be directly under this node
80    /// but under a child.
81    fn attrs_including_inner(self) -> impl Iterator<Item = ast::Attr>
82    where
83        Self: Sized,
84    {
85        let inner_attrs_node = if let Some(it) =
86            support::child::<ast::BlockExpr>(self.syntax()).and_then(|it| it.stmt_list())
87        {
88            Some(it.syntax)
89        } else if let Some(it) = support::child::<ast::MatchArmList>(self.syntax()) {
90            Some(it.syntax)
91        } else if let Some(it) = support::child::<ast::AssocItemList>(self.syntax()) {
92            Some(it.syntax)
93        } else if let Some(it) = support::child::<ast::ItemList>(self.syntax()) {
94            Some(it.syntax)
95        } else if let Some(it) = support::child::<ast::ExternItemList>(self.syntax()) {
96            Some(it.syntax)
97        } else if let Some(it) = support::child::<ast::MacroItems>(self.syntax()) {
98            Some(it.syntax)
99        } else {
100            None
101        };
102
103        self.attrs().chain(inner_attrs_node.into_iter().flat_map(|it| support::children(&it)))
104    }
105}
106
107pub trait HasDocComments: HasAttrs {
108    fn doc_comments(&self) -> DocCommentIter {
109        DocCommentIter { iter: self.syntax().children_with_tokens() }
110    }
111}
112
113impl DocCommentIter {
114    pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> DocCommentIter {
115        DocCommentIter { iter: syntax_node.children_with_tokens() }
116    }
117
118    #[cfg(test)]
119    pub fn doc_comment_text(self) -> Option<String> {
120        let docs = itertools::Itertools::join(
121            &mut self.filter_map(|comment| comment.doc_comment().map(ToOwned::to_owned)),
122            "\n",
123        );
124        if docs.is_empty() { None } else { Some(docs) }
125    }
126}
127
128pub struct DocCommentIter {
129    iter: SyntaxElementChildren,
130}
131
132impl Iterator for DocCommentIter {
133    type Item = ast::Comment;
134    fn next(&mut self) -> Option<ast::Comment> {
135        self.iter.by_ref().find_map(|el| {
136            el.into_token().and_then(ast::Comment::cast).filter(ast::Comment::is_doc)
137        })
138    }
139}
140
141pub struct AttrDocCommentIter {
142    iter: SyntaxElementChildren,
143}
144
145impl AttrDocCommentIter {
146    pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> AttrDocCommentIter {
147        AttrDocCommentIter { iter: syntax_node.children_with_tokens() }
148    }
149}
150
151impl Iterator for AttrDocCommentIter {
152    type Item = Either<ast::Attr, ast::Comment>;
153    fn next(&mut self) -> Option<Self::Item> {
154        self.iter.by_ref().find_map(|el| match el {
155            SyntaxElement::Node(node) => ast::Attr::cast(node).map(Either::Left),
156            SyntaxElement::Token(tok) => {
157                ast::Comment::cast(tok).filter(ast::Comment::is_doc).map(Either::Right)
158            }
159        })
160    }
161}
162
163impl<A: HasName, B: HasName> HasName for Either<A, B> {}