ra_ap_syntax/ast/
traits.rs1use 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 fn inner_attributes_node(&self) -> Option<SyntaxNode> {
80 let syntax = self.syntax();
81 Some(match_ast! {
82 match syntax {
83 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
103pub 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> {}