Skip to main content

ra_ap_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, SyntaxNode, SyntaxToken, T,
8    ast::{self, AstChildren, AstNode, AstToken, support},
9    match_ast,
10    syntax_node::SyntaxElementChildren,
11};
12
13pub trait HasName: AstNode {
14    fn name(&self) -> Option<ast::Name> {
15        support::child(self.syntax())
16    }
17}
18
19pub trait HasVisibility: AstNode {
20    fn visibility(&self) -> Option<ast::Visibility> {
21        support::child(self.syntax())
22    }
23}
24
25pub trait HasLoopBody: AstNode {
26    fn loop_body(&self) -> Option<ast::BlockExpr> {
27        support::child(self.syntax())
28    }
29
30    fn label(&self) -> Option<ast::Label> {
31        support::child(self.syntax())
32    }
33}
34
35pub trait HasArgList: AstNode {
36    fn arg_list(&self) -> Option<ast::ArgList> {
37        support::child(self.syntax())
38    }
39}
40
41pub trait HasModuleItem: AstNode {
42    fn items(&self) -> AstChildren<ast::Item> {
43        support::children(self.syntax())
44    }
45}
46
47pub trait HasGenericParams: AstNode {
48    fn generic_param_list(&self) -> Option<ast::GenericParamList> {
49        support::child(self.syntax())
50    }
51
52    fn where_clause(&self) -> Option<ast::WhereClause> {
53        support::child(self.syntax())
54    }
55}
56pub trait HasGenericArgs: AstNode {
57    fn generic_arg_list(&self) -> Option<ast::GenericArgList> {
58        support::child(self.syntax())
59    }
60}
61
62pub trait HasTypeBounds: AstNode {
63    fn type_bound_list(&self) -> Option<ast::TypeBoundList> {
64        support::child(self.syntax())
65    }
66
67    fn colon_token(&self) -> Option<SyntaxToken> {
68        support::token(self.syntax(), T![:])
69    }
70}
71
72pub trait HasAttrs: AstNode {
73    fn attrs(&self) -> AstChildren<ast::Attr> {
74        support::children(self.syntax())
75    }
76
77    /// This may return the same node as called with (with `SourceFile`). The caller has the responsibility
78    /// to avoid duplicate attributes.
79    fn inner_attributes_node(&self) -> Option<SyntaxNode> {
80        let syntax = self.syntax();
81        Some(match_ast! {
82            match syntax {
83                // A `SourceFile` contains the inner attributes of itself.
84                ast::SourceFile(_) => syntax.clone(),
85                ast::ExternBlock(it) => it.extern_item_list()?.syntax().clone(),
86                ast::Fn(it) => it.body()?.stmt_list()?.syntax().clone(),
87                ast::MatchExpr(it) => it.match_arm_list()?.syntax().clone(),
88                ast::Impl(it) => it.assoc_item_list()?.syntax().clone(),
89                ast::Trait(it) => it.assoc_item_list()?.syntax().clone(),
90                ast::Module(it) => it.item_list()?.syntax().clone(),
91                ast::BlockExpr(it) => {
92                    if !it.may_carry_attributes() {
93                        return None;
94                    }
95                    syntax.clone()
96                },
97                _ => return None,
98            }
99        })
100    }
101}
102
103/// Returns all attributes of this node, including inner attributes that may not be directly under this node
104/// but under a child.
105pub fn attrs_including_inner(owner: &dyn HasAttrs) -> impl Iterator<Item = ast::Attr> + Clone {
106    owner.attrs().filter(|attr| attr.kind().is_outer()).chain(
107        owner
108            .inner_attributes_node()
109            .into_iter()
110            .flat_map(|node| support::children::<ast::Attr>(&node))
111            .filter(|attr| attr.kind().is_inner()),
112    )
113}
114
115pub trait HasDocComments: HasAttrs {
116    fn doc_comments(&self) -> DocCommentIter {
117        DocCommentIter { iter: self.syntax().children_with_tokens() }
118    }
119}
120
121impl DocCommentIter {
122    pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> DocCommentIter {
123        DocCommentIter { iter: syntax_node.children_with_tokens() }
124    }
125
126    #[cfg(test)]
127    pub fn doc_comment_text(self) -> Option<String> {
128        let docs = itertools::Itertools::join(
129            &mut self.filter_map(|comment| comment.doc_comment().map(|it| it.0.to_owned())),
130            "\n",
131        );
132        if docs.is_empty() { None } else { Some(docs) }
133    }
134}
135
136pub struct DocCommentIter {
137    iter: SyntaxElementChildren,
138}
139
140impl Iterator for DocCommentIter {
141    type Item = ast::Comment;
142    fn next(&mut self) -> Option<ast::Comment> {
143        self.iter.by_ref().find_map(|el| {
144            el.into_token().and_then(ast::Comment::cast).filter(ast::Comment::is_doc)
145        })
146    }
147}
148
149pub struct AttrDocCommentIter {
150    iter: SyntaxElementChildren,
151}
152
153impl AttrDocCommentIter {
154    pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> AttrDocCommentIter {
155        AttrDocCommentIter { iter: syntax_node.children_with_tokens() }
156    }
157}
158
159impl Iterator for AttrDocCommentIter {
160    type Item = Either<ast::Attr, ast::Comment>;
161    fn next(&mut self) -> Option<Self::Item> {
162        self.iter.find_map(|el| match el {
163            SyntaxElement::Node(node) => ast::Attr::cast(node).map(Either::Left),
164            SyntaxElement::Token(tok) => {
165                ast::Comment::cast(tok).filter(ast::Comment::is_doc).map(Either::Right)
166            }
167        })
168    }
169}
170
171impl<A: HasName, B: HasName> HasName for Either<A, B> {}