From 4f07d8dd587c24bca8622ee8c39e5a1e156825b4 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 18 Dec 2020 00:23:46 +0100 Subject: Refactor attributes API to allow handling cfg_attr --- crates/hir_def/src/attr.rs | 146 +++++++++++++++++++++++++++++---------------- 1 file changed, 93 insertions(+), 53 deletions(-) (limited to 'crates/hir_def/src/attr.rs') diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index 45313f335..9cd0b72aa 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs @@ -2,6 +2,7 @@ use std::{ops, sync::Arc}; +use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; use either::Either; use hir_expand::{hygiene::Hygiene, AstId, InFile}; @@ -38,12 +39,16 @@ impl From for String { } } +/// Syntactical attributes, without filtering of `cfg_attr`s. #[derive(Default, Debug, Clone, PartialEq, Eq)] -pub struct Attrs { +pub struct RawAttrs { entries: Option>, } -impl ops::Deref for Attrs { +#[derive(Default, Debug, Clone, PartialEq, Eq)] +pub struct Attrs(RawAttrs); + +impl ops::Deref for RawAttrs { type Target = [Attr]; fn deref(&self) -> &[Attr] { @@ -54,19 +59,88 @@ impl ops::Deref for Attrs { } } +impl ops::Deref for Attrs { + type Target = [Attr]; + + fn deref(&self) -> &[Attr] { + match &self.0.entries { + Some(it) => &*it, + None => &[], + } + } +} + +impl RawAttrs { + pub const EMPTY: Self = Self { entries: None }; + + pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Self { + let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) + .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs)))); + + let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none()); + let attrs = outer_attrs + .chain(inner_attrs.into_iter().flatten()) + .map(|attr| (attr.syntax().text_range().start(), Attr::from_src(attr, hygiene))); + + let outer_docs = + ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer); + let docs = outer_docs.chain(inner_docs.into_iter().flatten()).map(|docs_text| { + ( + docs_text.syntax().text_range().start(), + docs_text.doc_comment().map(|doc| Attr { + input: Some(AttrInput::Literal(SmolStr::new(doc))), + path: ModPath::from(hir_expand::name!(doc)), + }), + ) + }); + // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved + let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect(); + let entries = if attrs.is_empty() { + // Avoid heap allocation + None + } else { + Some(attrs.into_iter().flat_map(|(_, attr)| attr).collect()) + }; + Self { entries } + } + + fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Self { + let hygiene = Hygiene::new(db.upcast(), owner.file_id); + Self::new(owner.value, &hygiene) + } + + pub(crate) fn merge(&self, other: Self) -> Self { + match (&self.entries, &other.entries) { + (None, None) => Self::EMPTY, + (Some(entries), None) | (None, Some(entries)) => { + Self { entries: Some(entries.clone()) } + } + (Some(a), Some(b)) => { + Self { entries: Some(a.iter().chain(b.iter()).cloned().collect()) } + } + } + } + + /// Processes `cfg_attr`s, returning the resulting semantic `Attrs`. + pub(crate) fn filter(self, _db: &dyn DefDatabase, _krate: CrateId) -> Attrs { + // FIXME actually implement this + Attrs(self) + } +} + impl Attrs { - pub const EMPTY: Attrs = Attrs { entries: None }; + pub const EMPTY: Self = Self(RawAttrs::EMPTY); pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs { - match def { + let raw_attrs = match def { AttrDefId::ModuleId(module) => { let def_map = db.crate_def_map(module.krate); let mod_data = &def_map[module.local_id]; match mod_data.declaration_source(db) { Some(it) => { - Attrs::from_attrs_owner(db, it.as_ref().map(|it| it as &dyn AttrsOwner)) + RawAttrs::from_attrs_owner(db, it.as_ref().map(|it| it as &dyn AttrsOwner)) } - None => Attrs::from_attrs_owner( + None => RawAttrs::from_attrs_owner( db, mod_data.definition_source(db).as_ref().map(|src| match src { ModuleSource::SourceFile(file) => file as &dyn AttrsOwner, @@ -78,14 +152,14 @@ impl Attrs { AttrDefId::FieldId(it) => { let src = it.parent.child_source(db); match &src.value[it.local_id] { - Either::Left(_tuple) => Attrs::default(), - Either::Right(record) => Attrs::from_attrs_owner(db, src.with_value(record)), + Either::Left(_tuple) => RawAttrs::default(), + Either::Right(record) => RawAttrs::from_attrs_owner(db, src.with_value(record)), } } AttrDefId::EnumVariantId(var_id) => { let src = var_id.parent.child_source(db); let src = src.as_ref().map(|it| &it[var_id.local_id]); - Attrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner)) + RawAttrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner)) } AttrDefId::AdtId(it) => match it { AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db), @@ -101,53 +175,19 @@ impl Attrs { 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), - } - } - - fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Attrs { - let hygiene = Hygiene::new(db.upcast(), owner.file_id); - Attrs::new(owner.value, &hygiene) - } - - pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs { - let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) - .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs)))); - - let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none()); - let attrs = outer_attrs - .chain(inner_attrs.into_iter().flatten()) - .map(|attr| (attr.syntax().text_range().start(), Attr::from_src(attr, hygiene))); - - let outer_docs = - ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer); - let docs = outer_docs.chain(inner_docs.into_iter().flatten()).map(|docs_text| { - ( - docs_text.syntax().text_range().start(), - docs_text.doc_comment().map(|doc| Attr { - input: Some(AttrInput::Literal(SmolStr::new(doc))), - path: ModPath::from(hir_expand::name!(doc)), - }), - ) - }); - // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved - let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect(); - let entries = if attrs.is_empty() { - // Avoid heap allocation - None - } else { - Some(attrs.into_iter().flat_map(|(_, attr)| attr).collect()) }; - Attrs { entries } + + raw_attrs.filter(db, def.krate(db)) } pub fn merge(&self, other: Attrs) -> Attrs { - match (&self.entries, &other.entries) { - (None, None) => Attrs { entries: None }, + match (&self.0.entries, &other.0.entries) { + (None, None) => Attrs::EMPTY, (Some(entries), None) | (None, Some(entries)) => { - Attrs { entries: Some(entries.clone()) } + Attrs(RawAttrs { entries: Some(entries.clone()) }) } (Some(a), Some(b)) => { - Attrs { entries: Some(a.iter().chain(b.iter()).cloned().collect()) } + Attrs(RawAttrs { entries: Some(a.iter().chain(b.iter()).cloned().collect()) }) } } } @@ -291,16 +331,16 @@ impl<'a> AttrQuery<'a> { } } -fn attrs_from_ast(src: AstId, db: &dyn DefDatabase) -> Attrs +fn attrs_from_ast(src: AstId, db: &dyn DefDatabase) -> RawAttrs where N: ast::AttrsOwner, { let src = InFile::new(src.file_id, src.to_node(db.upcast())); - Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner)) + RawAttrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner)) } -fn attrs_from_item_tree(id: ItemTreeId, db: &dyn DefDatabase) -> Attrs { +fn attrs_from_item_tree(id: ItemTreeId, db: &dyn DefDatabase) -> RawAttrs { let tree = db.item_tree(id.file_id); let mod_item = N::id_to_mod_item(id.value); - tree.attrs(mod_item.into()).clone() + tree.raw_attrs(mod_item.into()).clone() } -- cgit v1.2.3