aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src/ast/extensions.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_syntax/src/ast/extensions.rs')
-rw-r--r--crates/ra_syntax/src/ast/extensions.rs300
1 files changed, 300 insertions, 0 deletions
diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs
new file mode 100644
index 000000000..87592bfd8
--- /dev/null
+++ b/crates/ra_syntax/src/ast/extensions.rs
@@ -0,0 +1,300 @@
1use itertools::Itertools;
2
3use crate::{
4 SmolStr, SyntaxToken,
5 ast::{self, AstNode, children, child_opt},
6 SyntaxKind::*,
7};
8
9impl ast::Name {
10 pub fn text(&self) -> &SmolStr {
11 let ident = self.syntax().first_child_or_token().unwrap().as_token().unwrap();
12 ident.text()
13 }
14}
15
16impl ast::NameRef {
17 pub fn text(&self) -> &SmolStr {
18 let ident = self.syntax().first_child_or_token().unwrap().as_token().unwrap();
19 ident.text()
20 }
21}
22
23impl ast::Attr {
24 pub fn is_inner(&self) -> bool {
25 let tt = match self.value() {
26 None => return false,
27 Some(tt) => tt,
28 };
29
30 let prev = match tt.syntax().prev_sibling() {
31 None => return false,
32 Some(prev) => prev,
33 };
34
35 prev.kind() == EXCL
36 }
37
38 pub fn as_atom(&self) -> Option<SmolStr> {
39 let tt = self.value()?;
40 let (_bra, attr, _ket) = tt.syntax().children_with_tokens().collect_tuple()?;
41 if attr.kind() == IDENT {
42 Some(attr.as_token()?.text().clone())
43 } else {
44 None
45 }
46 }
47
48 pub fn as_call(&self) -> Option<(SmolStr, &ast::TokenTree)> {
49 let tt = self.value()?;
50 let (_bra, attr, args, _ket) = tt.syntax().children_with_tokens().collect_tuple()?;
51 let args = ast::TokenTree::cast(args.as_node()?)?;
52 if attr.kind() == IDENT {
53 Some((attr.as_token()?.text().clone(), args))
54 } else {
55 None
56 }
57 }
58
59 pub fn as_named(&self) -> Option<SmolStr> {
60 let tt = self.value()?;
61 let attr = tt.syntax().children_with_tokens().nth(1)?;
62 if attr.kind() == IDENT {
63 Some(attr.as_token()?.text().clone())
64 } else {
65 None
66 }
67 }
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum PathSegmentKind<'a> {
72 Name(&'a ast::NameRef),
73 SelfKw,
74 SuperKw,
75 CrateKw,
76}
77
78impl ast::PathSegment {
79 pub fn parent_path(&self) -> &ast::Path {
80 self.syntax()
81 .parent()
82 .and_then(ast::Path::cast)
83 .expect("segments are always nested in paths")
84 }
85
86 pub fn kind(&self) -> Option<PathSegmentKind> {
87 let res = if let Some(name_ref) = self.name_ref() {
88 PathSegmentKind::Name(name_ref)
89 } else {
90 match self.syntax().first_child_or_token()?.kind() {
91 SELF_KW => PathSegmentKind::SelfKw,
92 SUPER_KW => PathSegmentKind::SuperKw,
93 CRATE_KW => PathSegmentKind::CrateKw,
94 _ => return None,
95 }
96 };
97 Some(res)
98 }
99
100 pub fn has_colon_colon(&self) -> bool {
101 match self.syntax.first_child_or_token().map(|s| s.kind()) {
102 Some(COLONCOLON) => true,
103 _ => false,
104 }
105 }
106}
107
108impl ast::Path {
109 pub fn parent_path(&self) -> Option<&ast::Path> {
110 self.syntax().parent().and_then(ast::Path::cast)
111 }
112}
113
114impl ast::Module {
115 pub fn has_semi(&self) -> bool {
116 match self.syntax().last_child_or_token() {
117 None => false,
118 Some(node) => node.kind() == SEMI,
119 }
120 }
121}
122
123impl ast::UseTree {
124 pub fn has_star(&self) -> bool {
125 self.syntax().children_with_tokens().any(|it| it.kind() == STAR)
126 }
127}
128
129impl ast::UseTreeList {
130 pub fn parent_use_tree(&self) -> &ast::UseTree {
131 self.syntax()
132 .parent()
133 .and_then(ast::UseTree::cast)
134 .expect("UseTreeLists are always nested in UseTrees")
135 }
136}
137
138impl ast::ImplBlock {
139 pub fn target_type(&self) -> Option<&ast::TypeRef> {
140 match self.target() {
141 (Some(t), None) | (_, Some(t)) => Some(t),
142 _ => None,
143 }
144 }
145
146 pub fn target_trait(&self) -> Option<&ast::TypeRef> {
147 match self.target() {
148 (Some(t), Some(_)) => Some(t),
149 _ => None,
150 }
151 }
152
153 fn target(&self) -> (Option<&ast::TypeRef>, Option<&ast::TypeRef>) {
154 let mut types = children(self);
155 let first = types.next();
156 let second = types.next();
157 (first, second)
158 }
159}
160
161#[derive(Debug, Clone, PartialEq, Eq)]
162pub enum StructFlavor<'a> {
163 Tuple(&'a ast::PosFieldDefList),
164 Named(&'a ast::NamedFieldDefList),
165 Unit,
166}
167
168impl StructFlavor<'_> {
169 fn from_node<N: AstNode>(node: &N) -> StructFlavor {
170 if let Some(nfdl) = child_opt::<_, ast::NamedFieldDefList>(node) {
171 StructFlavor::Named(nfdl)
172 } else if let Some(pfl) = child_opt::<_, ast::PosFieldDefList>(node) {
173 StructFlavor::Tuple(pfl)
174 } else {
175 StructFlavor::Unit
176 }
177 }
178}
179
180impl ast::StructDef {
181 pub fn flavor(&self) -> StructFlavor {
182 StructFlavor::from_node(self)
183 }
184}
185
186impl ast::EnumVariant {
187 pub fn parent_enum(&self) -> &ast::EnumDef {
188 self.syntax()
189 .parent()
190 .and_then(|it| it.parent())
191 .and_then(ast::EnumDef::cast)
192 .expect("EnumVariants are always nested in Enums")
193 }
194 pub fn flavor(&self) -> StructFlavor {
195 StructFlavor::from_node(self)
196 }
197}
198
199impl ast::LetStmt {
200 pub fn has_semi(&self) -> bool {
201 match self.syntax().last_child_or_token() {
202 None => false,
203 Some(node) => node.kind() == SEMI,
204 }
205 }
206}
207
208impl ast::ExprStmt {
209 pub fn has_semi(&self) -> bool {
210 match self.syntax().last_child_or_token() {
211 None => false,
212 Some(node) => node.kind() == SEMI,
213 }
214 }
215}
216
217impl ast::RefPat {
218 pub fn is_mut(&self) -> bool {
219 self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW)
220 }
221}
222
223impl ast::BindPat {
224 pub fn is_mutable(&self) -> bool {
225 self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW)
226 }
227
228 pub fn is_ref(&self) -> bool {
229 self.syntax().children_with_tokens().any(|n| n.kind() == REF_KW)
230 }
231}
232
233impl ast::PointerType {
234 pub fn is_mut(&self) -> bool {
235 self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW)
236 }
237}
238
239impl ast::ReferenceType {
240 pub fn is_mut(&self) -> bool {
241 self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW)
242 }
243}
244
245#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
246pub enum SelfParamFlavor {
247 /// self
248 Owned,
249 /// &self
250 Ref,
251 /// &mut self
252 MutRef,
253}
254
255impl ast::SelfParam {
256 pub fn self_kw_token(&self) -> SyntaxToken {
257 self.syntax()
258 .children_with_tokens()
259 .filter_map(|it| it.as_token())
260 .find(|it| it.kind() == SELF_KW)
261 .expect("invalid tree: self param must have self")
262 }
263
264 pub fn flavor(&self) -> SelfParamFlavor {
265 let borrowed = self.syntax().children_with_tokens().any(|n| n.kind() == AMP);
266 if borrowed {
267 // check for a `mut` coming after the & -- `mut &self` != `&mut self`
268 if self
269 .syntax()
270 .children_with_tokens()
271 .skip_while(|n| n.kind() != AMP)
272 .any(|n| n.kind() == MUT_KW)
273 {
274 SelfParamFlavor::MutRef
275 } else {
276 SelfParamFlavor::Ref
277 }
278 } else {
279 SelfParamFlavor::Owned
280 }
281 }
282}
283
284impl ast::LifetimeParam {
285 pub fn lifetime_token(&self) -> Option<SyntaxToken> {
286 self.syntax()
287 .children_with_tokens()
288 .filter_map(|it| it.as_token())
289 .find(|it| it.kind() == LIFETIME)
290 }
291}
292
293impl ast::WherePred {
294 pub fn lifetime_token(&self) -> Option<SyntaxToken> {
295 self.syntax()
296 .children_with_tokens()
297 .filter_map(|it| it.as_token())
298 .find(|it| it.kind() == LIFETIME)
299 }
300}