Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/syntax/src/ast/traits.rs')
| -rw-r--r-- | crates/syntax/src/ast/traits.rs | 67 |
1 files changed, 39 insertions, 28 deletions
diff --git a/crates/syntax/src/ast/traits.rs b/crates/syntax/src/ast/traits.rs index 5290f32dd2..2f4109a2c9 100644 --- a/crates/syntax/src/ast/traits.rs +++ b/crates/syntax/src/ast/traits.rs @@ -4,8 +4,9 @@ use either::Either; use crate::{ - SyntaxElement, SyntaxToken, T, + SyntaxElement, SyntaxNode, SyntaxToken, T, ast::{self, AstChildren, AstNode, AstToken, support}, + match_ast, syntax_node::SyntaxElementChildren, }; @@ -76,34 +77,44 @@ pub trait HasAttrs: AstNode { self.attrs().filter_map(|x| x.as_simple_atom()).any(|x| x == atom) } - /// Returns all attributes of this node, including inner attributes that may not be directly under this node - /// but under a child. - fn attrs_including_inner(self) -> impl Iterator<Item = ast::Attr> - where - Self: Sized, - { - let inner_attrs_node = if let Some(it) = - support::child::<ast::BlockExpr>(self.syntax()).and_then(|it| it.stmt_list()) - { - Some(it.syntax) - } else if let Some(it) = support::child::<ast::MatchArmList>(self.syntax()) { - Some(it.syntax) - } else if let Some(it) = support::child::<ast::AssocItemList>(self.syntax()) { - Some(it.syntax) - } else if let Some(it) = support::child::<ast::ItemList>(self.syntax()) { - Some(it.syntax) - } else if let Some(it) = support::child::<ast::ExternItemList>(self.syntax()) { - Some(it.syntax) - } else if let Some(it) = support::child::<ast::MacroItems>(self.syntax()) { - Some(it.syntax) - } else { - None - }; - - self.attrs().chain(inner_attrs_node.into_iter().flat_map(|it| support::children(&it))) + /// This may return the same node as called with (with `SourceFile`). The caller has the responsibility + /// to avoid duplicate attributes. + fn inner_attributes_node(&self) -> Option<SyntaxNode> { + let syntax = self.syntax(); + Some(match_ast! { + match syntax { + // A `SourceFile` contains the inner attributes of itself. + ast::SourceFile(_) => syntax.clone(), + ast::ExternBlock(it) => it.extern_item_list()?.syntax().clone(), + ast::Fn(it) => it.body()?.stmt_list()?.syntax().clone(), + ast::MatchExpr(it) => it.match_arm_list()?.syntax().clone(), + ast::Impl(it) => it.assoc_item_list()?.syntax().clone(), + ast::Trait(it) => it.assoc_item_list()?.syntax().clone(), + ast::Module(it) => it.item_list()?.syntax().clone(), + ast::BlockExpr(it) => { + if !it.may_carry_attributes() { + return None; + } + syntax.clone() + }, + _ => return None, + } + }) } } +/// Returns all attributes of this node, including inner attributes that may not be directly under this node +/// but under a child. +pub fn attrs_including_inner(owner: &dyn HasAttrs) -> impl Iterator<Item = ast::Attr> + Clone { + owner.attrs().filter(|attr| attr.kind().is_outer()).chain( + owner + .inner_attributes_node() + .into_iter() + .flat_map(|node| support::children::<ast::Attr>(&node)) + .filter(|attr| attr.kind().is_inner()), + ) +} + pub trait HasDocComments: HasAttrs { fn doc_comments(&self) -> DocCommentIter { DocCommentIter { iter: self.syntax().children_with_tokens() } @@ -118,7 +129,7 @@ impl DocCommentIter { #[cfg(test)] pub fn doc_comment_text(self) -> Option<String> { let docs = itertools::Itertools::join( - &mut self.filter_map(|comment| comment.doc_comment().map(ToOwned::to_owned)), + &mut self.filter_map(|comment| comment.doc_comment().map(|it| it.0.to_owned())), "\n", ); if docs.is_empty() { None } else { Some(docs) } @@ -151,7 +162,7 @@ impl AttrDocCommentIter { impl Iterator for AttrDocCommentIter { type Item = Either<ast::Attr, ast::Comment>; fn next(&mut self) -> Option<Self::Item> { - self.iter.by_ref().find_map(|el| match el { + self.iter.find_map(|el| match el { SyntaxElement::Node(node) => ast::Attr::cast(node).map(Either::Left), SyntaxElement::Token(tok) => { ast::Comment::cast(tok).filter(ast::Comment::is_doc).map(Either::Right) |