diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-12-09 13:16:39 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-12-09 13:16:39 +0000 |
commit | ef989880fff36f10b7e166647497779bacc1c47f (patch) | |
tree | 3fbf3b76697775ec50c286f9691fc736d7fb4886 | |
parent | 7bda4c722bc5bb81b0f5b0f4c9390e3c7b602d0f (diff) | |
parent | da3b5e35a69fd71ba061169e2fe719e36dbc3ef4 (diff) |
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]>
-rw-r--r-- | crates/hir_def/src/attr.rs | 52 | ||||
-rw-r--r-- | crates/ide/src/hover.rs | 62 | ||||
-rw-r--r-- | crates/syntax/src/ast/token_ext.rs | 8 |
3 files changed, 117 insertions, 5 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index 12f4b02e2..228d706db 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -9,7 +9,7 @@ use itertools::Itertools; | |||
9 | use mbe::ast_to_token_tree; | 9 | use mbe::ast_to_token_tree; |
10 | use syntax::{ | 10 | use syntax::{ |
11 | ast::{self, AstNode, AttrsOwner}, | 11 | ast::{self, AstNode, AttrsOwner}, |
12 | AstToken, SmolStr, | 12 | match_ast, AstToken, SmolStr, SyntaxNode, |
13 | }; | 13 | }; |
14 | use tt::Subtree; | 14 | use tt::Subtree; |
15 | 15 | ||
@@ -110,7 +110,17 @@ impl Attrs { | |||
110 | } | 110 | } |
111 | 111 | ||
112 | pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs { | 112 | 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| { | ||
114 | ( | 124 | ( |
115 | docs_text.syntax().text_range().start(), | 125 | docs_text.syntax().text_range().start(), |
116 | docs_text.doc_comment().map(|doc| Attr { | 126 | docs_text.doc_comment().map(|doc| Attr { |
@@ -119,9 +129,6 @@ impl Attrs { | |||
119 | }), | 129 | }), |
120 | ) | 130 | ) |
121 | }); | 131 | }); |
122 | let attrs = owner | ||
123 | .attrs() | ||
124 | .map(|attr| (attr.syntax().text_range().start(), Attr::from_src(attr, hygiene))); | ||
125 | // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved | 132 | // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved |
126 | let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect(); | 133 | let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect(); |
127 | let entries = if attrs.is_empty() { | 134 | let entries = if attrs.is_empty() { |
@@ -184,6 +191,41 @@ impl Attrs { | |||
184 | } | 191 | } |
185 | } | 192 | } |
186 | 193 | ||
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 | |||
187 | #[derive(Debug, Clone, PartialEq, Eq)] | 229 | #[derive(Debug, Clone, PartialEq, Eq)] |
188 | pub struct Attr { | 230 | pub struct Attr { |
189 | pub(crate) path: ModPath, | 231 | pub(crate) path: ModPath, |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 1b6ff6d21..cf04c3de0 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -3357,4 +3357,66 @@ impl Foo { | |||
3357 | "#]], | 3357 | "#]], |
3358 | ); | 3358 | ); |
3359 | } | 3359 | } |
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 | } | ||
3360 | } | 3422 | } |
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index a10b14778..52b7285dd 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs | |||
@@ -17,6 +17,14 @@ impl ast::Comment { | |||
17 | CommentKind::from_text(self.text()) | 17 | CommentKind::from_text(self.text()) |
18 | } | 18 | } |
19 | 19 | ||
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 | |||
20 | pub fn prefix(&self) -> &'static str { | 28 | pub fn prefix(&self) -> &'static str { |
21 | let &(prefix, _kind) = CommentKind::BY_PREFIX | 29 | let &(prefix, _kind) = CommentKind::BY_PREFIX |
22 | .iter() | 30 | .iter() |