Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/syntax/src/ast/node_ext.rs')
-rw-r--r--crates/syntax/src/ast/node_ext.rs91
1 files changed, 74 insertions, 17 deletions
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index af741d100f..76cfea9d5b 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -10,7 +10,7 @@ use parser::SyntaxKind;
use rowan::{GreenNodeData, GreenTokenData};
use crate::{
- NodeOrToken, SmolStr, SyntaxElement, SyntaxToken, T, TokenText,
+ NodeOrToken, SmolStr, SyntaxElement, SyntaxElementChildren, SyntaxToken, T, TokenText,
ast::{
self, AstNode, AstToken, HasAttrs, HasGenericArgs, HasGenericParams, HasName,
HasTypeBounds, SyntaxNode, support,
@@ -447,24 +447,23 @@ impl ast::UseTreeList {
impl ast::Impl {
pub fn self_ty(&self) -> Option<ast::Type> {
- match self.target() {
- (Some(t), None) | (_, Some(t)) => Some(t),
- _ => None,
- }
+ self.target().1
}
pub fn trait_(&self) -> Option<ast::Type> {
- match self.target() {
- (Some(t), Some(_)) => Some(t),
- _ => None,
- }
+ self.target().0
}
fn target(&self) -> (Option<ast::Type>, Option<ast::Type>) {
- let mut types = support::children(self.syntax());
- let first = types.next();
- let second = types.next();
- (first, second)
+ let mut types = support::children(self.syntax()).peekable();
+ let for_kw = self.for_token();
+ let trait_ = types.next_if(|trait_: &ast::Type| {
+ for_kw.is_some_and(|for_kw| {
+ trait_.syntax().text_range().start() < for_kw.text_range().start()
+ })
+ });
+ let self_ty = types.next();
+ (trait_, self_ty)
}
pub fn for_trait_name_ref(name_ref: &ast::NameRef) -> Option<ast::Impl> {
@@ -813,13 +812,16 @@ pub enum TypeBoundKind {
}
impl ast::TypeBound {
- pub fn kind(&self) -> TypeBoundKind {
+ pub fn kind(&self) -> Option<TypeBoundKind> {
if let Some(path_type) = support::children(self.syntax()).next() {
- TypeBoundKind::PathType(self.for_binder(), path_type)
+ Some(TypeBoundKind::PathType(self.for_binder(), path_type))
+ } else if let Some(for_binder) = support::children::<ast::ForType>(&self.syntax).next() {
+ let Some(ast::Type::PathType(path_type)) = for_binder.ty() else { return None };
+ Some(TypeBoundKind::PathType(for_binder.for_binder(), path_type))
} else if let Some(args) = self.use_bound_generic_args() {
- TypeBoundKind::Use(args)
+ Some(TypeBoundKind::Use(args))
} else if let Some(lifetime) = self.lifetime() {
- TypeBoundKind::Lifetime(lifetime)
+ Some(TypeBoundKind::Lifetime(lifetime))
} else {
unreachable!()
}
@@ -1093,6 +1095,16 @@ impl ast::MatchGuard {
}
}
+impl ast::MatchArm {
+ pub fn parent_match(&self) -> ast::MatchExpr {
+ self.syntax()
+ .parent()
+ .and_then(|it| it.parent())
+ .and_then(ast::MatchExpr::cast)
+ .expect("MatchArms are always nested in MatchExprs")
+ }
+}
+
impl From<ast::Item> for ast::AnyHasAttrs {
fn from(node: ast::Item) -> Self {
Self::new(node)
@@ -1105,6 +1117,15 @@ impl From<ast::AssocItem> for ast::AnyHasAttrs {
}
}
+impl ast::FormatArgsArgName {
+ /// This is not a [`ast::Name`], because the name may be a keyword.
+ pub fn name(&self) -> SyntaxToken {
+ let name = self.syntax.first_token().unwrap();
+ assert!(name.kind().is_any_identifier());
+ name
+ }
+}
+
impl ast::OrPat {
pub fn leading_pipe(&self) -> Option<SyntaxToken> {
self.syntax
@@ -1114,3 +1135,39 @@ impl ast::OrPat {
.filter(|it| it.kind() == T![|])
}
}
+
+/// An iterator over the elements in an [`ast::TokenTree`].
+///
+/// Does not yield trivia or the delimiters.
+#[derive(Clone)]
+pub struct TokenTreeChildren {
+ iter: SyntaxElementChildren,
+}
+
+impl TokenTreeChildren {
+ #[inline]
+ pub fn new(tt: &ast::TokenTree) -> Self {
+ let mut iter = tt.syntax.children_with_tokens();
+ iter.next(); // Bump the opening delimiter.
+ Self { iter }
+ }
+}
+
+impl Iterator for TokenTreeChildren {
+ type Item = NodeOrToken<ast::TokenTree, SyntaxToken>;
+
+ #[inline]
+ fn next(&mut self) -> Option<Self::Item> {
+ self.iter.find_map(|item| match item {
+ NodeOrToken::Node(node) => ast::TokenTree::cast(node).map(NodeOrToken::Node),
+ NodeOrToken::Token(token) => {
+ let kind = token.kind();
+ (!matches!(
+ kind,
+ SyntaxKind::WHITESPACE | SyntaxKind::COMMENT | T![')'] | T![']'] | T!['}']
+ ))
+ .then_some(NodeOrToken::Token(token))
+ }
+ })
+ }
+}