aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src/ast/node_ext.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_syntax/src/ast/node_ext.rs')
-rw-r--r--crates/ra_syntax/src/ast/node_ext.rs474
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
4use std::fmt;
5
6use itertools::Itertools;
7use ra_parser::SyntaxKind;
8
9use crate::{
10 ast::{self, support, AstNode, AttrInput, NameOwner, SyntaxNode},
11 SmolStr, SyntaxElement, SyntaxToken, T,
12};
13
14impl ast::Name {
15 pub fn text(&self) -> &SmolStr {
16 text_of_first_token(self.syntax())
17 }
18}
19
20impl 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
30fn 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)]
35pub enum AttrKind {
36 Inner,
37 Outer,
38}
39
40impl 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)]
89pub 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
97impl 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
129impl ast::Path {
130 pub fn parent_path(&self) -> Option<ast::Path> {
131 self.syntax().parent().and_then(ast::Path::cast)
132 }
133}
134
135impl 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
144impl 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)]
168pub enum StructKind {
169 Record(ast::RecordFieldDefList),
170 Tuple(ast::TupleFieldDefList),
171 Unit,
172}
173
174impl 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
186impl ast::StructDef {
187 pub fn kind(&self) -> StructKind {
188 StructKind::from_node(self)
189 }
190}
191
192impl 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
222pub enum NameOrNameRef {
223 Name(ast::Name),
224 NameRef(ast::NameRef),
225}
226
227impl 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
236impl 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
250impl 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)]
264pub enum FieldKind {
265 Name(ast::NameRef),
266 Index(SyntaxToken),
267}
268
269impl 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
291pub struct SlicePatComponents {
292 pub prefix: Vec<ast::Pat>,
293 pub slice: Option<ast::Pat>,
294 pub suffix: Vec<ast::Pat>,
295}
296
297impl 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)]
326pub enum SelfParamKind {
327 /// self
328 Owned,
329 /// &self
330 Ref,
331 /// &mut self
332 MutRef,
333}
334
335impl 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)]
350pub enum TypeBoundKind {
351 /// Trait
352 PathType(ast::PathType),
353 /// for<'a> ...
354 ForType(ast::ForType),
355 /// 'a
356 Lifetime(SyntaxToken),
357}
358
359impl 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
393pub enum VisibilityKind {
394 In(ast::Path),
395 PubCrate,
396 PubSuper,
397 PubSelf,
398 Pub,
399}
400
401impl 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
417impl 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
432impl 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
442impl 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
460impl 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}