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.rs241
1 files changed, 197 insertions, 44 deletions
diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs
index 200072c172..bab3bbc232 100644
--- a/crates/hir-def/src/attr.rs
+++ b/crates/hir-def/src/attr.rs
@@ -1,6 +1,11 @@
//! A higher level attributes based on TokenTree, with also some shortcuts.
-use std::{hash::Hash, ops, sync::Arc};
+pub mod builtin;
+
+#[cfg(test)]
+mod tests;
+
+use std::{hash::Hash, ops};
use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
@@ -16,14 +21,16 @@ use syntax::{
ast::{self, HasAttrs, IsString},
AstPtr, AstToken, SmolStr, TextRange, TextSize,
};
+use triomphe::Arc;
use crate::{
db::DefDatabase,
item_tree::{AttrOwner, Fields, ItemTreeId, ItemTreeNode},
+ lang_item::LangItem,
nameres::{ModuleOrigin, ModuleSource},
src::{HasChildSource, HasSource},
- AdtId, AttrDefId, EnumId, GenericParamId, LocalEnumVariantId, LocalFieldId, Lookup, MacroId,
- VariantId,
+ AdtId, AssocItemLoc, AttrDefId, EnumId, GenericParamId, ItemLoc, LocalEnumVariantId,
+ LocalFieldId, Lookup, MacroId, VariantId,
};
/// Holds documentation
@@ -88,6 +95,7 @@ impl Attrs {
db: &dyn DefDatabase,
e: EnumId,
) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>> {
+ let _p = profile::span("variants_attrs_query");
// FIXME: There should be some proper form of mapping between item tree enum variant ids and hir enum variant ids
let mut res = ArenaMap::default();
@@ -114,6 +122,7 @@ impl Attrs {
db: &dyn DefDatabase,
v: VariantId,
) -> Arc<ArenaMap<LocalFieldId, Attrs>> {
+ let _p = profile::span("fields_attrs_query");
// FIXME: There should be some proper form of mapping between item tree field ids and hir field ids
let mut res = ArenaMap::default();
@@ -175,13 +184,13 @@ impl Attrs {
Arc::new(res)
}
+}
+impl Attrs {
pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
AttrQuery { attrs: self, key }
}
-}
-impl Attrs {
pub fn cfg(&self) -> Option<CfgExpr> {
let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse);
let first = cfgs.next()?;
@@ -193,6 +202,7 @@ impl Attrs {
None => Some(first),
}
}
+
pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
match self.cfg() {
None => true,
@@ -204,6 +214,10 @@ impl Attrs {
self.by_key("lang").string_value()
}
+ pub fn lang_item(&self) -> Option<LangItem> {
+ self.by_key("lang").string_value().and_then(|it| LangItem::from_str(it))
+ }
+
pub fn docs(&self) -> Option<Documentation> {
let docs = self.by_key("doc").attrs().filter_map(|attr| attr.string_value());
let indent = doc_indent(self);
@@ -238,6 +252,14 @@ impl Attrs {
})
}
+ pub fn doc_exprs(&self) -> impl Iterator<Item = DocExpr> + '_ {
+ self.by_key("doc").tt_values().map(DocExpr::parse)
+ }
+
+ pub fn doc_aliases(&self) -> impl Iterator<Item = SmolStr> + '_ {
+ self.doc_exprs().flat_map(|doc_expr| doc_expr.aliases().to_vec())
+ }
+
pub fn is_proc_macro(&self) -> bool {
self.by_key("proc_macro").exists()
}
@@ -249,10 +271,120 @@ impl Attrs {
pub fn is_proc_macro_derive(&self) -> bool {
self.by_key("proc_macro_derive").exists()
}
+
+ pub fn is_unstable(&self) -> bool {
+ self.by_key("unstable").exists()
+ }
+}
+
+use std::slice::Iter as SliceIter;
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
+pub enum DocAtom {
+ /// eg. `#[doc(hidden)]`
+ Flag(SmolStr),
+ /// eg. `#[doc(alias = "x")]`
+ ///
+ /// Note that a key can have multiple values that are all considered "active" at the same time.
+ /// For example, `#[doc(alias = "x")]` and `#[doc(alias = "y")]`.
+ KeyValue { key: SmolStr, value: SmolStr },
+}
+
+// Adapted from `CfgExpr` parsing code
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+// #[cfg_attr(test, derive(derive_arbitrary::Arbitrary))]
+pub enum DocExpr {
+ Invalid,
+ /// eg. `#[doc(hidden)]`, `#[doc(alias = "x")]`
+ Atom(DocAtom),
+ /// eg. `#[doc(alias("x", "y"))]`
+ Alias(Vec<SmolStr>),
+}
+
+impl From<DocAtom> for DocExpr {
+ fn from(atom: DocAtom) -> Self {
+ DocExpr::Atom(atom)
+ }
+}
+
+impl DocExpr {
+ fn parse<S>(tt: &tt::Subtree<S>) -> DocExpr {
+ next_doc_expr(&mut tt.token_trees.iter()).unwrap_or(DocExpr::Invalid)
+ }
+
+ pub fn aliases(&self) -> &[SmolStr] {
+ match self {
+ DocExpr::Atom(DocAtom::KeyValue { key, value }) if key == "alias" => {
+ std::slice::from_ref(value)
+ }
+ DocExpr::Alias(aliases) => aliases,
+ _ => &[],
+ }
+ }
+}
+
+fn next_doc_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<DocExpr> {
+ let name = match it.next() {
+ None => return None,
+ Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.text.clone(),
+ Some(_) => return Some(DocExpr::Invalid),
+ };
+
+ // Peek
+ let ret = match it.as_slice().first() {
+ Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => {
+ match it.as_slice().get(1) {
+ Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => {
+ it.next();
+ it.next();
+ // FIXME: escape? raw string?
+ let value =
+ SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"'));
+ DocAtom::KeyValue { key: name, value }.into()
+ }
+ _ => return Some(DocExpr::Invalid),
+ }
+ }
+ Some(tt::TokenTree::Subtree(subtree)) => {
+ it.next();
+ let subs = parse_comma_sep(subtree);
+ match name.as_str() {
+ "alias" => DocExpr::Alias(subs),
+ _ => DocExpr::Invalid,
+ }
+ }
+ _ => DocAtom::Flag(name).into(),
+ };
+
+ // Eat comma separator
+ if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = it.as_slice().first() {
+ if punct.char == ',' {
+ it.next();
+ }
+ }
+ Some(ret)
+}
+
+fn parse_comma_sep<S>(subtree: &tt::Subtree<S>) -> Vec<SmolStr> {
+ subtree
+ .token_trees
+ .iter()
+ .filter_map(|tt| match tt {
+ tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
+ // FIXME: escape? raw string?
+ Some(SmolStr::new(lit.text.trim_start_matches('"').trim_end_matches('"')))
+ }
+ _ => None,
+ })
+ .collect()
}
impl AttrsWithOwner {
- pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Self {
+ pub(crate) fn attrs_with_owner(db: &dyn DefDatabase, owner: AttrDefId) -> Self {
+ Self { attrs: db.attrs(owner), owner }
+ }
+
+ pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs {
+ let _p = profile::span("attrs_query");
// FIXME: this should use `Trace` to avoid duplication in `source_map` below
let raw_attrs = match def {
AttrDefId::ModuleId(module) => {
@@ -286,31 +418,29 @@ impl AttrsWithOwner {
}
}
AttrDefId::FieldId(it) => {
- return Self { attrs: db.fields_attrs(it.parent)[it.local_id].clone(), owner: def };
+ return db.fields_attrs(it.parent)[it.local_id].clone();
}
AttrDefId::EnumVariantId(it) => {
- return Self {
- attrs: db.variants_attrs(it.parent)[it.local_id].clone(),
- owner: def,
- };
+ return db.variants_attrs(it.parent)[it.local_id].clone();
}
+ // FIXME: DRY this up
AttrDefId::AdtId(it) => match it {
- AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AdtId::EnumId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+ AdtId::StructId(it) => attrs_from_item_tree_loc(db, it),
+ AdtId::EnumId(it) => attrs_from_item_tree_loc(db, it),
+ AdtId::UnionId(it) => attrs_from_item_tree_loc(db, it),
},
- AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AttrDefId::TraitAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+ AttrDefId::TraitId(it) => attrs_from_item_tree_loc(db, it),
+ AttrDefId::TraitAliasId(it) => attrs_from_item_tree_loc(db, it),
AttrDefId::MacroId(it) => match it {
- MacroId::Macro2Id(it) => attrs_from_item_tree(it.lookup(db).id, db),
- MacroId::MacroRulesId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- MacroId::ProcMacroId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+ MacroId::Macro2Id(it) => attrs_from_item_tree(db, it.lookup(db).id),
+ MacroId::MacroRulesId(it) => attrs_from_item_tree(db, it.lookup(db).id),
+ MacroId::ProcMacroId(it) => attrs_from_item_tree(db, it.lookup(db).id),
},
- AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+ AttrDefId::ImplId(it) => attrs_from_item_tree_loc(db, it),
+ AttrDefId::ConstId(it) => attrs_from_item_tree_assoc(db, it),
+ AttrDefId::StaticId(it) => attrs_from_item_tree_assoc(db, it),
+ AttrDefId::FunctionId(it) => attrs_from_item_tree_assoc(db, it),
+ AttrDefId::TypeAliasId(it) => attrs_from_item_tree_assoc(db, it),
AttrDefId::GenericParamId(it) => match it {
GenericParamId::ConstParamId(it) => {
let src = it.parent().child_source(db);
@@ -331,11 +461,11 @@ impl AttrsWithOwner {
RawAttrs::from_attrs_owner(db.upcast(), src.with_value(&src.value[it.local_id]))
}
},
- AttrDefId::ExternBlockId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+ AttrDefId::ExternBlockId(it) => attrs_from_item_tree_loc(db, it),
};
let attrs = raw_attrs.filter(db.upcast(), def.krate(db));
- Self { attrs: Attrs(attrs), owner: def }
+ Attrs(attrs)
}
pub fn source_map(&self, db: &dyn DefDatabase) -> AttrSourceMap {
@@ -371,7 +501,7 @@ impl AttrsWithOwner {
AttrDefId::FieldId(id) => {
let map = db.fields_attrs_source_map(id.parent);
let file_id = id.parent.file_id(db);
- let root = db.parse_or_expand(file_id).unwrap();
+ let root = db.parse_or_expand(file_id);
let owner = match &map[id.local_id] {
Either::Left(it) => ast::AnyHasAttrs::new(it.to_node(&root)),
Either::Right(it) => ast::AnyHasAttrs::new(it.to_node(&root)),
@@ -379,28 +509,28 @@ impl AttrsWithOwner {
InFile::new(file_id, owner)
}
AttrDefId::AdtId(adt) => match adt {
- AdtId::StructId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AdtId::UnionId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AdtId::EnumId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ AdtId::StructId(id) => any_has_attrs(db, id),
+ AdtId::UnionId(id) => any_has_attrs(db, id),
+ AdtId::EnumId(id) => any_has_attrs(db, id),
},
- AttrDefId::FunctionId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ AttrDefId::FunctionId(id) => any_has_attrs(db, id),
AttrDefId::EnumVariantId(id) => {
let map = db.variants_attrs_source_map(id.parent);
let file_id = id.parent.lookup(db).id.file_id();
- let root = db.parse_or_expand(file_id).unwrap();
+ let root = db.parse_or_expand(file_id);
InFile::new(file_id, ast::AnyHasAttrs::new(map[id.local_id].to_node(&root)))
}
- AttrDefId::StaticId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AttrDefId::TraitAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AttrDefId::TypeAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ AttrDefId::StaticId(id) => any_has_attrs(db, id),
+ AttrDefId::ConstId(id) => any_has_attrs(db, id),
+ AttrDefId::TraitId(id) => any_has_attrs(db, id),
+ AttrDefId::TraitAliasId(id) => any_has_attrs(db, id),
+ AttrDefId::TypeAliasId(id) => any_has_attrs(db, id),
AttrDefId::MacroId(id) => match id {
- MacroId::Macro2Id(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- MacroId::MacroRulesId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- MacroId::ProcMacroId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ MacroId::Macro2Id(id) => any_has_attrs(db, id),
+ MacroId::MacroRulesId(id) => any_has_attrs(db, id),
+ MacroId::ProcMacroId(id) => any_has_attrs(db, id),
},
- AttrDefId::ImplId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ AttrDefId::ImplId(id) => any_has_attrs(db, id),
AttrDefId::GenericParamId(id) => match id {
GenericParamId::ConstParamId(id) => id
.parent()
@@ -415,7 +545,7 @@ impl AttrsWithOwner {
.child_source(db)
.map(|source| ast::AnyHasAttrs::new(source[id.local_id].clone())),
},
- AttrDefId::ExternBlockId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ AttrDefId::ExternBlockId(id) => any_has_attrs(db, id),
};
AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn HasAttrs))
@@ -635,19 +765,42 @@ impl<'attr> AttrQuery<'attr> {
.nth(2);
match name {
- Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ref text, ..}))) => Some(text),
+ Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ ref text, ..}))) => Some(text),
_ => None
}
})
}
}
-fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> RawAttrs {
+fn any_has_attrs(
+ db: &dyn DefDatabase,
+ id: impl Lookup<Data = impl HasSource<Value = impl ast::HasAttrs>>,
+) -> InFile<ast::AnyHasAttrs> {
+ id.lookup(db).source(db).map(ast::AnyHasAttrs::new)
+}
+
+fn attrs_from_item_tree<N: ItemTreeNode>(db: &dyn DefDatabase, id: ItemTreeId<N>) -> RawAttrs {
let tree = id.item_tree(db);
let mod_item = N::id_to_mod_item(id.value);
tree.raw_attrs(mod_item.into()).clone()
}
+fn attrs_from_item_tree_loc<N: ItemTreeNode>(
+ db: &dyn DefDatabase,
+ lookup: impl Lookup<Data = ItemLoc<N>>,
+) -> RawAttrs {
+ let id = lookup.lookup(db).id;
+ attrs_from_item_tree(db, id)
+}
+
+fn attrs_from_item_tree_assoc<N: ItemTreeNode>(
+ db: &dyn DefDatabase,
+ lookup: impl Lookup<Data = AssocItemLoc<N>>,
+) -> RawAttrs {
+ let id = lookup.lookup(db).id;
+ attrs_from_item_tree(db, id)
+}
+
pub(crate) fn variants_attrs_source_map(
db: &dyn DefDatabase,
def: EnumId,