Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/cfg/src/cfg_expr.rs')
| -rw-r--r-- | crates/cfg/src/cfg_expr.rs | 117 |
1 files changed, 108 insertions, 9 deletions
diff --git a/crates/cfg/src/cfg_expr.rs b/crates/cfg/src/cfg_expr.rs index 7a21015e14..d253f6f492 100644 --- a/crates/cfg/src/cfg_expr.rs +++ b/crates/cfg/src/cfg_expr.rs @@ -56,6 +56,36 @@ pub enum CfgExpr { Not(Box<CfgExpr>), } +impl fmt::Display for CfgExpr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + CfgExpr::Atom(atom) => atom.fmt(f), + CfgExpr::All(exprs) => { + write!(f, "all(")?; + for (i, expr) in exprs.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + expr.fmt(f)?; + } + write!(f, ")") + } + CfgExpr::Any(exprs) => { + write!(f, "any(")?; + for (i, expr) in exprs.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + expr.fmt(f)?; + } + write!(f, ")") + } + CfgExpr::Not(expr) => write!(f, "not({})", expr), + CfgExpr::Invalid => write!(f, "invalid"), + } + } +} + impl From<CfgAtom> for CfgExpr { fn from(atom: CfgAtom) -> Self { CfgExpr::Atom(atom) @@ -63,16 +93,25 @@ impl From<CfgAtom> for CfgExpr { } impl CfgExpr { + // FIXME: Parsing from `tt` is only used in a handful of places, reconsider + // if we should switch them to AST. #[cfg(feature = "tt")] - pub fn parse<S: Copy>(tt: &tt::TopSubtree<S>) -> CfgExpr { + pub fn parse(tt: &tt::TopSubtree) -> CfgExpr { next_cfg_expr(&mut tt.iter()).unwrap_or(CfgExpr::Invalid) } #[cfg(feature = "tt")] - pub fn parse_from_iter<S: Copy>(tt: &mut tt::iter::TtIter<'_, S>) -> CfgExpr { + pub fn parse_from_iter(tt: &mut tt::iter::TtIter<'_>) -> CfgExpr { next_cfg_expr(tt).unwrap_or(CfgExpr::Invalid) } + #[cfg(feature = "syntax")] + pub fn parse_from_ast( + ast: &mut std::iter::Peekable<syntax::ast::TokenTreeChildren>, + ) -> CfgExpr { + next_cfg_expr_from_ast(ast).unwrap_or(CfgExpr::Invalid) + } + /// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates. pub fn fold(&self, query: &dyn Fn(&CfgAtom) -> bool) -> Option<bool> { match self { @@ -89,8 +128,67 @@ impl CfgExpr { } } +#[cfg(feature = "syntax")] +fn next_cfg_expr_from_ast( + it: &mut std::iter::Peekable<syntax::ast::TokenTreeChildren>, +) -> Option<CfgExpr> { + use intern::sym; + use syntax::{NodeOrToken, SyntaxKind, T, ast}; + + let name = match it.next() { + None => return None, + Some(NodeOrToken::Token(ident)) if ident.kind().is_any_identifier() => { + Symbol::intern(ident.text()) + } + Some(_) => return Some(CfgExpr::Invalid), + }; + + let ret = match it.peek() { + Some(NodeOrToken::Token(eq)) if eq.kind() == T![=] => { + it.next(); + if let Some(NodeOrToken::Token(literal)) = it.peek() + && matches!(literal.kind(), SyntaxKind::STRING) + { + let dummy_span = span::Span { + range: span::TextRange::empty(span::TextSize::new(0)), + anchor: span::SpanAnchor { + file_id: span::EditionedFileId::from_raw(0), + ast_id: span::FIXUP_ERASED_FILE_AST_ID_MARKER, + }, + ctx: span::SyntaxContext::root(span::Edition::Edition2015), + }; + let literal = + Symbol::intern(tt::token_to_literal(literal.text(), dummy_span).text()); + it.next(); + CfgAtom::KeyValue { key: name, value: literal.clone() }.into() + } else { + return Some(CfgExpr::Invalid); + } + } + Some(NodeOrToken::Node(subtree)) => { + let mut subtree_iter = ast::TokenTreeChildren::new(subtree).peekable(); + it.next(); + let mut subs = std::iter::from_fn(|| next_cfg_expr_from_ast(&mut subtree_iter)); + match name { + s if s == sym::all => CfgExpr::All(subs.collect()), + s if s == sym::any => CfgExpr::Any(subs.collect()), + s if s == sym::not => { + CfgExpr::Not(Box::new(subs.next().unwrap_or(CfgExpr::Invalid))) + } + _ => CfgExpr::Invalid, + } + } + _ => CfgAtom::Flag(name).into(), + }; + + // Eat comma separator + while it.next().is_some_and(|it| it.as_token().is_none_or(|it| it.kind() != T![,])) {} + + Some(ret) +} + #[cfg(feature = "tt")] -fn next_cfg_expr<S: Copy>(it: &mut tt::iter::TtIter<'_, S>) -> Option<CfgExpr> { +fn next_cfg_expr(it: &mut tt::iter::TtIter<'_>) -> Option<CfgExpr> { use intern::sym; use tt::iter::TtElement; @@ -100,20 +198,21 @@ fn next_cfg_expr<S: Copy>(it: &mut tt::iter::TtIter<'_, S>) -> Option<CfgExpr> { Some(_) => return Some(CfgExpr::Invalid), }; - let ret = match it.peek() { + let mut it_clone = it.clone(); + let ret = match it_clone.next() { Some(TtElement::Leaf(tt::Leaf::Punct(punct))) // Don't consume on e.g. `=>`. if punct.char == '=' && (punct.spacing == tt::Spacing::Alone - || it.remaining().flat_tokens().get(1).is_none_or(|peek2| { - !matches!(peek2, tt::TokenTree::Leaf(tt::Leaf::Punct(_))) + || it_clone.peek().is_none_or(|peek2| { + !matches!(peek2, tt::TtElement::Leaf(tt::Leaf::Punct(_))) })) => { - match it.remaining().flat_tokens().get(1) { - Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => { + match it_clone.next() { + Some(tt::TtElement::Leaf(tt::Leaf::Literal(literal))) => { it.next(); it.next(); - CfgAtom::KeyValue { key: name, value: literal.symbol.clone() }.into() + CfgAtom::KeyValue { key: name, value: Symbol::intern(literal.text()) }.into() } _ => return Some(CfgExpr::Invalid), } |