Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-expand/src/attrs.rs')
-rw-r--r--crates/hir-expand/src/attrs.rs158
1 files changed, 95 insertions, 63 deletions
diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs
index 5dae27f7a1..bb17eb0627 100644
--- a/crates/hir-expand/src/attrs.rs
+++ b/crates/hir-expand/src/attrs.rs
@@ -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];
+ }
+
+ 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 {