diff options
Diffstat (limited to 'crates/hir_def')
-rw-r--r-- | crates/hir_def/src/attr.rs | 78 |
1 files changed, 55 insertions, 23 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index 73b17da10..042e119b1 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -75,32 +75,31 @@ impl RawAttrs { | |||
75 | pub(crate) const EMPTY: Self = Self { entries: None }; | 75 | pub(crate) const EMPTY: Self = Self { entries: None }; |
76 | 76 | ||
77 | pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Self { | 77 | pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Self { |
78 | let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) | 78 | let attrs: Vec<_> = collect_attrs(owner).collect(); |
79 | .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs)))); | ||
80 | |||
81 | let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none()); | ||
82 | let attrs = outer_attrs | ||
83 | .chain(inner_attrs.into_iter().flatten()) | ||
84 | .map(|attr| (attr.syntax().text_range().start(), Attr::from_src(attr, hygiene))); | ||
85 | |||
86 | let outer_docs = | ||
87 | ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer); | ||
88 | let docs = outer_docs.chain(inner_docs.into_iter().flatten()).map(|docs_text| { | ||
89 | ( | ||
90 | docs_text.syntax().text_range().start(), | ||
91 | docs_text.doc_comment().map(|doc| Attr { | ||
92 | input: Some(AttrInput::Literal(SmolStr::new(doc))), | ||
93 | path: ModPath::from(hir_expand::name!(doc)), | ||
94 | }), | ||
95 | ) | ||
96 | }); | ||
97 | // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved | ||
98 | let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect(); | ||
99 | let entries = if attrs.is_empty() { | 79 | let entries = if attrs.is_empty() { |
100 | // Avoid heap allocation | 80 | // Avoid heap allocation |
101 | None | 81 | None |
102 | } else { | 82 | } else { |
103 | Some(attrs.into_iter().flat_map(|(_, attr)| attr).collect()) | 83 | Some( |
84 | attrs | ||
85 | .into_iter() | ||
86 | .enumerate() | ||
87 | .flat_map(|(i, attr)| match attr { | ||
88 | Either::Left(attr) => Attr::from_src(attr, hygiene).map(|attr| (i, attr)), | ||
89 | Either::Right(comment) => comment.doc_comment().map(|doc| { | ||
90 | ( | ||
91 | i, | ||
92 | Attr { | ||
93 | index: 0, | ||
94 | input: Some(AttrInput::Literal(SmolStr::new(doc))), | ||
95 | path: ModPath::from(hir_expand::name!(doc)), | ||
96 | }, | ||
97 | ) | ||
98 | }), | ||
99 | }) | ||
100 | .map(|(i, attr)| Attr { index: i as u32, ..attr }) | ||
101 | .collect(), | ||
102 | ) | ||
104 | }; | 103 | }; |
105 | Self { entries } | 104 | Self { entries } |
106 | } | 105 | } |
@@ -316,6 +315,7 @@ fn inner_attributes( | |||
316 | 315 | ||
317 | #[derive(Debug, Clone, PartialEq, Eq)] | 316 | #[derive(Debug, Clone, PartialEq, Eq)] |
318 | pub struct Attr { | 317 | pub struct Attr { |
318 | index: u32, | ||
319 | pub(crate) path: ModPath, | 319 | pub(crate) path: ModPath, |
320 | pub(crate) input: Option<AttrInput>, | 320 | pub(crate) input: Option<AttrInput>, |
321 | } | 321 | } |
@@ -342,7 +342,19 @@ impl Attr { | |||
342 | } else { | 342 | } else { |
343 | None | 343 | None |
344 | }; | 344 | }; |
345 | Some(Attr { path, input }) | 345 | Some(Attr { index: 0, path, input }) |
346 | } | ||
347 | |||
348 | /// Maps this lowered `Attr` back to its original syntax node. | ||
349 | /// | ||
350 | /// `owner` must be the original owner of the attribute. | ||
351 | /// | ||
352 | /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of | ||
353 | /// the attribute represented by `Attr`. | ||
354 | pub fn to_src(&self, owner: &dyn AttrsOwner) -> Either<ast::Attr, ast::Comment> { | ||
355 | collect_attrs(owner).nth(self.index as usize).unwrap_or_else(|| { | ||
356 | panic!("cannot find `Attr` at index {} in {}", self.index, owner.syntax()) | ||
357 | }) | ||
346 | } | 358 | } |
347 | 359 | ||
348 | /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths | 360 | /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths |
@@ -432,3 +444,23 @@ fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase | |||
432 | let mod_item = N::id_to_mod_item(id.value); | 444 | let mod_item = N::id_to_mod_item(id.value); |
433 | tree.raw_attrs(mod_item.into()).clone() | 445 | tree.raw_attrs(mod_item.into()).clone() |
434 | } | 446 | } |
447 | |||
448 | fn collect_attrs(owner: &dyn AttrsOwner) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> { | ||
449 | let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) | ||
450 | .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs)))); | ||
451 | |||
452 | let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none()); | ||
453 | let attrs = outer_attrs | ||
454 | .chain(inner_attrs.into_iter().flatten()) | ||
455 | .map(|attr| (attr.syntax().text_range().start(), Either::Left(attr))); | ||
456 | |||
457 | let outer_docs = | ||
458 | ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer); | ||
459 | let docs = outer_docs | ||
460 | .chain(inner_docs.into_iter().flatten()) | ||
461 | .map(|docs_text| (docs_text.syntax().text_range().start(), Either::Right(docs_text))); | ||
462 | // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved | ||
463 | let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect(); | ||
464 | |||
465 | attrs.into_iter().map(|(_, attr)| attr) | ||
466 | } | ||