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.
4 use std::{borrow::Cow, fmt, iter::successors};
6 use itertools::Itertools;
7 use parser::SyntaxKind;
8 use rowan::{GreenNodeData, GreenTokenData};
11 ast::{self, support, AstNode, AstToken, AttrsOwner, NameOwner, SyntaxNode},
12 NodeOrToken, SmolStr, SyntaxElement, SyntaxToken, TokenText, T,
16 pub fn text(&self) -> TokenText<'_> {
17 text_of_first_token(self.syntax())
22 pub fn text(&self) -> TokenText<'_> {
23 text_of_first_token(self.syntax())
28 pub fn text(&self) -> TokenText<'_> {
29 text_of_first_token(self.syntax())
32 pub fn as_tuple_field(&self) -> Option<usize> {
33 self.text().parse().ok()
37 fn text_of_first_token(node: &SyntaxNode) -> TokenText<'_> {
38 fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData {
39 green_ref.children().next().and_then(NodeOrToken::into_token).unwrap()
43 Cow::Borrowed(green_ref) => TokenText::borrowed(first_token(green_ref).text()),
44 Cow::Owned(green) => TokenText::owned(first_token(&green).to_owned()),
48 #[derive(Debug, PartialEq, Eq, Clone)]
50 MacroRules(ast::MacroRules),
51 MacroDef(ast::MacroDef),
54 impl From<ast::MacroRules> for Macro {
55 fn from(it: ast::MacroRules) -> Self {
60 impl From<ast::MacroDef> for Macro {
61 fn from(it: ast::MacroDef) -> Self {
66 impl AstNode for Macro {
67 fn can_cast(kind: SyntaxKind) -> bool {
68 matches!(kind, SyntaxKind::MACRO_RULES | SyntaxKind::MACRO_DEF)
70 fn cast(syntax: SyntaxNode) -> Option<Self> {
71 let res = match syntax.kind() {
72 SyntaxKind::MACRO_RULES => Macro::MacroRules(ast::MacroRules { syntax }),
73 SyntaxKind::MACRO_DEF => Macro::MacroDef(ast::MacroDef { syntax }),
78 fn syntax(&self) -> &SyntaxNode {
80 Macro::MacroRules(it) => it.syntax(),
81 Macro::MacroDef(it) => it.syntax(),
86 impl NameOwner for Macro {
87 fn name(&self) -> Option<ast::Name> {
89 Macro::MacroRules(mac) => mac.name(),
90 Macro::MacroDef(mac) => mac.name(),
95 impl AttrsOwner for Macro {}
97 /// Basically an owned `dyn AttrsOwner` without extra boxing.
98 pub struct AttrsOwnerNode {
102 impl AttrsOwnerNode {
103 pub fn new<N: AttrsOwner>(node: N) -> Self {
104 AttrsOwnerNode { node: node.syntax().clone() }
108 impl AttrsOwner for AttrsOwnerNode {}
109 impl AstNode for AttrsOwnerNode {
110 fn can_cast(_: SyntaxKind) -> bool
116 fn cast(_: SyntaxNode) -> Option<Self>
122 fn syntax(&self) -> &SyntaxNode {
127 #[derive(Debug, Clone, PartialEq, Eq)]
134 /// Returns `true` if the attr_kind is [`Inner`].
135 pub fn is_inner(&self) -> bool {
136 matches!(self, Self::Inner)
139 /// Returns `true` if the attr_kind is [`Outer`].
140 pub fn is_outer(&self) -> bool {
141 matches!(self, Self::Outer)
146 pub fn as_simple_atom(&self) -> Option<SmolStr> {
147 if self.eq_token().is_some() || self.token_tree().is_some() {
153 pub fn as_simple_call(&self) -> Option<(SmolStr, ast::TokenTree)> {
154 let tt = self.token_tree()?;
155 Some((self.simple_name()?, tt))
158 pub fn simple_name(&self) -> Option<SmolStr> {
159 let path = self.path()?;
160 match (path.segment(), path.qualifier()) {
161 (Some(segment), None) => Some(segment.syntax().first_token()?.text().into()),
166 pub fn kind(&self) -> AttrKind {
167 let first_token = self.syntax().first_token();
168 let first_token_kind = first_token.as_ref().map(SyntaxToken::kind);
169 let second_token_kind =
170 first_token.and_then(|token| token.next_token()).as_ref().map(SyntaxToken::kind);
172 match (first_token_kind, second_token_kind) {
173 (Some(T![#]), Some(T![!])) => AttrKind::Inner,
174 _ => AttrKind::Outer,
179 #[derive(Debug, Clone, PartialEq, Eq)]
180 pub enum PathSegmentKind {
182 Type { type_ref: Option<ast::Type>, trait_ref: Option<ast::PathType> },
188 impl ast::PathSegment {
189 pub fn parent_path(&self) -> ast::Path {
192 .and_then(ast::Path::cast)
193 .expect("segments are always nested in paths")
196 pub fn crate_token(&self) -> Option<SyntaxToken> {
197 self.name_ref().and_then(|it| it.crate_token())
200 pub fn self_token(&self) -> Option<SyntaxToken> {
201 self.name_ref().and_then(|it| it.self_token())
204 pub fn super_token(&self) -> Option<SyntaxToken> {
205 self.name_ref().and_then(|it| it.super_token())
208 pub fn kind(&self) -> Option<PathSegmentKind> {
209 let res = if let Some(name_ref) = self.name_ref() {
210 match name_ref.syntax().first_token().map(|it| it.kind()) {
211 Some(T![self]) => PathSegmentKind::SelfKw,
212 Some(T![super]) => PathSegmentKind::SuperKw,
213 Some(T![crate]) => PathSegmentKind::CrateKw,
214 _ => PathSegmentKind::Name(name_ref),
217 match self.syntax().first_child_or_token()?.kind() {
219 // <T> or <T as Trait>
220 // T is any TypeRef, Trait has to be a PathType
222 self.syntax().children().filter(|node| ast::Type::can_cast(node.kind()));
223 let type_ref = type_refs.next().and_then(ast::Type::cast);
224 let trait_ref = type_refs.next().and_then(ast::PathType::cast);
225 PathSegmentKind::Type { type_ref, trait_ref }
235 pub fn parent_path(&self) -> Option<ast::Path> {
236 self.syntax().parent().and_then(ast::Path::cast)
239 pub fn as_single_segment(&self) -> Option<ast::PathSegment> {
240 match self.qualifier() {
242 None => self.segment(),
246 pub fn first_qualifier_or_self(&self) -> ast::Path {
247 successors(Some(self.clone()), ast::Path::qualifier).last().unwrap()
250 pub fn first_segment(&self) -> Option<ast::PathSegment> {
251 self.first_qualifier_or_self().segment()
254 pub fn segments(&self) -> impl Iterator<Item = ast::PathSegment> + Clone {
255 // cant make use of SyntaxNode::siblings, because the returned Iterator is not clone
256 successors(self.first_segment(), |p| {
257 p.parent_path().parent_path().and_then(|p| p.segment())
262 pub fn is_simple_path(&self) -> bool {
263 self.use_tree_list().is_none() && self.star_token().is_none()
267 impl ast::UseTreeList {
268 pub fn parent_use_tree(&self) -> ast::UseTree {
271 .and_then(ast::UseTree::cast)
272 .expect("UseTreeLists are always nested in UseTrees")
275 pub fn has_inner_comment(&self) -> bool {
277 .children_with_tokens()
278 .filter_map(|it| it.into_token())
279 .find_map(ast::Comment::cast)
285 pub fn self_ty(&self) -> Option<ast::Type> {
286 match self.target() {
287 (Some(t), None) | (_, Some(t)) => Some(t),
292 pub fn trait_(&self) -> Option<ast::Type> {
293 match self.target() {
294 (Some(t), Some(_)) => Some(t),
299 fn target(&self) -> (Option<ast::Type>, Option<ast::Type>) {
300 let mut types = support::children(self.syntax());
301 let first = types.next();
302 let second = types.next();
307 #[derive(Debug, Clone, PartialEq, Eq)]
308 pub enum StructKind {
309 Record(ast::RecordFieldList),
310 Tuple(ast::TupleFieldList),
315 fn from_node<N: AstNode>(node: &N) -> StructKind {
316 if let Some(nfdl) = support::child::<ast::RecordFieldList>(node.syntax()) {
317 StructKind::Record(nfdl)
318 } else if let Some(pfl) = support::child::<ast::TupleFieldList>(node.syntax()) {
319 StructKind::Tuple(pfl)
327 pub fn kind(&self) -> StructKind {
328 StructKind::from_node(self)
332 impl ast::RecordExprField {
333 pub fn for_field_name(field_name: &ast::NameRef) -> Option<ast::RecordExprField> {
334 let candidate = Self::for_name_ref(field_name)?;
335 if candidate.field_name().as_ref() == Some(field_name) {
342 pub fn for_name_ref(name_ref: &ast::NameRef) -> Option<ast::RecordExprField> {
343 let syn = name_ref.syntax();
345 .and_then(ast::RecordExprField::cast)
346 .or_else(|| syn.ancestors().nth(4).and_then(ast::RecordExprField::cast))
349 /// Deals with field init shorthand
350 pub fn field_name(&self) -> Option<ast::NameRef> {
351 if let Some(name_ref) = self.name_ref() {
352 return Some(name_ref);
354 self.expr()?.name_ref()
358 #[derive(Debug, Clone)]
360 NameRef(ast::NameRef),
362 Lifetime(ast::Lifetime),
366 pub fn as_name_ref(&self) -> Option<&ast::NameRef> {
368 NameLike::NameRef(name_ref) => Some(name_ref),
374 impl ast::AstNode for NameLike {
375 fn can_cast(kind: SyntaxKind) -> bool {
376 matches!(kind, SyntaxKind::NAME | SyntaxKind::NAME_REF | SyntaxKind::LIFETIME)
378 fn cast(syntax: SyntaxNode) -> Option<Self> {
379 let res = match syntax.kind() {
380 SyntaxKind::NAME => NameLike::Name(ast::Name { syntax }),
381 SyntaxKind::NAME_REF => NameLike::NameRef(ast::NameRef { syntax }),
382 SyntaxKind::LIFETIME => NameLike::Lifetime(ast::Lifetime { syntax }),
387 fn syntax(&self) -> &SyntaxNode {
389 NameLike::NameRef(it) => it.syntax(),
390 NameLike::Name(it) => it.syntax(),
391 NameLike::Lifetime(it) => it.syntax(),
398 ast::{Lifetime, Name, NameRef},
401 stdx::impl_from!(NameRef, Name, Lifetime for NameLike);
404 #[derive(Debug, Clone, PartialEq)]
405 pub enum NameOrNameRef {
407 NameRef(ast::NameRef),
410 impl fmt::Display for NameOrNameRef {
411 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
413 NameOrNameRef::Name(it) => fmt::Display::fmt(it, f),
414 NameOrNameRef::NameRef(it) => fmt::Display::fmt(it, f),
420 pub fn text(&self) -> TokenText<'_> {
422 NameOrNameRef::Name(name) => name.text(),
423 NameOrNameRef::NameRef(name_ref) => name_ref.text(),
428 impl ast::RecordPatField {
429 pub fn for_field_name_ref(field_name: &ast::NameRef) -> Option<ast::RecordPatField> {
430 let candidate = field_name.syntax().parent().and_then(ast::RecordPatField::cast)?;
431 match candidate.field_name()? {
432 NameOrNameRef::NameRef(name_ref) if name_ref == *field_name => Some(candidate),
437 pub fn for_field_name(field_name: &ast::Name) -> Option<ast::RecordPatField> {
439 field_name.syntax().ancestors().nth(2).and_then(ast::RecordPatField::cast)?;
440 match candidate.field_name()? {
441 NameOrNameRef::Name(name) if name == *field_name => Some(candidate),
446 /// Deals with field init shorthand
447 pub fn field_name(&self) -> Option<NameOrNameRef> {
448 if let Some(name_ref) = self.name_ref() {
449 return Some(NameOrNameRef::NameRef(name_ref));
452 Some(ast::Pat::IdentPat(pat)) => {
453 let name = pat.name()?;
454 Some(NameOrNameRef::Name(name))
456 Some(ast::Pat::BoxPat(pat)) => match pat.pat() {
457 Some(ast::Pat::IdentPat(pat)) => {
458 let name = pat.name()?;
459 Some(NameOrNameRef::Name(name))
469 pub fn parent_enum(&self) -> ast::Enum {
472 .and_then(|it| it.parent())
473 .and_then(ast::Enum::cast)
474 .expect("EnumVariants are always nested in Enums")
476 pub fn kind(&self) -> StructKind {
477 StructKind::from_node(self)
481 #[derive(Debug, Clone, PartialEq, Eq)]
487 impl ast::FieldExpr {
488 pub fn index_token(&self) -> Option<SyntaxToken> {
490 .children_with_tokens()
491 // FIXME: Accepting floats here to reject them in validation later
492 .find(|c| c.kind() == SyntaxKind::INT_NUMBER || c.kind() == SyntaxKind::FLOAT_NUMBER)
494 .and_then(SyntaxElement::as_token)
498 pub fn field_access(&self) -> Option<FieldKind> {
499 if let Some(nr) = self.name_ref() {
500 Some(FieldKind::Name(nr))
502 self.index_token().map(FieldKind::Index)
507 pub struct SlicePatComponents {
508 pub prefix: Vec<ast::Pat>,
509 pub slice: Option<ast::Pat>,
510 pub suffix: Vec<ast::Pat>,
514 pub fn components(&self) -> SlicePatComponents {
515 let mut args = self.pats().peekable();
517 .peeking_take_while(|p| match p {
518 ast::Pat::RestPat(_) => false,
519 ast::Pat::IdentPat(bp) => !matches!(bp.pat(), Some(ast::Pat::RestPat(_))),
520 ast::Pat::RefPat(rp) => match rp.pat() {
521 Some(ast::Pat::RestPat(_)) => false,
522 Some(ast::Pat::IdentPat(bp)) => !matches!(bp.pat(), Some(ast::Pat::RestPat(_))),
528 let slice = args.next();
529 let suffix = args.collect();
531 SlicePatComponents { prefix, slice, suffix }
535 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
536 pub enum SelfParamKind {
545 impl ast::SelfParam {
546 pub fn kind(&self) -> SelfParamKind {
547 if self.amp_token().is_some() {
548 if self.mut_token().is_some() {
549 SelfParamKind::MutRef
559 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
560 pub enum TypeBoundKind {
562 PathType(ast::PathType),
564 ForType(ast::ForType),
566 Lifetime(ast::Lifetime),
569 impl ast::TypeBound {
570 pub fn kind(&self) -> TypeBoundKind {
571 if let Some(path_type) = support::children(self.syntax()).next() {
572 TypeBoundKind::PathType(path_type)
573 } else if let Some(for_type) = support::children(self.syntax()).next() {
574 TypeBoundKind::ForType(for_type)
575 } else if let Some(lifetime) = self.lifetime() {
576 TypeBoundKind::Lifetime(lifetime)
583 pub enum VisibilityKind {
591 impl ast::Visibility {
592 pub fn kind(&self) -> VisibilityKind {
595 if let Some(segment) =
596 path.as_single_segment().filter(|it| it.coloncolon_token().is_none())
598 if segment.crate_token().is_some() {
599 return VisibilityKind::PubCrate;
600 } else if segment.super_token().is_some() {
601 return VisibilityKind::PubSuper;
602 } else if segment.self_token().is_some() {
603 return VisibilityKind::PubSelf;
606 VisibilityKind::In(path)
608 None => VisibilityKind::Pub,
612 pub fn is_eq_to(&self, other: &Self) -> bool {
613 match (self.kind(), other.kind()) {
614 (VisibilityKind::In(this), VisibilityKind::In(other)) => {
615 stdx::iter_eq_by(this.segments(), other.segments(), |lhs, rhs| {
616 lhs.kind().zip(rhs.kind()).map_or(false, |it| match it {
617 (PathSegmentKind::CrateKw, PathSegmentKind::CrateKw)
618 | (PathSegmentKind::SelfKw, PathSegmentKind::SelfKw)
619 | (PathSegmentKind::SuperKw, PathSegmentKind::SuperKw) => true,
620 (PathSegmentKind::Name(lhs), PathSegmentKind::Name(rhs)) => {
621 lhs.text() == rhs.text()
627 (VisibilityKind::PubSelf, VisibilityKind::PubSelf)
628 | (VisibilityKind::PubSuper, VisibilityKind::PubSuper)
629 | (VisibilityKind::PubCrate, VisibilityKind::PubCrate)
630 | (VisibilityKind::Pub, VisibilityKind::Pub) => true,
636 impl ast::LifetimeParam {
637 pub fn lifetime_bounds(&self) -> impl Iterator<Item = SyntaxToken> {
639 .children_with_tokens()
640 .filter_map(|it| it.into_token())
641 .skip_while(|x| x.kind() != T![:])
642 .filter(|it| it.kind() == T![lifetime_ident])
647 pub fn start(&self) -> Option<ast::Pat> {
649 .children_with_tokens()
650 .take_while(|it| !(it.kind() == T![..] || it.kind() == T![..=]))
651 .filter_map(|it| it.into_node())
652 .find_map(ast::Pat::cast)
655 pub fn end(&self) -> Option<ast::Pat> {
657 .children_with_tokens()
658 .skip_while(|it| !(it.kind() == T![..] || it.kind() == T![..=]))
659 .filter_map(|it| it.into_node())
660 .find_map(ast::Pat::cast)
664 impl ast::TokenTree {
665 pub fn left_delimiter_token(&self) -> Option<SyntaxToken> {
667 .first_child_or_token()?
669 .filter(|it| matches!(it.kind(), T!['{'] | T!['('] | T!['[']))
672 pub fn right_delimiter_token(&self) -> Option<SyntaxToken> {
674 .last_child_or_token()?
676 .filter(|it| matches!(it.kind(), T!['}'] | T![')'] | T![']']))
680 impl ast::GenericParamList {
681 pub fn lifetime_params(&self) -> impl Iterator<Item = ast::LifetimeParam> {
682 self.generic_params().filter_map(|param| match param {
683 ast::GenericParam::LifetimeParam(it) => Some(it),
684 ast::GenericParam::TypeParam(_) | ast::GenericParam::ConstParam(_) => None,
687 pub fn type_params(&self) -> impl Iterator<Item = ast::TypeParam> {
688 self.generic_params().filter_map(|param| match param {
689 ast::GenericParam::TypeParam(it) => Some(it),
690 ast::GenericParam::LifetimeParam(_) | ast::GenericParam::ConstParam(_) => None,
693 pub fn const_params(&self) -> impl Iterator<Item = ast::ConstParam> {
694 self.generic_params().filter_map(|param| match param {
695 ast::GenericParam::ConstParam(it) => Some(it),
696 ast::GenericParam::TypeParam(_) | ast::GenericParam::LifetimeParam(_) => None,
701 impl ast::DocCommentsOwner for ast::SourceFile {}
702 impl ast::DocCommentsOwner for ast::Fn {}
703 impl ast::DocCommentsOwner for ast::Struct {}
704 impl ast::DocCommentsOwner for ast::Union {}
705 impl ast::DocCommentsOwner for ast::RecordField {}
706 impl ast::DocCommentsOwner for ast::TupleField {}
707 impl ast::DocCommentsOwner for ast::Enum {}
708 impl ast::DocCommentsOwner for ast::Variant {}
709 impl ast::DocCommentsOwner for ast::Trait {}
710 impl ast::DocCommentsOwner for ast::Module {}
711 impl ast::DocCommentsOwner for ast::Static {}
712 impl ast::DocCommentsOwner for ast::Const {}
713 impl ast::DocCommentsOwner for ast::TypeAlias {}
714 impl ast::DocCommentsOwner for ast::Impl {}
715 impl ast::DocCommentsOwner for ast::MacroRules {}
716 impl ast::DocCommentsOwner for ast::MacroDef {}
717 impl ast::DocCommentsOwner for ast::Macro {}
718 impl ast::DocCommentsOwner for ast::Use {}