1use 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 fn has_atom_attr(&self, atom: &str) -> bool {
77 self.attrs().filter_map(|x| x.as_simple_atom()).any(|x| x == atom)
78 }
79
80 fn inner_attributes_node(&self) -> Option<SyntaxNode> {
83 let syntax = self.syntax();
84 Some(match_ast! {
85 match syntax {
86 ast::SourceFile(_) => syntax.clone(),
88 ast::ExternBlock(it) => it.extern_item_list()?.syntax().clone(),
89 ast::Fn(it) => it.body()?.stmt_list()?.syntax().clone(),
90 ast::MatchExpr(it) => it.match_arm_list()?.syntax().clone(),
91 ast::Impl(it) => it.assoc_item_list()?.syntax().clone(),
92 ast::Trait(it) => it.assoc_item_list()?.syntax().clone(),
93 ast::Module(it) => it.item_list()?.syntax().clone(),
94 ast::BlockExpr(it) => {
95 if !it.may_carry_attributes() {
96 return None;
97 }
98 syntax.clone()
99 },
100 _ => return None,
101 }
102 })
103 }
104}
105
106pub fn attrs_including_inner(owner: &dyn HasAttrs) -> impl Iterator<Item = ast::Attr> + Clone {
109 owner.attrs().filter(|attr| attr.kind().is_outer()).chain(
110 owner
111 .inner_attributes_node()
112 .into_iter()
113 .flat_map(|node| support::children::<ast::Attr>(&node))
114 .filter(|attr| attr.kind().is_inner()),
115 )
116}
117
118pub trait HasDocComments: HasAttrs {
119 fn doc_comments(&self) -> DocCommentIter {
120 DocCommentIter { iter: self.syntax().children_with_tokens() }
121 }
122}
123
124impl DocCommentIter {
125 pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> DocCommentIter {
126 DocCommentIter { iter: syntax_node.children_with_tokens() }
127 }
128
129 #[cfg(test)]
130 pub fn doc_comment_text(self) -> Option<String> {
131 let docs = itertools::Itertools::join(
132 &mut self.filter_map(|comment| comment.doc_comment().map(|it| it.0.to_owned())),
133 "\n",
134 );
135 if docs.is_empty() { None } else { Some(docs) }
136 }
137}
138
139pub struct DocCommentIter {
140 iter: SyntaxElementChildren,
141}
142
143impl Iterator for DocCommentIter {
144 type Item = ast::Comment;
145 fn next(&mut self) -> Option<ast::Comment> {
146 self.iter.by_ref().find_map(|el| {
147 el.into_token().and_then(ast::Comment::cast).filter(ast::Comment::is_doc)
148 })
149 }
150}
151
152pub struct AttrDocCommentIter {
153 iter: SyntaxElementChildren,
154}
155
156impl AttrDocCommentIter {
157 pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> AttrDocCommentIter {
158 AttrDocCommentIter { iter: syntax_node.children_with_tokens() }
159 }
160}
161
162impl Iterator for AttrDocCommentIter {
163 type Item = Either<ast::Attr, ast::Comment>;
164 fn next(&mut self) -> Option<Self::Item> {
165 self.iter.find_map(|el| match el {
166 SyntaxElement::Node(node) => ast::Attr::cast(node).map(Either::Left),
167 SyntaxElement::Token(tok) => {
168 ast::Comment::cast(tok).filter(ast::Comment::is_doc).map(Either::Right)
169 }
170 })
171 }
172}
173
174impl<A: HasName, B: HasName> HasName for Either<A, B> {}