diff options
Diffstat (limited to 'crates/hir_def/src/attr.rs')
-rw-r--r-- | crates/hir_def/src/attr.rs | 146 |
1 files changed, 93 insertions, 53 deletions
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 @@ | |||
2 | 2 | ||
3 | use std::{ops, sync::Arc}; | 3 | use std::{ops, sync::Arc}; |
4 | 4 | ||
5 | use base_db::CrateId; | ||
5 | use cfg::{CfgExpr, CfgOptions}; | 6 | use cfg::{CfgExpr, CfgOptions}; |
6 | use either::Either; | 7 | use either::Either; |
7 | use hir_expand::{hygiene::Hygiene, AstId, InFile}; | 8 | use hir_expand::{hygiene::Hygiene, AstId, InFile}; |
@@ -38,12 +39,16 @@ impl From<Documentation> for String { | |||
38 | } | 39 | } |
39 | } | 40 | } |
40 | 41 | ||
42 | /// Syntactical attributes, without filtering of `cfg_attr`s. | ||
41 | #[derive(Default, Debug, Clone, PartialEq, Eq)] | 43 | #[derive(Default, Debug, Clone, PartialEq, Eq)] |
42 | pub struct Attrs { | 44 | pub struct RawAttrs { |
43 | entries: Option<Arc<[Attr]>>, | 45 | entries: Option<Arc<[Attr]>>, |
44 | } | 46 | } |
45 | 47 | ||
46 | impl ops::Deref for Attrs { | 48 | #[derive(Default, Debug, Clone, PartialEq, Eq)] |
49 | pub struct Attrs(RawAttrs); | ||
50 | |||
51 | impl ops::Deref for RawAttrs { | ||
47 | type Target = [Attr]; | 52 | type Target = [Attr]; |
48 | 53 | ||
49 | fn deref(&self) -> &[Attr] { | 54 | fn deref(&self) -> &[Attr] { |
@@ -54,19 +59,88 @@ impl ops::Deref for Attrs { | |||
54 | } | 59 | } |
55 | } | 60 | } |
56 | 61 | ||
62 | impl ops::Deref for Attrs { | ||
63 | type Target = [Attr]; | ||
64 | |||
65 | fn deref(&self) -> &[Attr] { | ||
66 | match &self.0.entries { | ||
67 | Some(it) => &*it, | ||
68 | None => &[], | ||
69 | } | ||
70 | } | ||
71 | } | ||
72 | |||
73 | impl RawAttrs { | ||
74 | pub const EMPTY: Self = Self { entries: None }; | ||
75 | |||
76 | pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Self { | ||
77 | let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) | ||
78 | .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs)))); | ||
79 | |||
80 | let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none()); | ||
81 | let attrs = outer_attrs | ||
82 | .chain(inner_attrs.into_iter().flatten()) | ||
83 | .map(|attr| (attr.syntax().text_range().start(), Attr::from_src(attr, hygiene))); | ||
84 | |||
85 | let outer_docs = | ||
86 | ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer); | ||
87 | let docs = outer_docs.chain(inner_docs.into_iter().flatten()).map(|docs_text| { | ||
88 | ( | ||
89 | docs_text.syntax().text_range().start(), | ||
90 | docs_text.doc_comment().map(|doc| Attr { | ||
91 | input: Some(AttrInput::Literal(SmolStr::new(doc))), | ||
92 | path: ModPath::from(hir_expand::name!(doc)), | ||
93 | }), | ||
94 | ) | ||
95 | }); | ||
96 | // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved | ||
97 | let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect(); | ||
98 | let entries = if attrs.is_empty() { | ||
99 | // Avoid heap allocation | ||
100 | None | ||
101 | } else { | ||
102 | Some(attrs.into_iter().flat_map(|(_, attr)| attr).collect()) | ||
103 | }; | ||
104 | Self { entries } | ||
105 | } | ||
106 | |||
107 | fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Self { | ||
108 | let hygiene = Hygiene::new(db.upcast(), owner.file_id); | ||
109 | Self::new(owner.value, &hygiene) | ||
110 | } | ||
111 | |||
112 | pub(crate) fn merge(&self, other: Self) -> Self { | ||
113 | match (&self.entries, &other.entries) { | ||
114 | (None, None) => Self::EMPTY, | ||
115 | (Some(entries), None) | (None, Some(entries)) => { | ||
116 | Self { entries: Some(entries.clone()) } | ||
117 | } | ||
118 | (Some(a), Some(b)) => { | ||
119 | Self { entries: Some(a.iter().chain(b.iter()).cloned().collect()) } | ||
120 | } | ||
121 | } | ||
122 | } | ||
123 | |||
124 | /// Processes `cfg_attr`s, returning the resulting semantic `Attrs`. | ||
125 | pub(crate) fn filter(self, _db: &dyn DefDatabase, _krate: CrateId) -> Attrs { | ||
126 | // FIXME actually implement this | ||
127 | Attrs(self) | ||
128 | } | ||
129 | } | ||
130 | |||
57 | impl Attrs { | 131 | impl Attrs { |
58 | pub const EMPTY: Attrs = Attrs { entries: None }; | 132 | pub const EMPTY: Self = Self(RawAttrs::EMPTY); |
59 | 133 | ||
60 | pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs { | 134 | pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs { |
61 | match def { | 135 | let raw_attrs = match def { |
62 | AttrDefId::ModuleId(module) => { | 136 | AttrDefId::ModuleId(module) => { |
63 | let def_map = db.crate_def_map(module.krate); | 137 | let def_map = db.crate_def_map(module.krate); |
64 | let mod_data = &def_map[module.local_id]; | 138 | let mod_data = &def_map[module.local_id]; |
65 | match mod_data.declaration_source(db) { | 139 | match mod_data.declaration_source(db) { |
66 | Some(it) => { | 140 | Some(it) => { |
67 | Attrs::from_attrs_owner(db, it.as_ref().map(|it| it as &dyn AttrsOwner)) | 141 | RawAttrs::from_attrs_owner(db, it.as_ref().map(|it| it as &dyn AttrsOwner)) |
68 | } | 142 | } |
69 | None => Attrs::from_attrs_owner( | 143 | None => RawAttrs::from_attrs_owner( |
70 | db, | 144 | db, |
71 | mod_data.definition_source(db).as_ref().map(|src| match src { | 145 | mod_data.definition_source(db).as_ref().map(|src| match src { |
72 | ModuleSource::SourceFile(file) => file as &dyn AttrsOwner, | 146 | ModuleSource::SourceFile(file) => file as &dyn AttrsOwner, |
@@ -78,14 +152,14 @@ impl Attrs { | |||
78 | AttrDefId::FieldId(it) => { | 152 | AttrDefId::FieldId(it) => { |
79 | let src = it.parent.child_source(db); | 153 | let src = it.parent.child_source(db); |
80 | match &src.value[it.local_id] { | 154 | match &src.value[it.local_id] { |
81 | Either::Left(_tuple) => Attrs::default(), | 155 | Either::Left(_tuple) => RawAttrs::default(), |
82 | Either::Right(record) => Attrs::from_attrs_owner(db, src.with_value(record)), | 156 | Either::Right(record) => RawAttrs::from_attrs_owner(db, src.with_value(record)), |
83 | } | 157 | } |
84 | } | 158 | } |
85 | AttrDefId::EnumVariantId(var_id) => { | 159 | AttrDefId::EnumVariantId(var_id) => { |
86 | let src = var_id.parent.child_source(db); | 160 | let src = var_id.parent.child_source(db); |
87 | let src = src.as_ref().map(|it| &it[var_id.local_id]); | 161 | let src = src.as_ref().map(|it| &it[var_id.local_id]); |
88 | Attrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner)) | 162 | RawAttrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner)) |
89 | } | 163 | } |
90 | AttrDefId::AdtId(it) => match it { | 164 | AttrDefId::AdtId(it) => match it { |
91 | AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db), | 165 | AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
@@ -101,53 +175,19 @@ impl Attrs { | |||
101 | AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db), | 175 | AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
102 | AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db), | 176 | AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
103 | AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db), | 177 | AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
104 | } | ||
105 | } | ||
106 | |||
107 | fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Attrs { | ||
108 | let hygiene = Hygiene::new(db.upcast(), owner.file_id); | ||
109 | Attrs::new(owner.value, &hygiene) | ||
110 | } | ||
111 | |||
112 | pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs { | ||
113 | let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) | ||
114 | .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs)))); | ||
115 | |||
116 | let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none()); | ||
117 | let attrs = outer_attrs | ||
118 | .chain(inner_attrs.into_iter().flatten()) | ||
119 | .map(|attr| (attr.syntax().text_range().start(), Attr::from_src(attr, hygiene))); | ||
120 | |||
121 | let outer_docs = | ||
122 | ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer); | ||
123 | let docs = outer_docs.chain(inner_docs.into_iter().flatten()).map(|docs_text| { | ||
124 | ( | ||
125 | docs_text.syntax().text_range().start(), | ||
126 | docs_text.doc_comment().map(|doc| Attr { | ||
127 | input: Some(AttrInput::Literal(SmolStr::new(doc))), | ||
128 | path: ModPath::from(hir_expand::name!(doc)), | ||
129 | }), | ||
130 | ) | ||
131 | }); | ||
132 | // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved | ||
133 | let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect(); | ||
134 | let entries = if attrs.is_empty() { | ||
135 | // Avoid heap allocation | ||
136 | None | ||
137 | } else { | ||
138 | Some(attrs.into_iter().flat_map(|(_, attr)| attr).collect()) | ||
139 | }; | 178 | }; |
140 | Attrs { entries } | 179 | |
180 | raw_attrs.filter(db, def.krate(db)) | ||
141 | } | 181 | } |
142 | 182 | ||
143 | pub fn merge(&self, other: Attrs) -> Attrs { | 183 | pub fn merge(&self, other: Attrs) -> Attrs { |
144 | match (&self.entries, &other.entries) { | 184 | match (&self.0.entries, &other.0.entries) { |
145 | (None, None) => Attrs { entries: None }, | 185 | (None, None) => Attrs::EMPTY, |
146 | (Some(entries), None) | (None, Some(entries)) => { | 186 | (Some(entries), None) | (None, Some(entries)) => { |
147 | Attrs { entries: Some(entries.clone()) } | 187 | Attrs(RawAttrs { entries: Some(entries.clone()) }) |
148 | } | 188 | } |
149 | (Some(a), Some(b)) => { | 189 | (Some(a), Some(b)) => { |
150 | Attrs { entries: Some(a.iter().chain(b.iter()).cloned().collect()) } | 190 | Attrs(RawAttrs { entries: Some(a.iter().chain(b.iter()).cloned().collect()) }) |
151 | } | 191 | } |
152 | } | 192 | } |
153 | } | 193 | } |
@@ -291,16 +331,16 @@ impl<'a> AttrQuery<'a> { | |||
291 | } | 331 | } |
292 | } | 332 | } |
293 | 333 | ||
294 | fn attrs_from_ast<N>(src: AstId<N>, db: &dyn DefDatabase) -> Attrs | 334 | fn attrs_from_ast<N>(src: AstId<N>, db: &dyn DefDatabase) -> RawAttrs |
295 | where | 335 | where |
296 | N: ast::AttrsOwner, | 336 | N: ast::AttrsOwner, |
297 | { | 337 | { |
298 | let src = InFile::new(src.file_id, src.to_node(db.upcast())); | 338 | let src = InFile::new(src.file_id, src.to_node(db.upcast())); |
299 | Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner)) | 339 | RawAttrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner)) |
300 | } | 340 | } |
301 | 341 | ||
302 | fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> Attrs { | 342 | fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> RawAttrs { |
303 | let tree = db.item_tree(id.file_id); | 343 | let tree = db.item_tree(id.file_id); |
304 | let mod_item = N::id_to_mod_item(id.value); | 344 | let mod_item = N::id_to_mod_item(id.value); |
305 | tree.attrs(mod_item.into()).clone() | 345 | tree.raw_attrs(mod_item.into()).clone() |
306 | } | 346 | } |