diff options
Diffstat (limited to 'crates/hir_def/src/attr.rs')
-rw-r--r-- | crates/hir_def/src/attr.rs | 289 |
1 files changed, 194 insertions, 95 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index e4c84afbf..52a2bce9b 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -21,7 +21,7 @@ use crate::{ | |||
21 | item_tree::{ItemTreeId, ItemTreeNode}, | 21 | item_tree::{ItemTreeId, ItemTreeNode}, |
22 | nameres::ModuleSource, | 22 | nameres::ModuleSource, |
23 | path::{ModPath, PathKind}, | 23 | path::{ModPath, PathKind}, |
24 | src::HasChildSource, | 24 | src::{HasChildSource, HasSource}, |
25 | AdtId, AttrDefId, EnumId, GenericParamId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup, | 25 | AdtId, AttrDefId, EnumId, GenericParamId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup, |
26 | VariantId, | 26 | VariantId, |
27 | }; | 27 | }; |
@@ -51,6 +51,12 @@ pub(crate) struct RawAttrs { | |||
51 | #[derive(Default, Debug, Clone, PartialEq, Eq)] | 51 | #[derive(Default, Debug, Clone, PartialEq, Eq)] |
52 | pub struct Attrs(RawAttrs); | 52 | pub struct Attrs(RawAttrs); |
53 | 53 | ||
54 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
55 | pub struct AttrsWithOwner { | ||
56 | attrs: Attrs, | ||
57 | owner: AttrDefId, | ||
58 | } | ||
59 | |||
54 | impl ops::Deref for RawAttrs { | 60 | impl ops::Deref for RawAttrs { |
55 | type Target = [Attr]; | 61 | type Target = [Attr]; |
56 | 62 | ||
@@ -73,6 +79,14 @@ impl ops::Deref for Attrs { | |||
73 | } | 79 | } |
74 | } | 80 | } |
75 | 81 | ||
82 | impl ops::Deref for AttrsWithOwner { | ||
83 | type Target = Attrs; | ||
84 | |||
85 | fn deref(&self) -> &Attrs { | ||
86 | &self.attrs | ||
87 | } | ||
88 | } | ||
89 | |||
76 | impl RawAttrs { | 90 | impl RawAttrs { |
77 | pub(crate) const EMPTY: Self = Self { entries: None }; | 91 | pub(crate) const EMPTY: Self = Self { entries: None }; |
78 | 92 | ||
@@ -169,77 +183,6 @@ impl RawAttrs { | |||
169 | impl Attrs { | 183 | impl Attrs { |
170 | pub const EMPTY: Self = Self(RawAttrs::EMPTY); | 184 | pub const EMPTY: Self = Self(RawAttrs::EMPTY); |
171 | 185 | ||
172 | pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs { | ||
173 | let raw_attrs = match def { | ||
174 | AttrDefId::ModuleId(module) => { | ||
175 | let def_map = module.def_map(db); | ||
176 | let mod_data = &def_map[module.local_id]; | ||
177 | match mod_data.declaration_source(db) { | ||
178 | Some(it) => { | ||
179 | let raw_attrs = RawAttrs::from_attrs_owner( | ||
180 | db, | ||
181 | it.as_ref().map(|it| it as &dyn ast::AttrsOwner), | ||
182 | ); | ||
183 | match mod_data.definition_source(db) { | ||
184 | InFile { file_id, value: ModuleSource::SourceFile(file) } => raw_attrs | ||
185 | .merge(RawAttrs::from_attrs_owner(db, InFile::new(file_id, &file))), | ||
186 | _ => raw_attrs, | ||
187 | } | ||
188 | } | ||
189 | None => RawAttrs::from_attrs_owner( | ||
190 | db, | ||
191 | mod_data.definition_source(db).as_ref().map(|src| match src { | ||
192 | ModuleSource::SourceFile(file) => file as &dyn ast::AttrsOwner, | ||
193 | ModuleSource::Module(module) => module as &dyn ast::AttrsOwner, | ||
194 | ModuleSource::BlockExpr(block) => block as &dyn ast::AttrsOwner, | ||
195 | }), | ||
196 | ), | ||
197 | } | ||
198 | } | ||
199 | AttrDefId::FieldId(it) => { | ||
200 | return db.fields_attrs(it.parent)[it.local_id].clone(); | ||
201 | } | ||
202 | AttrDefId::EnumVariantId(it) => { | ||
203 | return db.variants_attrs(it.parent)[it.local_id].clone(); | ||
204 | } | ||
205 | AttrDefId::AdtId(it) => match it { | ||
206 | AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
207 | AdtId::EnumId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
208 | AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
209 | }, | ||
210 | AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
211 | AttrDefId::MacroDefId(it) => { | ||
212 | it.ast_id.map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db)) | ||
213 | } | ||
214 | AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
215 | AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
216 | AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
217 | AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
218 | AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
219 | AttrDefId::GenericParamId(it) => match it { | ||
220 | GenericParamId::TypeParamId(it) => { | ||
221 | let src = it.parent.child_source(db); | ||
222 | RawAttrs::from_attrs_owner( | ||
223 | db, | ||
224 | src.with_value( | ||
225 | src.value[it.local_id].as_ref().either(|it| it as _, |it| it as _), | ||
226 | ), | ||
227 | ) | ||
228 | } | ||
229 | GenericParamId::LifetimeParamId(it) => { | ||
230 | let src = it.parent.child_source(db); | ||
231 | RawAttrs::from_attrs_owner(db, src.with_value(&src.value[it.local_id])) | ||
232 | } | ||
233 | GenericParamId::ConstParamId(it) => { | ||
234 | let src = it.parent.child_source(db); | ||
235 | RawAttrs::from_attrs_owner(db, src.with_value(&src.value[it.local_id])) | ||
236 | } | ||
237 | }, | ||
238 | }; | ||
239 | |||
240 | raw_attrs.filter(db, def.krate(db)) | ||
241 | } | ||
242 | |||
243 | pub(crate) fn variants_attrs_query( | 186 | pub(crate) fn variants_attrs_query( |
244 | db: &dyn DefDatabase, | 187 | db: &dyn DefDatabase, |
245 | e: EnumId, | 188 | e: EnumId, |
@@ -280,13 +223,6 @@ impl Attrs { | |||
280 | Arc::new(res) | 223 | Arc::new(res) |
281 | } | 224 | } |
282 | 225 | ||
283 | /// Constructs a map that maps the lowered `Attr`s in this `Attrs` back to its original syntax nodes. | ||
284 | /// | ||
285 | /// `owner` must be the original owner of the attributes. | ||
286 | pub fn source_map(&self, owner: &dyn ast::AttrsOwner) -> AttrSourceMap { | ||
287 | AttrSourceMap { attrs: collect_attrs(owner).collect() } | ||
288 | } | ||
289 | |||
290 | pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { | 226 | pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { |
291 | AttrQuery { attrs: self, key } | 227 | AttrQuery { attrs: self, key } |
292 | } | 228 | } |
@@ -343,6 +279,180 @@ impl Attrs { | |||
343 | } | 279 | } |
344 | } | 280 | } |
345 | 281 | ||
282 | impl AttrsWithOwner { | ||
283 | pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Self { | ||
284 | // FIXME: this should use `Trace` to avoid duplication in `source_map` below | ||
285 | let raw_attrs = match def { | ||
286 | AttrDefId::ModuleId(module) => { | ||
287 | let def_map = module.def_map(db); | ||
288 | let mod_data = &def_map[module.local_id]; | ||
289 | match mod_data.declaration_source(db) { | ||
290 | Some(it) => { | ||
291 | let raw_attrs = RawAttrs::from_attrs_owner( | ||
292 | db, | ||
293 | it.as_ref().map(|it| it as &dyn ast::AttrsOwner), | ||
294 | ); | ||
295 | match mod_data.definition_source(db) { | ||
296 | InFile { file_id, value: ModuleSource::SourceFile(file) } => raw_attrs | ||
297 | .merge(RawAttrs::from_attrs_owner(db, InFile::new(file_id, &file))), | ||
298 | _ => raw_attrs, | ||
299 | } | ||
300 | } | ||
301 | None => RawAttrs::from_attrs_owner( | ||
302 | db, | ||
303 | mod_data.definition_source(db).as_ref().map(|src| match src { | ||
304 | ModuleSource::SourceFile(file) => file as &dyn ast::AttrsOwner, | ||
305 | ModuleSource::Module(module) => module as &dyn ast::AttrsOwner, | ||
306 | ModuleSource::BlockExpr(block) => block as &dyn ast::AttrsOwner, | ||
307 | }), | ||
308 | ), | ||
309 | } | ||
310 | } | ||
311 | AttrDefId::FieldId(it) => { | ||
312 | return Self { attrs: db.fields_attrs(it.parent)[it.local_id].clone(), owner: def }; | ||
313 | } | ||
314 | AttrDefId::EnumVariantId(it) => { | ||
315 | return Self { | ||
316 | attrs: db.variants_attrs(it.parent)[it.local_id].clone(), | ||
317 | owner: def, | ||
318 | }; | ||
319 | } | ||
320 | AttrDefId::AdtId(it) => match it { | ||
321 | AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
322 | AdtId::EnumId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
323 | AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
324 | }, | ||
325 | AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
326 | AttrDefId::MacroDefId(it) => it | ||
327 | .ast_id() | ||
328 | .left() | ||
329 | .map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db)), | ||
330 | AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
331 | AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
332 | AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
333 | AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
334 | AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db), | ||
335 | AttrDefId::GenericParamId(it) => match it { | ||
336 | GenericParamId::TypeParamId(it) => { | ||
337 | let src = it.parent.child_source(db); | ||
338 | RawAttrs::from_attrs_owner( | ||
339 | db, | ||
340 | src.with_value( | ||
341 | src.value[it.local_id].as_ref().either(|it| it as _, |it| it as _), | ||
342 | ), | ||
343 | ) | ||
344 | } | ||
345 | GenericParamId::LifetimeParamId(it) => { | ||
346 | let src = it.parent.child_source(db); | ||
347 | RawAttrs::from_attrs_owner(db, src.with_value(&src.value[it.local_id])) | ||
348 | } | ||
349 | GenericParamId::ConstParamId(it) => { | ||
350 | let src = it.parent.child_source(db); | ||
351 | RawAttrs::from_attrs_owner(db, src.with_value(&src.value[it.local_id])) | ||
352 | } | ||
353 | }, | ||
354 | }; | ||
355 | |||
356 | let attrs = raw_attrs.filter(db, def.krate(db)); | ||
357 | Self { attrs, owner: def } | ||
358 | } | ||
359 | |||
360 | pub fn source_map(&self, db: &dyn DefDatabase) -> AttrSourceMap { | ||
361 | let owner = match self.owner { | ||
362 | AttrDefId::ModuleId(module) => { | ||
363 | // Modules can have 2 attribute owners (the `mod x;` item, and the module file itself). | ||
364 | |||
365 | let def_map = module.def_map(db); | ||
366 | let mod_data = &def_map[module.local_id]; | ||
367 | let attrs = match mod_data.declaration_source(db) { | ||
368 | Some(it) => { | ||
369 | let mut attrs: Vec<_> = collect_attrs(&it.value as &dyn ast::AttrsOwner) | ||
370 | .map(|attr| InFile::new(it.file_id, attr)) | ||
371 | .collect(); | ||
372 | if let InFile { file_id, value: ModuleSource::SourceFile(file) } = | ||
373 | mod_data.definition_source(db) | ||
374 | { | ||
375 | attrs.extend( | ||
376 | collect_attrs(&file as &dyn ast::AttrsOwner) | ||
377 | .map(|attr| InFile::new(file_id, attr)), | ||
378 | ) | ||
379 | } | ||
380 | attrs | ||
381 | } | ||
382 | None => { | ||
383 | let InFile { file_id, value } = mod_data.definition_source(db); | ||
384 | match &value { | ||
385 | ModuleSource::SourceFile(file) => { | ||
386 | collect_attrs(file as &dyn ast::AttrsOwner) | ||
387 | } | ||
388 | ModuleSource::Module(module) => { | ||
389 | collect_attrs(module as &dyn ast::AttrsOwner) | ||
390 | } | ||
391 | ModuleSource::BlockExpr(block) => { | ||
392 | collect_attrs(block as &dyn ast::AttrsOwner) | ||
393 | } | ||
394 | } | ||
395 | .map(|attr| InFile::new(file_id, attr)) | ||
396 | .collect() | ||
397 | } | ||
398 | }; | ||
399 | return AttrSourceMap { attrs }; | ||
400 | } | ||
401 | AttrDefId::FieldId(id) => { | ||
402 | id.parent.child_source(db).map(|source| match &source[id.local_id] { | ||
403 | Either::Left(field) => ast::AttrsOwnerNode::new(field.clone()), | ||
404 | Either::Right(field) => ast::AttrsOwnerNode::new(field.clone()), | ||
405 | }) | ||
406 | } | ||
407 | AttrDefId::AdtId(adt) => match adt { | ||
408 | AdtId::StructId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), | ||
409 | AdtId::UnionId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), | ||
410 | AdtId::EnumId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), | ||
411 | }, | ||
412 | AttrDefId::FunctionId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), | ||
413 | AttrDefId::EnumVariantId(id) => id | ||
414 | .parent | ||
415 | .child_source(db) | ||
416 | .map(|source| ast::AttrsOwnerNode::new(source[id.local_id].clone())), | ||
417 | AttrDefId::StaticId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), | ||
418 | AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), | ||
419 | AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), | ||
420 | AttrDefId::TypeAliasId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), | ||
421 | AttrDefId::MacroDefId(id) => match id.ast_id() { | ||
422 | Either::Left(it) => { | ||
423 | it.with_value(ast::AttrsOwnerNode::new(it.to_node(db.upcast()))) | ||
424 | } | ||
425 | Either::Right(it) => { | ||
426 | it.with_value(ast::AttrsOwnerNode::new(it.to_node(db.upcast()))) | ||
427 | } | ||
428 | }, | ||
429 | AttrDefId::ImplId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), | ||
430 | AttrDefId::GenericParamId(id) => match id { | ||
431 | GenericParamId::TypeParamId(id) => { | ||
432 | id.parent.child_source(db).map(|source| match &source[id.local_id] { | ||
433 | Either::Left(id) => ast::AttrsOwnerNode::new(id.clone()), | ||
434 | Either::Right(id) => ast::AttrsOwnerNode::new(id.clone()), | ||
435 | }) | ||
436 | } | ||
437 | GenericParamId::LifetimeParamId(id) => id | ||
438 | .parent | ||
439 | .child_source(db) | ||
440 | .map(|source| ast::AttrsOwnerNode::new(source[id.local_id].clone())), | ||
441 | GenericParamId::ConstParamId(id) => id | ||
442 | .parent | ||
443 | .child_source(db) | ||
444 | .map(|source| ast::AttrsOwnerNode::new(source[id.local_id].clone())), | ||
445 | }, | ||
446 | }; | ||
447 | |||
448 | AttrSourceMap { | ||
449 | attrs: collect_attrs(&owner.value) | ||
450 | .map(|attr| InFile::new(owner.file_id, attr)) | ||
451 | .collect(), | ||
452 | } | ||
453 | } | ||
454 | } | ||
455 | |||
346 | fn inner_attributes( | 456 | fn inner_attributes( |
347 | syntax: &SyntaxNode, | 457 | syntax: &SyntaxNode, |
348 | ) -> Option<(impl Iterator<Item = ast::Attr>, impl Iterator<Item = ast::Comment>)> { | 458 | ) -> Option<(impl Iterator<Item = ast::Attr>, impl Iterator<Item = ast::Comment>)> { |
@@ -379,7 +489,7 @@ fn inner_attributes( | |||
379 | } | 489 | } |
380 | 490 | ||
381 | pub struct AttrSourceMap { | 491 | pub struct AttrSourceMap { |
382 | attrs: Vec<Either<ast::Attr, ast::Comment>>, | 492 | attrs: Vec<InFile<Either<ast::Attr, ast::Comment>>>, |
383 | } | 493 | } |
384 | 494 | ||
385 | impl AttrSourceMap { | 495 | impl AttrSourceMap { |
@@ -389,10 +499,11 @@ impl AttrSourceMap { | |||
389 | /// | 499 | /// |
390 | /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of | 500 | /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of |
391 | /// the attribute represented by `Attr`. | 501 | /// the attribute represented by `Attr`. |
392 | pub fn source_of(&self, attr: &Attr) -> &Either<ast::Attr, ast::Comment> { | 502 | pub fn source_of(&self, attr: &Attr) -> InFile<&Either<ast::Attr, ast::Comment>> { |
393 | self.attrs | 503 | self.attrs |
394 | .get(attr.index as usize) | 504 | .get(attr.index as usize) |
395 | .unwrap_or_else(|| panic!("cannot find `Attr` at index {}", attr.index)) | 505 | .unwrap_or_else(|| panic!("cannot find `Attr` at index {}", attr.index)) |
506 | .as_ref() | ||
396 | } | 507 | } |
397 | } | 508 | } |
398 | 509 | ||
@@ -414,7 +525,7 @@ pub enum AttrInput { | |||
414 | impl Attr { | 525 | impl Attr { |
415 | fn from_src(ast: ast::Attr, hygiene: &Hygiene, index: u32) -> Option<Attr> { | 526 | fn from_src(ast: ast::Attr, hygiene: &Hygiene, index: u32) -> Option<Attr> { |
416 | let path = ModPath::from_src(ast.path()?, hygiene)?; | 527 | let path = ModPath::from_src(ast.path()?, hygiene)?; |
417 | let input = if let Some(lit) = ast.literal() { | 528 | let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() { |
418 | let value = match lit.kind() { | 529 | let value = match lit.kind() { |
419 | ast::LiteralKind::String(string) => string.value()?.into(), | 530 | ast::LiteralKind::String(string) => string.value()?.into(), |
420 | _ => lit.syntax().first_token()?.text().trim_matches('"').into(), | 531 | _ => lit.syntax().first_token()?.text().trim_matches('"').into(), |
@@ -428,18 +539,6 @@ impl Attr { | |||
428 | Some(Attr { index, path, input }) | 539 | Some(Attr { index, path, input }) |
429 | } | 540 | } |
430 | 541 | ||
431 | /// Maps this lowered `Attr` back to its original syntax node. | ||
432 | /// | ||
433 | /// `owner` must be the original owner of the attribute. | ||
434 | /// | ||
435 | /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of | ||
436 | /// the attribute represented by `Attr`. | ||
437 | pub fn to_src(&self, owner: &dyn ast::AttrsOwner) -> Either<ast::Attr, ast::Comment> { | ||
438 | collect_attrs(owner).nth(self.index as usize).unwrap_or_else(|| { | ||
439 | panic!("cannot find `Attr` at index {} in {}", self.index, owner.syntax()) | ||
440 | }) | ||
441 | } | ||
442 | |||
443 | /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths | 542 | /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths |
444 | /// to derive macros. | 543 | /// to derive macros. |
445 | /// | 544 | /// |
@@ -539,7 +638,7 @@ fn collect_attrs( | |||
539 | owner: &dyn ast::AttrsOwner, | 638 | owner: &dyn ast::AttrsOwner, |
540 | ) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> { | 639 | ) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> { |
541 | let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) | 640 | let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) |
542 | .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs)))); | 641 | .map_or((None, None), |(attrs, docs)| (Some(attrs), Some(docs))); |
543 | 642 | ||
544 | let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none()); | 643 | let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none()); |
545 | let attrs = outer_attrs | 644 | let attrs = outer_attrs |