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.rs117
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),
}