Skip to content

Commit df3652b

Browse files
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]>
2 parents e9440f5 + 218e88a commit df3652b

File tree

1 file changed

+55
-23
lines changed

1 file changed

+55
-23
lines changed

crates/hir_def/src/attr.rs

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -75,32 +75,31 @@ impl RawAttrs {
7575
pub(crate) const EMPTY: Self = Self { entries: None };
7676

7777
pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Self {
78-
let (inner_attrs, inner_docs) = inner_attributes(owner.syntax())
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();
78+
let attrs: Vec<_> = collect_attrs(owner).collect();
9979
let entries = if attrs.is_empty() {
10080
// Avoid heap allocation
10181
None
10282
} 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+
)
104103
};
105104
Self { entries }
106105
}
@@ -316,6 +315,7 @@ fn inner_attributes(
316315

317316
#[derive(Debug, Clone, PartialEq, Eq)]
318317
pub struct Attr {
318+
index: u32,
319319
pub(crate) path: ModPath,
320320
pub(crate) input: Option<AttrInput>,
321321
}
@@ -342,7 +342,19 @@ impl Attr {
342342
} else {
343343
None
344344
};
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+
})
346358
}
347359

348360
/// 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
432444
let mod_item = N::id_to_mod_item(id.value);
433445
tree.raw_attrs(mod_item.into()).clone()
434446
}
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+
}

0 commit comments

Comments
 (0)