Skip to content

Commit ef98988

Browse files
bors[bot]Veykril
andauthored
Merge #6771
6771: Properly attach inner attributes in Attrs::new r=matklad a=Veykril Properly attach inner and outer attributes to the things they actually belong to in the HIR. ~~I can add some tests for this if wanted once I know where to put them/how to test for this.~~ Put some tests into `hover.rs`. So the following snippet ```rust mod foo { //! Hello } ``` now shows `Hello` on hover 🎉 Fixes #2148 Co-authored-by: Lukas Wirth <[email protected]>
2 parents 7bda4c7 + da3b5e3 commit ef98988

File tree

3 files changed

+117
-5
lines changed

3 files changed

+117
-5
lines changed

crates/hir_def/src/attr.rs

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use itertools::Itertools;
99
use mbe::ast_to_token_tree;
1010
use syntax::{
1111
ast::{self, AstNode, AttrsOwner},
12-
AstToken, SmolStr,
12+
match_ast, AstToken, SmolStr, SyntaxNode,
1313
};
1414
use tt::Subtree;
1515

@@ -110,7 +110,17 @@ impl Attrs {
110110
}
111111

112112
pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs {
113-
let docs = ast::CommentIter::from_syntax_node(owner.syntax()).map(|docs_text| {
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| {
114124
(
115125
docs_text.syntax().text_range().start(),
116126
docs_text.doc_comment().map(|doc| Attr {
@@ -119,9 +129,6 @@ impl Attrs {
119129
}),
120130
)
121131
});
122-
let attrs = owner
123-
.attrs()
124-
.map(|attr| (attr.syntax().text_range().start(), Attr::from_src(attr, hygiene)));
125132
// sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
126133
let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect();
127134
let entries = if attrs.is_empty() {
@@ -184,6 +191,41 @@ impl Attrs {
184191
}
185192
}
186193

194+
fn inner_attributes(
195+
syntax: &SyntaxNode,
196+
) -> Option<(impl Iterator<Item = ast::Attr>, impl Iterator<Item = ast::Comment>)> {
197+
let (attrs, docs) = match_ast! {
198+
match syntax {
199+
ast::SourceFile(it) => (it.attrs(), ast::CommentIter::from_syntax_node(it.syntax())),
200+
ast::ExternBlock(it) => {
201+
let extern_item_list = it.extern_item_list()?;
202+
(extern_item_list.attrs(), ast::CommentIter::from_syntax_node(extern_item_list.syntax()))
203+
},
204+
ast::Fn(it) => {
205+
let body = it.body()?;
206+
(body.attrs(), ast::CommentIter::from_syntax_node(body.syntax()))
207+
},
208+
ast::Impl(it) => {
209+
let assoc_item_list = it.assoc_item_list()?;
210+
(assoc_item_list.attrs(), ast::CommentIter::from_syntax_node(assoc_item_list.syntax()))
211+
},
212+
ast::Module(it) => {
213+
let item_list = it.item_list()?;
214+
(item_list.attrs(), ast::CommentIter::from_syntax_node(item_list.syntax()))
215+
},
216+
// FIXME: BlockExpr's only accept inner attributes in specific cases
217+
// Excerpt from the reference:
218+
// Block expressions accept outer and inner attributes, but only when they are the outer
219+
// expression of an expression statement or the final expression of another block expression.
220+
ast::BlockExpr(it) => return None,
221+
_ => return None,
222+
}
223+
};
224+
let attrs = attrs.filter(|attr| attr.excl_token().is_some());
225+
let docs = docs.filter(|doc| doc.is_inner());
226+
Some((attrs, docs))
227+
}
228+
187229
#[derive(Debug, Clone, PartialEq, Eq)]
188230
pub struct Attr {
189231
pub(crate) path: ModPath,

crates/ide/src/hover.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3357,4 +3357,66 @@ impl Foo {
33573357
"#]],
33583358
);
33593359
}
3360+
3361+
#[test]
3362+
fn hover_doc_outer_inner() {
3363+
check(
3364+
r#"
3365+
/// Be quick;
3366+
mod Foo<|> {
3367+
//! time is mana
3368+
3369+
/// This comment belongs to the function
3370+
fn foo() {}
3371+
}
3372+
"#,
3373+
expect![[r#"
3374+
*Foo*
3375+
3376+
```rust
3377+
test
3378+
```
3379+
3380+
```rust
3381+
mod Foo
3382+
```
3383+
3384+
---
3385+
3386+
Be quick;
3387+
time is mana
3388+
"#]],
3389+
);
3390+
}
3391+
3392+
#[test]
3393+
fn hover_doc_outer_inner_attribue() {
3394+
check(
3395+
r#"
3396+
#[doc = "Be quick;"]
3397+
mod Foo<|> {
3398+
#![doc = "time is mana"]
3399+
3400+
#[doc = "This comment belongs to the function"]
3401+
fn foo() {}
3402+
}
3403+
"#,
3404+
expect![[r#"
3405+
*Foo*
3406+
3407+
```rust
3408+
test
3409+
```
3410+
3411+
```rust
3412+
mod Foo
3413+
```
3414+
3415+
---
3416+
3417+
Be quick;
3418+
time is mana
3419+
"#]],
3420+
);
3421+
}
33603422
}

crates/syntax/src/ast/token_ext.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ impl ast::Comment {
1717
CommentKind::from_text(self.text())
1818
}
1919

20+
pub fn is_inner(&self) -> bool {
21+
self.kind().doc == Some(CommentPlacement::Inner)
22+
}
23+
24+
pub fn is_outer(&self) -> bool {
25+
self.kind().doc == Some(CommentPlacement::Outer)
26+
}
27+
2028
pub fn prefix(&self) -> &'static str {
2129
let &(prefix, _kind) = CommentKind::BY_PREFIX
2230
.iter()

0 commit comments

Comments
 (0)