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.rs505
1 files changed, 505 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..bba7310ad
--- /dev/null
+++ b/crates/ra_syntax/src/ast/node_ext.rs
@@ -0,0 +1,505 @@
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, 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 if self.eq_token().is_some() || self.token_tree().is_some() {
43 return None;
44 }
45 self.simple_name()
46 }
47
48 pub fn as_simple_call(&self) -> Option<(SmolStr, ast::TokenTree)> {
49 let tt = self.token_tree()?;
50 Some((self.simple_name()?, tt))
51 }
52
53 pub fn as_simple_key_value(&self) -> Option<(SmolStr, SmolStr)> {
54 let lit = self.literal()?;
55 let key = self.simple_name()?;
56 // FIXME: escape? raw string?
57 let value = lit.syntax().first_token()?.text().trim_matches('"').into();
58 Some((key, value))
59 }
60
61 pub fn simple_name(&self) -> Option<SmolStr> {
62 let path = self.path()?;
63 match (path.segment(), path.qualifier()) {
64 (Some(segment), None) => Some(segment.syntax().first_token()?.text().clone()),
65 _ => None,
66 }
67 }
68
69 pub fn kind(&self) -> AttrKind {
70 let first_token = self.syntax().first_token();
71 let first_token_kind = first_token.as_ref().map(SyntaxToken::kind);
72 let second_token_kind =
73 first_token.and_then(|token| token.next_token()).as_ref().map(SyntaxToken::kind);
74
75 match (first_token_kind, second_token_kind) {
76 (Some(SyntaxKind::POUND), Some(T![!])) => AttrKind::Inner,
77 _ => AttrKind::Outer,
78 }
79 }
80}
81
82#[derive(Debug, Clone, PartialEq, Eq)]
83pub enum PathSegmentKind {
84 Name(ast::NameRef),
85 Type { type_ref: Option<ast::TypeRef>, trait_ref: Option<ast::PathType> },
86 SelfKw,
87 SuperKw,
88 CrateKw,
89}
90
91impl ast::PathSegment {
92 pub fn parent_path(&self) -> ast::Path {
93 self.syntax()
94 .parent()
95 .and_then(ast::Path::cast)
96 .expect("segments are always nested in paths")
97 }
98
99 pub fn kind(&self) -> Option<PathSegmentKind> {
100 let res = if let Some(name_ref) = self.name_ref() {
101 PathSegmentKind::Name(name_ref)
102 } else {
103 match self.syntax().first_child_or_token()?.kind() {
104 T![self] => PathSegmentKind::SelfKw,
105 T![super] => PathSegmentKind::SuperKw,
106 T![crate] => PathSegmentKind::CrateKw,
107 T![<] => {
108 // <T> or <T as Trait>
109 // T is any TypeRef, Trait has to be a PathType
110 let mut type_refs =
111 self.syntax().children().filter(|node| ast::TypeRef::can_cast(node.kind()));
112 let type_ref = type_refs.next().and_then(ast::TypeRef::cast);
113 let trait_ref = type_refs.next().and_then(ast::PathType::cast);
114 PathSegmentKind::Type { type_ref, trait_ref }
115 }
116 _ => return None,
117 }
118 };
119 Some(res)
120 }
121}
122
123impl ast::Path {
124 pub fn parent_path(&self) -> Option<ast::Path> {
125 self.syntax().parent().and_then(ast::Path::cast)
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::Impl {
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 = support::children(self.syntax());
155 let first = types.next();
156 let second = types.next();
157 (first, second)
158 }
159}
160
161#[derive(Debug, Clone, PartialEq, Eq)]
162pub enum StructKind {
163 Record(ast::RecordFieldList),
164 Tuple(ast::TupleFieldList),
165 Unit,
166}
167
168impl StructKind {
169 fn from_node<N: AstNode>(node: &N) -> StructKind {
170 if let Some(nfdl) = support::child::<ast::RecordFieldList>(node.syntax()) {
171 StructKind::Record(nfdl)
172 } else if let Some(pfl) = support::child::<ast::TupleFieldList>(node.syntax()) {
173 StructKind::Tuple(pfl)
174 } else {
175 StructKind::Unit
176 }
177 }
178}
179
180impl ast::Struct {
181 pub fn kind(&self) -> StructKind {
182 StructKind::from_node(self)
183 }
184}
185
186impl ast::RecordExprField {
187 pub fn for_field_name(field_name: &ast::NameRef) -> Option<ast::RecordExprField> {
188 let candidate =
189 field_name.syntax().parent().and_then(ast::RecordExprField::cast).or_else(|| {
190 field_name.syntax().ancestors().nth(4).and_then(ast::RecordExprField::cast)
191 })?;
192 if candidate.field_name().as_ref() == Some(field_name) {
193 Some(candidate)
194 } else {
195 None
196 }
197 }
198
199 /// Deals with field init shorthand
200 pub fn field_name(&self) -> Option<ast::NameRef> {
201 if let Some(name_ref) = self.name_ref() {
202 return Some(name_ref);
203 }
204 if let Some(ast::Expr::PathExpr(expr)) = self.expr() {
205 let path = expr.path()?;
206 let segment = path.segment()?;
207 let name_ref = segment.name_ref()?;
208 if path.qualifier().is_none() {
209 return Some(name_ref);
210 }
211 }
212 None
213 }
214}
215
216pub enum NameOrNameRef {
217 Name(ast::Name),
218 NameRef(ast::NameRef),
219}
220
221impl fmt::Display for NameOrNameRef {
222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223 match self {
224 NameOrNameRef::Name(it) => fmt::Display::fmt(it, f),
225 NameOrNameRef::NameRef(it) => fmt::Display::fmt(it, f),
226 }
227 }
228}
229
230impl ast::RecordFieldPat {
231 /// Deals with field init shorthand
232 pub fn field_name(&self) -> Option<NameOrNameRef> {
233 if let Some(name_ref) = self.name_ref() {
234 return Some(NameOrNameRef::NameRef(name_ref));
235 }
236 if let Some(ast::Pat::BindPat(pat)) = self.pat() {
237 let name = pat.name()?;
238 return Some(NameOrNameRef::Name(name));
239 }
240 None
241 }
242}
243
244impl ast::Variant {
245 pub fn parent_enum(&self) -> ast::Enum {
246 self.syntax()
247 .parent()
248 .and_then(|it| it.parent())
249 .and_then(ast::Enum::cast)
250 .expect("EnumVariants are always nested in Enums")
251 }
252 pub fn kind(&self) -> StructKind {
253 StructKind::from_node(self)
254 }
255}
256
257#[derive(Debug, Clone, PartialEq, Eq)]
258pub enum FieldKind {
259 Name(ast::NameRef),
260 Index(SyntaxToken),
261}
262
263impl ast::FieldExpr {
264 pub fn index_token(&self) -> Option<SyntaxToken> {
265 self.syntax
266 .children_with_tokens()
267 // FIXME: Accepting floats here to reject them in validation later
268 .find(|c| c.kind() == SyntaxKind::INT_NUMBER || c.kind() == SyntaxKind::FLOAT_NUMBER)
269 .as_ref()
270 .and_then(SyntaxElement::as_token)
271 .cloned()
272 }
273
274 pub fn field_access(&self) -> Option<FieldKind> {
275 if let Some(nr) = self.name_ref() {
276 Some(FieldKind::Name(nr))
277 } else if let Some(tok) = self.index_token() {
278 Some(FieldKind::Index(tok))
279 } else {
280 None
281 }
282 }
283}
284
285pub struct SlicePatComponents {
286 pub prefix: Vec<ast::Pat>,
287 pub slice: Option<ast::Pat>,
288 pub suffix: Vec<ast::Pat>,
289}
290
291impl ast::SlicePat {
292 pub fn components(&self) -> SlicePatComponents {
293 let mut args = self.args().peekable();
294 let prefix = args
295 .peeking_take_while(|p| match p {
296 ast::Pat::DotDotPat(_) => false,
297 ast::Pat::BindPat(bp) => match bp.pat() {
298 Some(ast::Pat::DotDotPat(_)) => false,
299 _ => true,
300 },
301 ast::Pat::RefPat(rp) => match rp.pat() {
302 Some(ast::Pat::DotDotPat(_)) => false,
303 Some(ast::Pat::BindPat(bp)) => match bp.pat() {
304 Some(ast::Pat::DotDotPat(_)) => false,
305 _ => true,
306 },
307 _ => true,
308 },
309 _ => true,
310 })
311 .collect();
312 let slice = args.next();
313 let suffix = args.collect();
314
315 SlicePatComponents { prefix, slice, suffix }
316 }
317}
318
319#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
320pub enum SelfParamKind {
321 /// self
322 Owned,
323 /// &self
324 Ref,
325 /// &mut self
326 MutRef,
327}
328
329impl ast::SelfParam {
330 pub fn kind(&self) -> SelfParamKind {
331 if self.amp_token().is_some() {
332 if self.mut_token().is_some() {
333 SelfParamKind::MutRef
334 } else {
335 SelfParamKind::Ref
336 }
337 } else {
338 SelfParamKind::Owned
339 }
340 }
341}
342
343#[derive(Clone, Debug, PartialEq, Eq, Hash)]
344pub enum TypeBoundKind {
345 /// Trait
346 PathType(ast::PathType),
347 /// for<'a> ...
348 ForType(ast::ForType),
349 /// 'a
350 Lifetime(SyntaxToken),
351}
352
353impl ast::TypeBound {
354 pub fn kind(&self) -> TypeBoundKind {
355 if let Some(path_type) = support::children(self.syntax()).next() {
356 TypeBoundKind::PathType(path_type)
357 } else if let Some(for_type) = support::children(self.syntax()).next() {
358 TypeBoundKind::ForType(for_type)
359 } else if let Some(lifetime) = self.lifetime_token() {
360 TypeBoundKind::Lifetime(lifetime)
361 } else {
362 unreachable!()
363 }
364 }
365
366 pub fn const_question_token(&self) -> Option<SyntaxToken> {
367 self.syntax()
368 .children_with_tokens()
369 .filter_map(|it| it.into_token())
370 .take_while(|it| it.kind() != T![const])
371 .find(|it| it.kind() == T![?])
372 }
373
374 pub fn question_token(&self) -> Option<SyntaxToken> {
375 if self.const_token().is_some() {
376 self.syntax()
377 .children_with_tokens()
378 .filter_map(|it| it.into_token())
379 .skip_while(|it| it.kind() != T![const])
380 .find(|it| it.kind() == T![?])
381 } else {
382 support::token(&self.syntax, T![?])
383 }
384 }
385}
386
387pub enum VisibilityKind {
388 In(ast::Path),
389 PubCrate,
390 PubSuper,
391 PubSelf,
392 Pub,
393}
394
395impl ast::Visibility {
396 pub fn kind(&self) -> VisibilityKind {
397 if let Some(path) = support::children(self.syntax()).next() {
398 VisibilityKind::In(path)
399 } else if self.crate_token().is_some() {
400 VisibilityKind::PubCrate
401 } else if self.super_token().is_some() {
402 VisibilityKind::PubSuper
403 } else if self.self_token().is_some() {
404 VisibilityKind::PubSelf
405 } else {
406 VisibilityKind::Pub
407 }
408 }
409}
410
411impl ast::MacroCall {
412 pub fn is_macro_rules(&self) -> Option<ast::Name> {
413 let name_ref = self.path()?.segment()?.name_ref()?;
414 if name_ref.text() == "macro_rules" {
415 self.name()
416 } else {
417 None
418 }
419 }
420
421 pub fn is_bang(&self) -> bool {
422 self.is_macro_rules().is_none()
423 }
424}
425
426impl ast::LifetimeParam {
427 pub fn lifetime_bounds(&self) -> impl Iterator<Item = SyntaxToken> {
428 self.syntax()
429 .children_with_tokens()
430 .filter_map(|it| it.into_token())
431 .skip_while(|x| x.kind() != T![:])
432 .filter(|it| it.kind() == T![lifetime])
433 }
434}
435
436impl ast::RangePat {
437 pub fn start(&self) -> Option<ast::Pat> {
438 self.syntax()
439 .children_with_tokens()
440 .take_while(|it| !(it.kind() == T![..] || it.kind() == T![..=]))
441 .filter_map(|it| it.into_node())
442 .find_map(ast::Pat::cast)
443 }
444
445 pub fn end(&self) -> Option<ast::Pat> {
446 self.syntax()
447 .children_with_tokens()
448 .skip_while(|it| !(it.kind() == T![..] || it.kind() == T![..=]))
449 .filter_map(|it| it.into_node())
450 .find_map(ast::Pat::cast)
451 }
452}
453
454impl ast::TokenTree {
455 pub fn left_delimiter_token(&self) -> Option<SyntaxToken> {
456 self.syntax()
457 .first_child_or_token()?
458 .into_token()
459 .filter(|it| matches!(it.kind(), T!['{'] | T!['('] | T!['[']))
460 }
461
462 pub fn right_delimiter_token(&self) -> Option<SyntaxToken> {
463 self.syntax()
464 .last_child_or_token()?
465 .into_token()
466 .filter(|it| matches!(it.kind(), T!['}'] | T![')'] | T![']']))
467 }
468}
469
470impl ast::GenericParamList {
471 pub fn lifetime_params(&self) -> impl Iterator<Item = ast::LifetimeParam> {
472 self.generic_params().filter_map(|param| match param {
473 ast::GenericParam::LifetimeParam(it) => Some(it),
474 ast::GenericParam::TypeParam(_) | ast::GenericParam::ConstParam(_) => None,
475 })
476 }
477 pub fn type_params(&self) -> impl Iterator<Item = ast::TypeParam> {
478 self.generic_params().filter_map(|param| match param {
479 ast::GenericParam::TypeParam(it) => Some(it),
480 ast::GenericParam::LifetimeParam(_) | ast::GenericParam::ConstParam(_) => None,
481 })
482 }
483 pub fn const_params(&self) -> impl Iterator<Item = ast::ConstParam> {
484 self.generic_params().filter_map(|param| match param {
485 ast::GenericParam::ConstParam(it) => Some(it),
486 ast::GenericParam::TypeParam(_) | ast::GenericParam::LifetimeParam(_) => None,
487 })
488 }
489}
490
491impl ast::DocCommentsOwner for ast::SourceFile {}
492impl ast::DocCommentsOwner for ast::Fn {}
493impl ast::DocCommentsOwner for ast::Struct {}
494impl ast::DocCommentsOwner for ast::Union {}
495impl ast::DocCommentsOwner for ast::RecordField {}
496impl ast::DocCommentsOwner for ast::TupleField {}
497impl ast::DocCommentsOwner for ast::Enum {}
498impl ast::DocCommentsOwner for ast::Variant {}
499impl ast::DocCommentsOwner for ast::Trait {}
500impl ast::DocCommentsOwner for ast::Module {}
501impl ast::DocCommentsOwner for ast::Static {}
502impl ast::DocCommentsOwner for ast::Const {}
503impl ast::DocCommentsOwner for ast::TypeAlias {}
504impl ast::DocCommentsOwner for ast::Impl {}
505impl ast::DocCommentsOwner for ast::MacroCall {}