Cleanup cfg check handling in expression store lowering

This commit is contained in:
Lukas Wirth 2025-04-29 10:49:41 +02:00
parent 7d9b839f9c
commit d11dbf648f
17 changed files with 234 additions and 188 deletions

View file

@ -2,7 +2,7 @@
use std::{borrow::Cow, fmt, ops};
use base_db::Crate;
use cfg::CfgExpr;
use cfg::{CfgExpr, CfgOptions};
use either::Either;
use intern::{Interned, Symbol, sym};
@ -14,11 +14,10 @@ use syntax::{AstNode, AstToken, SyntaxNode, ast, match_ast};
use syntax_bridge::{DocCommentDesugarMode, desugar_doc_comment_text, syntax_node_to_token_tree};
use triomphe::ThinArc;
use crate::name::Name;
use crate::{
InFile,
db::ExpandDatabase,
mod_path::ModPath,
name::Name,
span_map::SpanMapRef,
tt::{self, TopSubtree, token_to_literal},
};
@ -49,29 +48,7 @@ impl RawAttrs {
owner: &dyn ast::HasAttrs,
span_map: SpanMapRef<'_>,
) -> Self {
let entries: Vec<_> = collect_attrs(owner)
.filter_map(|(id, attr)| match attr {
Either::Left(attr) => {
attr.meta().and_then(|meta| Attr::from_src(db, meta, span_map, id))
}
Either::Right(comment) => comment.doc_comment().map(|doc| {
let span = span_map.span_for_range(comment.syntax().text_range());
let (text, kind) =
desugar_doc_comment_text(doc, DocCommentDesugarMode::ProcMacro);
Attr {
id,
input: Some(Box::new(AttrInput::Literal(tt::Literal {
symbol: text,
span,
kind,
suffix: None,
}))),
path: Interned::new(ModPath::from(Name::new_symbol(sym::doc, span.ctx))),
ctxt: span.ctx,
}
}),
})
.collect();
let entries: Vec<_> = Self::attrs_iter::<true>(db, owner, span_map).collect();
let entries = if entries.is_empty() {
None
@ -82,12 +59,61 @@ impl RawAttrs {
RawAttrs { entries }
}
pub fn from_attrs_owner(
/// A [`RawAttrs`] that has its `#[cfg_attr(...)]` attributes expanded.
pub fn new_expanded(
db: &dyn ExpandDatabase,
owner: InFile<&dyn ast::HasAttrs>,
owner: &dyn ast::HasAttrs,
span_map: SpanMapRef<'_>,
cfg_options: &CfgOptions,
) -> Self {
Self::new(db, owner.value, span_map)
let entries: Vec<_> =
Self::attrs_iter_expanded::<true>(db, owner, span_map, cfg_options).collect();
let entries = if entries.is_empty() {
None
} else {
Some(ThinArc::from_header_and_iter((), entries.into_iter()))
};
RawAttrs { entries }
}
pub fn attrs_iter<const DESUGAR_COMMENTS: bool>(
db: &dyn ExpandDatabase,
owner: &dyn ast::HasAttrs,
span_map: SpanMapRef<'_>,
) -> impl Iterator<Item = Attr> {
collect_attrs(owner).filter_map(move |(id, attr)| match attr {
Either::Left(attr) => {
attr.meta().and_then(|meta| Attr::from_src(db, meta, span_map, id))
}
Either::Right(comment) if DESUGAR_COMMENTS => comment.doc_comment().map(|doc| {
let span = span_map.span_for_range(comment.syntax().text_range());
let (text, kind) = desugar_doc_comment_text(doc, DocCommentDesugarMode::ProcMacro);
Attr {
id,
input: Some(Box::new(AttrInput::Literal(tt::Literal {
symbol: text,
span,
kind,
suffix: None,
}))),
path: Interned::new(ModPath::from(Name::new_symbol(sym::doc, span.ctx))),
ctxt: span.ctx,
}
}),
Either::Right(_) => None,
})
}
pub fn attrs_iter_expanded<const DESUGAR_COMMENTS: bool>(
db: &dyn ExpandDatabase,
owner: &dyn ast::HasAttrs,
span_map: SpanMapRef<'_>,
cfg_options: &CfgOptions,
) -> impl Iterator<Item = Attr> {
Self::attrs_iter::<DESUGAR_COMMENTS>(db, owner, span_map)
.flat_map(|attr| attr.expand_cfg_attr(db, cfg_options))
}
pub fn merge(&self, other: Self) -> Self {
@ -114,9 +140,8 @@ impl RawAttrs {
}
}
/// Processes `cfg_attr`s, returning the resulting semantic `Attrs`.
// FIXME: This should return a different type, signaling it was filtered?
pub fn filter(self, db: &dyn ExpandDatabase, krate: Crate) -> RawAttrs {
/// Processes `cfg_attr`s
pub fn expand_cfg_attr(self, db: &dyn ExpandDatabase, krate: Crate) -> RawAttrs {
let has_cfg_attrs =
self.iter().any(|attr| attr.path.as_ident().is_some_and(|name| *name == sym::cfg_attr));
if !has_cfg_attrs {
@ -126,37 +151,8 @@ impl RawAttrs {
let cfg_options = krate.cfg_options(db);
let new_attrs = self
.iter()
.flat_map(|attr| -> SmallVec<[_; 1]> {
let is_cfg_attr = attr.path.as_ident().is_some_and(|name| *name == sym::cfg_attr);
if !is_cfg_attr {
return smallvec![attr.clone()];
}
let subtree = match attr.token_tree_value() {
Some(it) => it,
_ => return smallvec![attr.clone()],
};
let (cfg, parts) = match parse_cfg_attr_input(subtree) {
Some(it) => it,
None => return smallvec![attr.clone()],
};
let index = attr.id;
let attrs = parts
.enumerate()
.take(1 << AttrId::CFG_ATTR_BITS)
.filter_map(|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)));
let cfg = TopSubtree::from_token_trees(subtree.top_subtree().delimiter, cfg);
let cfg = CfgExpr::parse(&cfg);
if cfg_options.check(&cfg) == Some(false) {
smallvec![]
} else {
cov_mark::hit!(cfg_attr_active);
attrs.collect()
}
})
.cloned()
.flat_map(|attr| attr.expand_cfg_attr(db, cfg_options))
.collect::<Vec<_>>();
let entries = if new_attrs.is_empty() {
None
@ -316,6 +312,42 @@ impl Attr {
pub fn path(&self) -> &ModPath {
&self.path
}
pub fn expand_cfg_attr(
self,
db: &dyn ExpandDatabase,
cfg_options: &CfgOptions,
) -> impl IntoIterator<Item = Self> {
let is_cfg_attr = self.path.as_ident().is_some_and(|name| *name == sym::cfg_attr);
if !is_cfg_attr {
return smallvec![self.clone()];
}
let subtree = match self.token_tree_value() {
Some(it) => it,
_ => return smallvec![self.clone()],
};
let (cfg, parts) = match parse_cfg_attr_input(subtree) {
Some(it) => it,
None => return smallvec![self.clone()],
};
let index = self.id;
let attrs = parts
.enumerate()
.take(1 << AttrId::CFG_ATTR_BITS)
.filter_map(|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)));
let cfg = TopSubtree::from_token_trees(subtree.top_subtree().delimiter, cfg);
let cfg = CfgExpr::parse(&cfg);
if cfg_options.check(&cfg) == Some(false) {
smallvec![]
} else {
cov_mark::hit!(cfg_attr_active);
attrs.collect::<SmallVec<[_; 1]>>()
}
}
}
impl Attr {

View file

@ -82,7 +82,7 @@ impl DeclarativeMacroExpander {
let transparency = |node| {
// ... would be nice to have the item tree here
let attrs = RawAttrs::new(db, node, map.as_ref()).filter(db, def_crate);
let attrs = RawAttrs::new_expanded(db, node, map.as_ref(), def_crate.cfg_options(db));
match attrs
.iter()
.find(|it| {