diff options
author | Aleksey Kladov <[email protected]> | 2020-07-23 16:23:01 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2020-07-23 16:23:01 +0100 |
commit | e2030405d5936911d99ef9854d9626122bf03a02 (patch) | |
tree | b105d90e090ffd6588f7cffcecd68170ef01535d /crates/ra_syntax/src/ast/node_ext.rs | |
parent | 38e38d9b290ff90973c25a06962b81dbbb5d3d9e (diff) |
Rename modules
Diffstat (limited to 'crates/ra_syntax/src/ast/node_ext.rs')
-rw-r--r-- | crates/ra_syntax/src/ast/node_ext.rs | 474 |
1 files changed, 474 insertions, 0 deletions
diff --git a/crates/ra_syntax/src/ast/node_ext.rs b/crates/ra_syntax/src/ast/node_ext.rs new file mode 100644 index 000000000..662c6f73e --- /dev/null +++ b/crates/ra_syntax/src/ast/node_ext.rs | |||
@@ -0,0 +1,474 @@ | |||
1 | //! Various extension methods to ast Nodes, which are hard to code-generate. | ||
2 | //! Extensions for various expressions live in a sibling `expr_extensions` module. | ||
3 | |||
4 | use std::fmt; | ||
5 | |||
6 | use itertools::Itertools; | ||
7 | use ra_parser::SyntaxKind; | ||
8 | |||
9 | use crate::{ | ||
10 | ast::{self, support, AstNode, AttrInput, NameOwner, SyntaxNode}, | ||
11 | SmolStr, SyntaxElement, SyntaxToken, T, | ||
12 | }; | ||
13 | |||
14 | impl ast::Name { | ||
15 | pub fn text(&self) -> &SmolStr { | ||
16 | text_of_first_token(self.syntax()) | ||
17 | } | ||
18 | } | ||
19 | |||
20 | impl ast::NameRef { | ||
21 | pub fn text(&self) -> &SmolStr { | ||
22 | text_of_first_token(self.syntax()) | ||
23 | } | ||
24 | |||
25 | pub fn as_tuple_field(&self) -> Option<usize> { | ||
26 | self.text().parse().ok() | ||
27 | } | ||
28 | } | ||
29 | |||
30 | fn text_of_first_token(node: &SyntaxNode) -> &SmolStr { | ||
31 | node.green().children().next().and_then(|it| it.into_token()).unwrap().text() | ||
32 | } | ||
33 | |||
34 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
35 | pub enum AttrKind { | ||
36 | Inner, | ||
37 | Outer, | ||
38 | } | ||
39 | |||
40 | impl ast::Attr { | ||
41 | pub fn as_simple_atom(&self) -> Option<SmolStr> { | ||
42 | match self.input() { | ||
43 | None => self.simple_name(), | ||
44 | Some(_) => None, | ||
45 | } | ||
46 | } | ||
47 | |||
48 | pub fn as_simple_call(&self) -> Option<(SmolStr, ast::TokenTree)> { | ||
49 | match self.input() { | ||
50 | Some(AttrInput::TokenTree(tt)) => Some((self.simple_name()?, tt)), | ||
51 | _ => None, | ||
52 | } | ||
53 | } | ||
54 | |||
55 | pub fn as_simple_key_value(&self) -> Option<(SmolStr, SmolStr)> { | ||
56 | match self.input() { | ||
57 | Some(AttrInput::Literal(lit)) => { | ||
58 | let key = self.simple_name()?; | ||
59 | // FIXME: escape? raw string? | ||
60 | let value = lit.syntax().first_token()?.text().trim_matches('"').into(); | ||
61 | Some((key, value)) | ||
62 | } | ||
63 | _ => None, | ||
64 | } | ||
65 | } | ||
66 | |||
67 | pub fn simple_name(&self) -> Option<SmolStr> { | ||
68 | let path = self.path()?; | ||
69 | match (path.segment(), path.qualifier()) { | ||
70 | (Some(segment), None) => Some(segment.syntax().first_token()?.text().clone()), | ||
71 | _ => None, | ||
72 | } | ||
73 | } | ||
74 | |||
75 | pub fn kind(&self) -> AttrKind { | ||
76 | let first_token = self.syntax().first_token(); | ||
77 | let first_token_kind = first_token.as_ref().map(SyntaxToken::kind); | ||
78 | let second_token_kind = | ||
79 | first_token.and_then(|token| token.next_token()).as_ref().map(SyntaxToken::kind); | ||
80 | |||
81 | match (first_token_kind, second_token_kind) { | ||
82 | (Some(SyntaxKind::POUND), Some(T![!])) => AttrKind::Inner, | ||
83 | _ => AttrKind::Outer, | ||
84 | } | ||
85 | } | ||
86 | } | ||
87 | |||
88 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
89 | pub enum PathSegmentKind { | ||
90 | Name(ast::NameRef), | ||
91 | Type { type_ref: Option<ast::TypeRef>, trait_ref: Option<ast::PathType> }, | ||
92 | SelfKw, | ||
93 | SuperKw, | ||
94 | CrateKw, | ||
95 | } | ||
96 | |||
97 | impl ast::PathSegment { | ||
98 | pub fn parent_path(&self) -> ast::Path { | ||
99 | self.syntax() | ||
100 | .parent() | ||
101 | .and_then(ast::Path::cast) | ||
102 | .expect("segments are always nested in paths") | ||
103 | } | ||
104 | |||
105 | pub fn kind(&self) -> Option<PathSegmentKind> { | ||
106 | let res = if let Some(name_ref) = self.name_ref() { | ||
107 | PathSegmentKind::Name(name_ref) | ||
108 | } else { | ||
109 | match self.syntax().first_child_or_token()?.kind() { | ||
110 | T![self] => PathSegmentKind::SelfKw, | ||
111 | T![super] => PathSegmentKind::SuperKw, | ||
112 | T![crate] => PathSegmentKind::CrateKw, | ||
113 | T![<] => { | ||
114 | // <T> or <T as Trait> | ||
115 | // T is any TypeRef, Trait has to be a PathType | ||
116 | let mut type_refs = | ||
117 | self.syntax().children().filter(|node| ast::TypeRef::can_cast(node.kind())); | ||
118 | let type_ref = type_refs.next().and_then(ast::TypeRef::cast); | ||
119 | let trait_ref = type_refs.next().and_then(ast::PathType::cast); | ||
120 | PathSegmentKind::Type { type_ref, trait_ref } | ||
121 | } | ||
122 | _ => return None, | ||
123 | } | ||
124 | }; | ||
125 | Some(res) | ||
126 | } | ||
127 | } | ||
128 | |||
129 | impl ast::Path { | ||
130 | pub fn parent_path(&self) -> Option<ast::Path> { | ||
131 | self.syntax().parent().and_then(ast::Path::cast) | ||
132 | } | ||
133 | } | ||
134 | |||
135 | impl ast::UseTreeList { | ||
136 | pub fn parent_use_tree(&self) -> ast::UseTree { | ||
137 | self.syntax() | ||
138 | .parent() | ||
139 | .and_then(ast::UseTree::cast) | ||
140 | .expect("UseTreeLists are always nested in UseTrees") | ||
141 | } | ||
142 | } | ||
143 | |||
144 | impl ast::ImplDef { | ||
145 | pub fn target_type(&self) -> Option<ast::TypeRef> { | ||
146 | match self.target() { | ||
147 | (Some(t), None) | (_, Some(t)) => Some(t), | ||
148 | _ => None, | ||
149 | } | ||
150 | } | ||
151 | |||
152 | pub fn target_trait(&self) -> Option<ast::TypeRef> { | ||
153 | match self.target() { | ||
154 | (Some(t), Some(_)) => Some(t), | ||
155 | _ => None, | ||
156 | } | ||
157 | } | ||
158 | |||
159 | fn target(&self) -> (Option<ast::TypeRef>, Option<ast::TypeRef>) { | ||
160 | let mut types = support::children(self.syntax()); | ||
161 | let first = types.next(); | ||
162 | let second = types.next(); | ||
163 | (first, second) | ||
164 | } | ||
165 | } | ||
166 | |||
167 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
168 | pub enum StructKind { | ||
169 | Record(ast::RecordFieldDefList), | ||
170 | Tuple(ast::TupleFieldDefList), | ||
171 | Unit, | ||
172 | } | ||
173 | |||
174 | impl StructKind { | ||
175 | fn from_node<N: AstNode>(node: &N) -> StructKind { | ||
176 | if let Some(nfdl) = support::child::<ast::RecordFieldDefList>(node.syntax()) { | ||
177 | StructKind::Record(nfdl) | ||
178 | } else if let Some(pfl) = support::child::<ast::TupleFieldDefList>(node.syntax()) { | ||
179 | StructKind::Tuple(pfl) | ||
180 | } else { | ||
181 | StructKind::Unit | ||
182 | } | ||
183 | } | ||
184 | } | ||
185 | |||
186 | impl ast::StructDef { | ||
187 | pub fn kind(&self) -> StructKind { | ||
188 | StructKind::from_node(self) | ||
189 | } | ||
190 | } | ||
191 | |||
192 | impl ast::RecordField { | ||
193 | pub fn for_field_name(field_name: &ast::NameRef) -> Option<ast::RecordField> { | ||
194 | let candidate = | ||
195 | field_name.syntax().parent().and_then(ast::RecordField::cast).or_else(|| { | ||
196 | field_name.syntax().ancestors().nth(4).and_then(ast::RecordField::cast) | ||
197 | })?; | ||
198 | if candidate.field_name().as_ref() == Some(field_name) { | ||
199 | Some(candidate) | ||
200 | } else { | ||
201 | None | ||
202 | } | ||
203 | } | ||
204 | |||
205 | /// Deals with field init shorthand | ||
206 | pub fn field_name(&self) -> Option<ast::NameRef> { | ||
207 | if let Some(name_ref) = self.name_ref() { | ||
208 | return Some(name_ref); | ||
209 | } | ||
210 | if let Some(ast::Expr::PathExpr(expr)) = self.expr() { | ||
211 | let path = expr.path()?; | ||
212 | let segment = path.segment()?; | ||
213 | let name_ref = segment.name_ref()?; | ||
214 | if path.qualifier().is_none() { | ||
215 | return Some(name_ref); | ||
216 | } | ||
217 | } | ||
218 | None | ||
219 | } | ||
220 | } | ||
221 | |||
222 | pub enum NameOrNameRef { | ||
223 | Name(ast::Name), | ||
224 | NameRef(ast::NameRef), | ||
225 | } | ||
226 | |||
227 | impl fmt::Display for NameOrNameRef { | ||
228 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
229 | match self { | ||
230 | NameOrNameRef::Name(it) => fmt::Display::fmt(it, f), | ||
231 | NameOrNameRef::NameRef(it) => fmt::Display::fmt(it, f), | ||
232 | } | ||
233 | } | ||
234 | } | ||
235 | |||
236 | impl ast::RecordFieldPat { | ||
237 | /// Deals with field init shorthand | ||
238 | pub fn field_name(&self) -> Option<NameOrNameRef> { | ||
239 | if let Some(name_ref) = self.name_ref() { | ||
240 | return Some(NameOrNameRef::NameRef(name_ref)); | ||
241 | } | ||
242 | if let Some(ast::Pat::BindPat(pat)) = self.pat() { | ||
243 | let name = pat.name()?; | ||
244 | return Some(NameOrNameRef::Name(name)); | ||
245 | } | ||
246 | None | ||
247 | } | ||
248 | } | ||
249 | |||
250 | impl ast::EnumVariant { | ||
251 | pub fn parent_enum(&self) -> ast::EnumDef { | ||
252 | self.syntax() | ||
253 | .parent() | ||
254 | .and_then(|it| it.parent()) | ||
255 | .and_then(ast::EnumDef::cast) | ||
256 | .expect("EnumVariants are always nested in Enums") | ||
257 | } | ||
258 | pub fn kind(&self) -> StructKind { | ||
259 | StructKind::from_node(self) | ||
260 | } | ||
261 | } | ||
262 | |||
263 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
264 | pub enum FieldKind { | ||
265 | Name(ast::NameRef), | ||
266 | Index(SyntaxToken), | ||
267 | } | ||
268 | |||
269 | impl ast::FieldExpr { | ||
270 | pub fn index_token(&self) -> Option<SyntaxToken> { | ||
271 | self.syntax | ||
272 | .children_with_tokens() | ||
273 | // FIXME: Accepting floats here to reject them in validation later | ||
274 | .find(|c| c.kind() == SyntaxKind::INT_NUMBER || c.kind() == SyntaxKind::FLOAT_NUMBER) | ||
275 | .as_ref() | ||
276 | .and_then(SyntaxElement::as_token) | ||
277 | .cloned() | ||
278 | } | ||
279 | |||
280 | pub fn field_access(&self) -> Option<FieldKind> { | ||
281 | if let Some(nr) = self.name_ref() { | ||
282 | Some(FieldKind::Name(nr)) | ||
283 | } else if let Some(tok) = self.index_token() { | ||
284 | Some(FieldKind::Index(tok)) | ||
285 | } else { | ||
286 | None | ||
287 | } | ||
288 | } | ||
289 | } | ||
290 | |||
291 | pub struct SlicePatComponents { | ||
292 | pub prefix: Vec<ast::Pat>, | ||
293 | pub slice: Option<ast::Pat>, | ||
294 | pub suffix: Vec<ast::Pat>, | ||
295 | } | ||
296 | |||
297 | impl ast::SlicePat { | ||
298 | pub fn components(&self) -> SlicePatComponents { | ||
299 | let mut args = self.args().peekable(); | ||
300 | let prefix = args | ||
301 | .peeking_take_while(|p| match p { | ||
302 | ast::Pat::DotDotPat(_) => false, | ||
303 | ast::Pat::BindPat(bp) => match bp.pat() { | ||
304 | Some(ast::Pat::DotDotPat(_)) => false, | ||
305 | _ => true, | ||
306 | }, | ||
307 | ast::Pat::RefPat(rp) => match rp.pat() { | ||
308 | Some(ast::Pat::DotDotPat(_)) => false, | ||
309 | Some(ast::Pat::BindPat(bp)) => match bp.pat() { | ||
310 | Some(ast::Pat::DotDotPat(_)) => false, | ||
311 | _ => true, | ||
312 | }, | ||
313 | _ => true, | ||
314 | }, | ||
315 | _ => true, | ||
316 | }) | ||
317 | .collect(); | ||
318 | let slice = args.next(); | ||
319 | let suffix = args.collect(); | ||
320 | |||
321 | SlicePatComponents { prefix, slice, suffix } | ||
322 | } | ||
323 | } | ||
324 | |||
325 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | ||
326 | pub enum SelfParamKind { | ||
327 | /// self | ||
328 | Owned, | ||
329 | /// &self | ||
330 | Ref, | ||
331 | /// &mut self | ||
332 | MutRef, | ||
333 | } | ||
334 | |||
335 | impl ast::SelfParam { | ||
336 | pub fn kind(&self) -> SelfParamKind { | ||
337 | if self.amp_token().is_some() { | ||
338 | if self.mut_token().is_some() { | ||
339 | SelfParamKind::MutRef | ||
340 | } else { | ||
341 | SelfParamKind::Ref | ||
342 | } | ||
343 | } else { | ||
344 | SelfParamKind::Owned | ||
345 | } | ||
346 | } | ||
347 | } | ||
348 | |||
349 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
350 | pub enum TypeBoundKind { | ||
351 | /// Trait | ||
352 | PathType(ast::PathType), | ||
353 | /// for<'a> ... | ||
354 | ForType(ast::ForType), | ||
355 | /// 'a | ||
356 | Lifetime(SyntaxToken), | ||
357 | } | ||
358 | |||
359 | impl ast::TypeBound { | ||
360 | pub fn kind(&self) -> TypeBoundKind { | ||
361 | if let Some(path_type) = support::children(self.syntax()).next() { | ||
362 | TypeBoundKind::PathType(path_type) | ||
363 | } else if let Some(for_type) = support::children(self.syntax()).next() { | ||
364 | TypeBoundKind::ForType(for_type) | ||
365 | } else if let Some(lifetime) = self.lifetime_token() { | ||
366 | TypeBoundKind::Lifetime(lifetime) | ||
367 | } else { | ||
368 | unreachable!() | ||
369 | } | ||
370 | } | ||
371 | |||
372 | pub fn const_question_token(&self) -> Option<SyntaxToken> { | ||
373 | self.syntax() | ||
374 | .children_with_tokens() | ||
375 | .filter_map(|it| it.into_token()) | ||
376 | .take_while(|it| it.kind() != T![const]) | ||
377 | .find(|it| it.kind() == T![?]) | ||
378 | } | ||
379 | |||
380 | pub fn question_token(&self) -> Option<SyntaxToken> { | ||
381 | if self.const_token().is_some() { | ||
382 | self.syntax() | ||
383 | .children_with_tokens() | ||
384 | .filter_map(|it| it.into_token()) | ||
385 | .skip_while(|it| it.kind() != T![const]) | ||
386 | .find(|it| it.kind() == T![?]) | ||
387 | } else { | ||
388 | support::token(&self.syntax, T![?]) | ||
389 | } | ||
390 | } | ||
391 | } | ||
392 | |||
393 | pub enum VisibilityKind { | ||
394 | In(ast::Path), | ||
395 | PubCrate, | ||
396 | PubSuper, | ||
397 | PubSelf, | ||
398 | Pub, | ||
399 | } | ||
400 | |||
401 | impl ast::Visibility { | ||
402 | pub fn kind(&self) -> VisibilityKind { | ||
403 | if let Some(path) = support::children(self.syntax()).next() { | ||
404 | VisibilityKind::In(path) | ||
405 | } else if self.crate_token().is_some() { | ||
406 | VisibilityKind::PubCrate | ||
407 | } else if self.super_token().is_some() { | ||
408 | VisibilityKind::PubSuper | ||
409 | } else if self.self_token().is_some() { | ||
410 | VisibilityKind::PubSelf | ||
411 | } else { | ||
412 | VisibilityKind::Pub | ||
413 | } | ||
414 | } | ||
415 | } | ||
416 | |||
417 | impl ast::MacroCall { | ||
418 | pub fn is_macro_rules(&self) -> Option<ast::Name> { | ||
419 | let name_ref = self.path()?.segment()?.name_ref()?; | ||
420 | if name_ref.text() == "macro_rules" { | ||
421 | self.name() | ||
422 | } else { | ||
423 | None | ||
424 | } | ||
425 | } | ||
426 | |||
427 | pub fn is_bang(&self) -> bool { | ||
428 | self.is_macro_rules().is_none() | ||
429 | } | ||
430 | } | ||
431 | |||
432 | impl ast::LifetimeParam { | ||
433 | pub fn lifetime_bounds(&self) -> impl Iterator<Item = SyntaxToken> { | ||
434 | self.syntax() | ||
435 | .children_with_tokens() | ||
436 | .filter_map(|it| it.into_token()) | ||
437 | .skip_while(|x| x.kind() != T![:]) | ||
438 | .filter(|it| it.kind() == T![lifetime]) | ||
439 | } | ||
440 | } | ||
441 | |||
442 | impl ast::RangePat { | ||
443 | pub fn start(&self) -> Option<ast::Pat> { | ||
444 | self.syntax() | ||
445 | .children_with_tokens() | ||
446 | .take_while(|it| !(it.kind() == T![..] || it.kind() == T![..=])) | ||
447 | .filter_map(|it| it.into_node()) | ||
448 | .find_map(ast::Pat::cast) | ||
449 | } | ||
450 | |||
451 | pub fn end(&self) -> Option<ast::Pat> { | ||
452 | self.syntax() | ||
453 | .children_with_tokens() | ||
454 | .skip_while(|it| !(it.kind() == T![..] || it.kind() == T![..=])) | ||
455 | .filter_map(|it| it.into_node()) | ||
456 | .find_map(ast::Pat::cast) | ||
457 | } | ||
458 | } | ||
459 | |||
460 | impl ast::TokenTree { | ||
461 | pub fn left_delimiter_token(&self) -> Option<SyntaxToken> { | ||
462 | self.syntax() | ||
463 | .first_child_or_token()? | ||
464 | .into_token() | ||
465 | .filter(|it| matches!(it.kind(), T!['{'] | T!['('] | T!['['])) | ||
466 | } | ||
467 | |||
468 | pub fn right_delimiter_token(&self) -> Option<SyntaxToken> { | ||
469 | self.syntax() | ||
470 | .last_child_or_token()? | ||
471 | .into_token() | ||
472 | .filter(|it| matches!(it.kind(), T!['}'] | T![')'] | T![']'])) | ||
473 | } | ||
474 | } | ||