aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-12-19 14:25:33 +0000
committerGitHub <[email protected]>2020-12-19 14:25:33 +0000
commitdf3652b663dbeb19d7353e6e789024951a0589af (patch)
treea654c79e11f98970fa8374cf3e8305d401ce684f
parente9440f598d7ad3758594d4c5ab44f467395170c5 (diff)
parent218e88ab552086754818e56e488e26d5ad60ea52 (diff)
Merge #6948
6948: Add API for mapping `Attr` back to its syntax node r=jonas-schievink a=jonas-schievink This will be useful for emitting diagnostics pertaining to a specific attribute Co-authored-by: Jonas Schievink <[email protected]>
-rw-r--r--crates/hir_def/src/attr.rs78
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)]
318pub struct Attr { 317pub 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
448fn 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}