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.rs | 120 |
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. |