Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/attr.rs')
-rw-r--r--crates/hir-def/src/attr.rs251
1 files changed, 203 insertions, 48 deletions
diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs
index 710bffcefe..a80313aba3 100644
--- a/crates/hir-def/src/attr.rs
+++ b/crates/hir-def/src/attr.rs
@@ -2,31 +2,32 @@
use std::{borrow::Cow, hash::Hash, ops};
-use base_db::CrateId;
+use base_db::Crate;
use cfg::{CfgExpr, CfgOptions};
use either::Either;
use hir_expand::{
- attrs::{collect_attrs, Attr, AttrId, RawAttrs},
HirFileId, InFile,
+ attrs::{Attr, AttrId, RawAttrs, collect_attrs},
};
-use intern::{sym, Symbol};
+use intern::{Symbol, sym};
use la_arena::{ArenaMap, Idx, RawIdx};
use mbe::DelimiterKind;
+use rustc_abi::ReprOptions;
use syntax::{
- ast::{self, HasAttrs},
AstPtr,
+ ast::{self, HasAttrs},
};
use triomphe::Arc;
use tt::iter::{TtElement, TtIter};
use crate::{
+ AdtId, AttrDefId, GenericParamId, HasModule, ItemTreeLoc, LocalFieldId, Lookup, MacroId,
+ VariantId,
db::DefDatabase,
item_tree::{AttrOwner, FieldParent, ItemTreeNode},
lang_item::LangItem,
nameres::{ModuleOrigin, ModuleSource},
src::{HasChildSource, HasSource},
- AdtId, AttrDefId, GenericParamId, HasModule, ItemTreeLoc, LocalFieldId, Lookup, MacroId,
- VariantId,
};
/// Desugared attributes of an item post `cfg_attr` expansion.
@@ -44,8 +45,8 @@ impl Attrs {
(**self).iter().find(|attr| attr.id == id)
}
- pub(crate) fn filter(db: &dyn DefDatabase, krate: CrateId, raw_attrs: RawAttrs) -> Attrs {
- Attrs(raw_attrs.filter(db.upcast(), krate))
+ pub(crate) fn filter(db: &dyn DefDatabase, krate: Crate, raw_attrs: RawAttrs) -> Attrs {
+ Attrs(raw_attrs.filter(db, krate))
}
}
@@ -75,8 +76,6 @@ impl Attrs {
let _p = tracing::info_span!("fields_attrs_query").entered();
// FIXME: There should be some proper form of mapping between item tree field ids and hir field ids
let mut res = ArenaMap::default();
-
- let crate_graph = db.crate_graph();
let item_tree;
let (parent, fields, krate) = match v {
VariantId::EnumVariantId(it) => {
@@ -84,7 +83,7 @@ impl Attrs {
let krate = loc.parent.lookup(db).container.krate;
item_tree = loc.id.item_tree(db);
let variant = &item_tree[loc.id.value];
- (FieldParent::Variant(loc.id.value), &variant.fields, krate)
+ (FieldParent::EnumVariant(loc.id.value), &variant.fields, krate)
}
VariantId::StructId(it) => {
let loc = it.lookup(db);
@@ -102,7 +101,7 @@ impl Attrs {
}
};
- let cfg_options = &crate_graph[krate].cfg_options;
+ let cfg_options = krate.cfg_options(db);
let mut idx = 0;
for (id, _field) in fields.iter().enumerate() {
@@ -118,17 +117,20 @@ impl Attrs {
}
impl Attrs {
- pub fn by_key<'attrs>(&'attrs self, key: &'attrs Symbol) -> AttrQuery<'attrs> {
+ #[inline]
+ pub fn by_key(&self, key: Symbol) -> AttrQuery<'_> {
AttrQuery { attrs: self, key }
}
+ #[inline]
pub fn rust_analyzer_tool(&self) -> impl Iterator<Item = &Attr> {
self.iter()
.filter(|&attr| attr.path.segments().first().is_some_and(|s| *s == sym::rust_analyzer))
}
+ #[inline]
pub fn cfg(&self) -> Option<CfgExpr> {
- let mut cfgs = self.by_key(&sym::cfg).tt_values().map(CfgExpr::parse);
+ let mut cfgs = self.by_key(sym::cfg).tt_values().map(CfgExpr::parse);
let first = cfgs.next()?;
match cfgs.next() {
Some(second) => {
@@ -139,10 +141,12 @@ impl Attrs {
}
}
+ #[inline]
pub fn cfgs(&self) -> impl Iterator<Item = CfgExpr> + '_ {
- self.by_key(&sym::cfg).tt_values().map(CfgExpr::parse)
+ self.by_key(sym::cfg).tt_values().map(CfgExpr::parse)
}
+ #[inline]
pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
match self.cfg() {
None => true,
@@ -150,78 +154,225 @@ impl Attrs {
}
}
+ #[inline]
pub fn lang(&self) -> Option<&Symbol> {
- self.by_key(&sym::lang).string_value()
+ self.by_key(sym::lang).string_value()
}
+ #[inline]
pub fn lang_item(&self) -> Option<LangItem> {
- self.by_key(&sym::lang).string_value().and_then(LangItem::from_symbol)
+ self.by_key(sym::lang).string_value().and_then(LangItem::from_symbol)
}
+ #[inline]
pub fn has_doc_hidden(&self) -> bool {
- self.by_key(&sym::doc).tt_values().any(|tt| {
+ self.by_key(sym::doc).tt_values().any(|tt| {
tt.top_subtree().delimiter.kind == DelimiterKind::Parenthesis &&
matches!(tt.token_trees().flat_tokens(), [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::hidden)
})
}
+ #[inline]
pub fn has_doc_notable_trait(&self) -> bool {
- self.by_key(&sym::doc).tt_values().any(|tt| {
+ self.by_key(sym::doc).tt_values().any(|tt| {
tt.top_subtree().delimiter.kind == DelimiterKind::Parenthesis &&
matches!(tt.token_trees().flat_tokens(), [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::notable_trait)
})
}
+ #[inline]
pub fn doc_exprs(&self) -> impl Iterator<Item = DocExpr> + '_ {
- self.by_key(&sym::doc).tt_values().map(DocExpr::parse)
+ self.by_key(sym::doc).tt_values().map(DocExpr::parse)
}
+ #[inline]
pub fn doc_aliases(&self) -> impl Iterator<Item = Symbol> + '_ {
self.doc_exprs().flat_map(|doc_expr| doc_expr.aliases().to_vec())
}
+ #[inline]
pub fn export_name(&self) -> Option<&Symbol> {
- self.by_key(&sym::export_name).string_value()
+ self.by_key(sym::export_name).string_value()
}
+ #[inline]
pub fn is_proc_macro(&self) -> bool {
- self.by_key(&sym::proc_macro).exists()
+ self.by_key(sym::proc_macro).exists()
}
+ #[inline]
pub fn is_proc_macro_attribute(&self) -> bool {
- self.by_key(&sym::proc_macro_attribute).exists()
+ self.by_key(sym::proc_macro_attribute).exists()
}
+ #[inline]
pub fn is_proc_macro_derive(&self) -> bool {
- self.by_key(&sym::proc_macro_derive).exists()
+ self.by_key(sym::proc_macro_derive).exists()
}
+ #[inline]
pub fn is_test(&self) -> bool {
self.iter().any(|it| {
it.path()
.segments()
.iter()
.rev()
- .zip(
- [sym::core.clone(), sym::prelude.clone(), sym::v1.clone(), sym::test.clone()]
- .iter()
- .rev(),
- )
+ .zip([sym::core, sym::prelude, sym::v1, sym::test].iter().rev())
.all(|it| it.0 == it.1)
})
}
+ #[inline]
pub fn is_ignore(&self) -> bool {
- self.by_key(&sym::ignore).exists()
+ self.by_key(sym::ignore).exists()
}
+ #[inline]
pub fn is_bench(&self) -> bool {
- self.by_key(&sym::bench).exists()
+ self.by_key(sym::bench).exists()
}
+ #[inline]
pub fn is_unstable(&self) -> bool {
- self.by_key(&sym::unstable).exists()
+ self.by_key(sym::unstable).exists()
+ }
+
+ #[inline]
+ pub fn rustc_legacy_const_generics(&self) -> Option<Box<Box<[u32]>>> {
+ self.by_key(sym::rustc_legacy_const_generics)
+ .tt_values()
+ .next()
+ .map(parse_rustc_legacy_const_generics)
+ .filter(|it| !it.is_empty())
+ .map(Box::new)
+ }
+
+ #[inline]
+ pub fn repr(&self) -> Option<ReprOptions> {
+ self.by_key(sym::repr).tt_values().filter_map(parse_repr_tt).fold(None, |acc, repr| {
+ acc.map_or(Some(repr), |mut acc| {
+ merge_repr(&mut acc, repr);
+ Some(acc)
+ })
+ })
+ }
+}
+
+fn parse_rustc_legacy_const_generics(tt: &crate::tt::TopSubtree) -> Box<[u32]> {
+ let mut indices = Vec::new();
+ let mut iter = tt.iter();
+ while let (Some(first), second) = (iter.next(), iter.next()) {
+ match first {
+ TtElement::Leaf(tt::Leaf::Literal(lit)) => match lit.symbol.as_str().parse() {
+ Ok(index) => indices.push(index),
+ Err(_) => break,
+ },
+ _ => break,
+ }
+
+ if let Some(comma) = second {
+ match comma {
+ TtElement::Leaf(tt::Leaf::Punct(punct)) if punct.char == ',' => {}
+ _ => break,
+ }
+ }
}
+
+ indices.into_boxed_slice()
+}
+
+fn merge_repr(this: &mut ReprOptions, other: ReprOptions) {
+ let ReprOptions { int, align, pack, flags, field_shuffle_seed: _ } = this;
+ flags.insert(other.flags);
+ *align = (*align).max(other.align);
+ *pack = match (*pack, other.pack) {
+ (Some(pack), None) | (None, Some(pack)) => Some(pack),
+ _ => (*pack).min(other.pack),
+ };
+ if other.int.is_some() {
+ *int = other.int;
+ }
+}
+
+fn parse_repr_tt(tt: &crate::tt::TopSubtree) -> Option<ReprOptions> {
+ use crate::builtin_type::{BuiltinInt, BuiltinUint};
+ use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions};
+
+ match tt.top_subtree().delimiter {
+ tt::Delimiter { kind: DelimiterKind::Parenthesis, .. } => {}
+ _ => return None,
+ }
+
+ let mut acc = ReprOptions::default();
+ let mut tts = tt.iter();
+ while let Some(tt) = tts.next() {
+ let TtElement::Leaf(tt::Leaf::Ident(ident)) = tt else {
+ continue;
+ };
+ let repr = match &ident.sym {
+ s if *s == sym::packed => {
+ let pack = if let Some(TtElement::Subtree(_, mut tt_iter)) = tts.peek() {
+ tts.next();
+ if let Some(TtElement::Leaf(tt::Leaf::Literal(lit))) = tt_iter.next() {
+ lit.symbol.as_str().parse().unwrap_or_default()
+ } else {
+ 0
+ }
+ } else {
+ 0
+ };
+ let pack = Some(Align::from_bytes(pack).unwrap_or(Align::ONE));
+ ReprOptions { pack, ..Default::default() }
+ }
+ s if *s == sym::align => {
+ let mut align = None;
+ if let Some(TtElement::Subtree(_, mut tt_iter)) = tts.peek() {
+ tts.next();
+ if let Some(TtElement::Leaf(tt::Leaf::Literal(lit))) = tt_iter.next() {
+ if let Ok(a) = lit.symbol.as_str().parse() {
+ align = Align::from_bytes(a).ok();
+ }
+ }
+ }
+ ReprOptions { align, ..Default::default() }
+ }
+ s if *s == sym::C => ReprOptions { flags: ReprFlags::IS_C, ..Default::default() },
+ s if *s == sym::transparent => {
+ ReprOptions { flags: ReprFlags::IS_TRANSPARENT, ..Default::default() }
+ }
+ s if *s == sym::simd => ReprOptions { flags: ReprFlags::IS_SIMD, ..Default::default() },
+ repr => {
+ let mut int = None;
+ if let Some(builtin) = BuiltinInt::from_suffix_sym(repr)
+ .map(Either::Left)
+ .or_else(|| BuiltinUint::from_suffix_sym(repr).map(Either::Right))
+ {
+ int = Some(match builtin {
+ Either::Left(bi) => match bi {
+ BuiltinInt::Isize => IntegerType::Pointer(true),
+ BuiltinInt::I8 => IntegerType::Fixed(Integer::I8, true),
+ BuiltinInt::I16 => IntegerType::Fixed(Integer::I16, true),
+ BuiltinInt::I32 => IntegerType::Fixed(Integer::I32, true),
+ BuiltinInt::I64 => IntegerType::Fixed(Integer::I64, true),
+ BuiltinInt::I128 => IntegerType::Fixed(Integer::I128, true),
+ },
+ Either::Right(bu) => match bu {
+ BuiltinUint::Usize => IntegerType::Pointer(false),
+ BuiltinUint::U8 => IntegerType::Fixed(Integer::I8, false),
+ BuiltinUint::U16 => IntegerType::Fixed(Integer::I16, false),
+ BuiltinUint::U32 => IntegerType::Fixed(Integer::I32, false),
+ BuiltinUint::U64 => IntegerType::Fixed(Integer::I64, false),
+ BuiltinUint::U128 => IntegerType::Fixed(Integer::I128, false),
+ },
+ });
+ }
+ ReprOptions { int, ..Default::default() }
+ }
+ };
+ merge_repr(&mut acc, repr);
+ }
+
+ Some(acc)
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -373,7 +524,7 @@ impl AttrsWithOwner {
// FIXME: We should be never getting `None` here.
match src.value.get(it.local_id()) {
Some(val) => RawAttrs::from_attrs_owner(
- db.upcast(),
+ db,
src.with_value(val),
db.span_map(src.file_id).as_ref(),
),
@@ -385,7 +536,7 @@ impl AttrsWithOwner {
// FIXME: We should be never getting `None` here.
match src.value.get(it.local_id()) {
Some(val) => RawAttrs::from_attrs_owner(
- db.upcast(),
+ db,
src.with_value(val),
db.span_map(src.file_id).as_ref(),
),
@@ -397,7 +548,7 @@ impl AttrsWithOwner {
// FIXME: We should be never getting `None` here.
match src.value.get(it.local_id) {
Some(val) => RawAttrs::from_attrs_owner(
- db.upcast(),
+ db,
src.with_value(val),
db.span_map(src.file_id).as_ref(),
),
@@ -410,7 +561,7 @@ impl AttrsWithOwner {
AttrDefId::UseId(it) => attrs_from_item_tree_loc(db, it),
};
- let attrs = raw_attrs.filter(db.upcast(), def.krate(db));
+ let attrs = raw_attrs.filter(db, def.krate(db));
Attrs(attrs)
}
@@ -547,36 +698,42 @@ impl AttrSourceMap {
}
}
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone)]
pub struct AttrQuery<'attr> {
attrs: &'attr Attrs,
- key: &'attr Symbol,
+ key: Symbol,
}
impl<'attr> AttrQuery<'attr> {
+ #[inline]
pub fn tt_values(self) -> impl Iterator<Item = &'attr crate::tt::TopSubtree> {
self.attrs().filter_map(|attr| attr.token_tree_value())
}
+ #[inline]
pub fn string_value(self) -> Option<&'attr Symbol> {
self.attrs().find_map(|attr| attr.string_value())
}
+ #[inline]
pub fn string_value_with_span(self) -> Option<(&'attr Symbol, span::Span)> {
self.attrs().find_map(|attr| attr.string_value_with_span())
}
+ #[inline]
pub fn string_value_unescape(self) -> Option<Cow<'attr, str>> {
self.attrs().find_map(|attr| attr.string_value_unescape())
}
+ #[inline]
pub fn exists(self) -> bool {
self.attrs().next().is_some()
}
+ #[inline]
pub fn attrs(self) -> impl Iterator<Item = &'attr Attr> + Clone {
let key = self.key;
- self.attrs.iter().filter(move |attr| attr.path.as_ident().is_some_and(|s| *s == *key))
+ self.attrs.iter().filter(move |attr| attr.path.as_ident().is_some_and(|s| *s == key))
}
/// Find string value for a specific key inside token tree
@@ -585,10 +742,11 @@ impl<'attr> AttrQuery<'attr> {
/// #[doc(html_root_url = "url")]
/// ^^^^^^^^^^^^^ key
/// ```
- pub fn find_string_value_in_tt(self, key: &'attr Symbol) -> Option<&'attr str> {
+ #[inline]
+ pub fn find_string_value_in_tt(self, key: Symbol) -> Option<&'attr str> {
self.tt_values().find_map(|tt| {
let name = tt.iter()
- .skip_while(|tt| !matches!(tt, TtElement::Leaf(tt::Leaf::Ident(tt::Ident { sym, ..} )) if *sym == *key))
+ .skip_while(|tt| !matches!(tt, TtElement::Leaf(tt::Leaf::Ident(tt::Ident { sym, ..} )) if *sym == key))
.nth(2);
match name {
@@ -601,17 +759,14 @@ impl<'attr> AttrQuery<'attr> {
fn any_has_attrs<'db>(
db: &(dyn DefDatabase + 'db),
- id: impl Lookup<
- Database<'db> = dyn DefDatabase + 'db,
- Data = impl HasSource<Value = impl ast::HasAttrs>,
- >,
+ id: impl Lookup<Database = dyn DefDatabase, Data = impl HasSource<Value = impl ast::HasAttrs>>,
) -> InFile<ast::AnyHasAttrs> {
id.lookup(db).source(db).map(ast::AnyHasAttrs::new)
}
fn attrs_from_item_tree_loc<'db, N: ItemTreeNode>(
db: &(dyn DefDatabase + 'db),
- lookup: impl Lookup<Database<'db> = dyn DefDatabase + 'db, Data = impl ItemTreeLoc<Id = N>>,
+ lookup: impl Lookup<Database = dyn DefDatabase, Data = impl ItemTreeLoc<Id = N>>,
) -> RawAttrs {
let id = lookup.lookup(db).item_tree_id();
let tree = id.item_tree(db);
@@ -649,8 +804,8 @@ mod tests {
use hir_expand::span_map::{RealSpanMap, SpanMap};
use span::FileId;
- use syntax::{ast, AstNode, TextRange};
- use syntax_bridge::{syntax_node_to_token_tree, DocCommentDesugarMode};
+ use syntax::{AstNode, TextRange, ast};
+ use syntax_bridge::{DocCommentDesugarMode, syntax_node_to_token_tree};
use crate::attr::{DocAtom, DocExpr};