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.rs120
1 files changed, 88 insertions, 32 deletions
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 3fc3b39fee..03118d01dc 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -8,6 +8,7 @@ use std::{borrow::Cow, fmt, iter::successors};
use itertools::Itertools;
use parser::SyntaxKind;
use rowan::{GreenNodeData, GreenTokenData};
+use smallvec::{SmallVec, smallvec};
use crate::{
NodeOrToken, SmolStr, SyntaxElement, SyntaxElementChildren, SyntaxToken, T, TokenText,
@@ -201,53 +202,95 @@ impl AttrKind {
}
}
-impl ast::Attr {
+impl ast::Meta {
pub fn as_simple_atom(&self) -> Option<SmolStr> {
- let meta = self.meta()?;
- if meta.eq_token().is_some() || meta.token_tree().is_some() {
- return None;
- }
- self.simple_name()
+ Some(self.as_simple_path()?.as_single_name_ref()?.text().into())
}
pub fn as_simple_call(&self) -> Option<(SmolStr, ast::TokenTree)> {
- let tt = self.meta()?.token_tree()?;
- Some((self.simple_name()?, tt))
+ let ast::Meta::TokenTreeMeta(meta) = self else { return None };
+ Some((meta.path()?.as_single_name_ref()?.text().into(), meta.token_tree()?))
}
pub fn as_simple_path(&self) -> Option<ast::Path> {
- let meta = self.meta()?;
- if meta.eq_token().is_some() || meta.token_tree().is_some() {
- return None;
- }
- self.path()
+ let ast::Meta::PathMeta(meta) = self else { return None };
+ meta.path()
}
pub fn simple_name(&self) -> Option<SmolStr> {
- let path = self.meta()?.path()?;
- match (path.segment(), path.qualifier()) {
- (Some(segment), None) => Some(segment.syntax().first_token()?.text().into()),
- _ => None,
+ match self {
+ ast::Meta::CfgAttrMeta(_) => Some(SmolStr::new_static("cfg_attr")),
+ ast::Meta::CfgMeta(_) => Some(SmolStr::new_static("cfg")),
+ _ => {
+ let path = self.path()?;
+ match (path.segment(), path.qualifier()) {
+ (Some(segment), None) => Some(segment.syntax().first_token()?.text().into()),
+ _ => None,
+ }
+ }
}
}
- pub fn kind(&self) -> AttrKind {
- match self.excl_token() {
- Some(_) => AttrKind::Inner,
- None => AttrKind::Outer,
+ pub fn path(&self) -> Option<ast::Path> {
+ match self {
+ ast::Meta::CfgAttrMeta(_) | ast::Meta::CfgMeta(_) => None,
+ ast::Meta::KeyValueMeta(it) => it.path(),
+ ast::Meta::PathMeta(it) => it.path(),
+ ast::Meta::TokenTreeMeta(it) => it.path(),
+ ast::Meta::UnsafeMeta(it) => it.meta()?.path(),
}
}
+ /// Includes `cfg_attr()` inner metas (without considering the predicate).
+ pub fn skip_cfg_attrs(self) -> SmallVec<[ast::Meta; 1]> {
+ match self {
+ ast::Meta::CfgAttrMeta(meta) => {
+ meta.metas().flat_map(|meta| meta.skip_cfg_attrs()).collect()
+ }
+ _ => smallvec![self],
+ }
+ }
+
+ /// FIXME: Calling this is almost always incorrect, as `cfg_attr` can contains multiple `Meta`s.
+ pub fn parent_attr(&self) -> Option<ast::Attr> {
+ self.syntax().ancestors().find_map(ast::Attr::cast)
+ }
+}
+
+impl ast::Attr {
+ pub fn as_simple_atom(&self) -> Option<SmolStr> {
+ self.meta().and_then(|meta| meta.as_simple_atom())
+ }
+
+ pub fn as_simple_call(&self) -> Option<(SmolStr, ast::TokenTree)> {
+ self.meta().and_then(|meta| meta.as_simple_call())
+ }
+
+ pub fn as_simple_path(&self) -> Option<ast::Path> {
+ self.meta().and_then(|meta| meta.as_simple_path())
+ }
+
+ pub fn simple_name(&self) -> Option<SmolStr> {
+ self.meta().and_then(|meta| meta.simple_name())
+ }
+
pub fn path(&self) -> Option<ast::Path> {
- self.meta()?.path()
+ self.meta().and_then(|meta| meta.path())
}
- pub fn expr(&self) -> Option<ast::Expr> {
- self.meta()?.expr()
+ pub fn kind(&self) -> AttrKind {
+ match self.excl_token() {
+ Some(_) => AttrKind::Inner,
+ None => AttrKind::Outer,
+ }
}
- pub fn token_tree(&self) -> Option<ast::TokenTree> {
- self.meta()?.token_tree()
+ /// Includes `cfg_attr()` inner metas (without considering the predicate).
+ pub fn skip_cfg_attrs(&self) -> SmallVec<[ast::Meta; 1]> {
+ match self.meta() {
+ Some(meta) => meta.skip_cfg_attrs(),
+ None => SmallVec::new(),
+ }
}
}
@@ -1015,12 +1058,6 @@ impl ast::TokenTree {
}
}
-impl ast::Meta {
- pub fn parent_attr(&self) -> Option<ast::Attr> {
- self.syntax().parent().and_then(ast::Attr::cast)
- }
-}
-
impl ast::GenericArgList {
pub fn lifetime_args(&self) -> impl Iterator<Item = ast::LifetimeArg> {
self.generic_args().filter_map(|arg| match arg {
@@ -1164,6 +1201,25 @@ impl ast::OrPat {
}
}
+#[derive(Debug, Clone)]
+pub enum CfgAtomKey {
+ True,
+ False,
+ Ident(SyntaxToken),
+}
+
+impl ast::CfgAtom {
+ pub fn key(&self) -> Option<CfgAtomKey> {
+ if self.true_token().is_some() {
+ Some(CfgAtomKey::True)
+ } else if self.false_token().is_some() {
+ Some(CfgAtomKey::False)
+ } else {
+ self.ident_token().map(CfgAtomKey::Ident)
+ }
+ }
+}
+
/// An iterator over the elements in an [`ast::TokenTree`].
///
/// Does not yield trivia or the delimiters.