mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 21:54:42 +00:00
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 <lukastw97@gmail.com>
This commit is contained in:
commit
ef989880ff
3 changed files with 117 additions and 5 deletions
|
@ -9,7 +9,7 @@ use itertools::Itertools;
|
|||
use mbe::ast_to_token_tree;
|
||||
use syntax::{
|
||||
ast::{self, AstNode, AttrsOwner},
|
||||
AstToken, SmolStr,
|
||||
match_ast, AstToken, SmolStr, SyntaxNode,
|
||||
};
|
||||
use tt::Subtree;
|
||||
|
||||
|
@ -110,7 +110,17 @@ impl Attrs {
|
|||
}
|
||||
|
||||
pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs {
|
||||
let docs = ast::CommentIter::from_syntax_node(owner.syntax()).map(|docs_text| {
|
||||
let (inner_attrs, inner_docs) = inner_attributes(owner.syntax())
|
||||
.map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs))));
|
||||
|
||||
let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none());
|
||||
let attrs = outer_attrs
|
||||
.chain(inner_attrs.into_iter().flatten())
|
||||
.map(|attr| (attr.syntax().text_range().start(), Attr::from_src(attr, hygiene)));
|
||||
|
||||
let outer_docs =
|
||||
ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer);
|
||||
let docs = outer_docs.chain(inner_docs.into_iter().flatten()).map(|docs_text| {
|
||||
(
|
||||
docs_text.syntax().text_range().start(),
|
||||
docs_text.doc_comment().map(|doc| Attr {
|
||||
|
@ -119,9 +129,6 @@ impl Attrs {
|
|||
}),
|
||||
)
|
||||
});
|
||||
let attrs = owner
|
||||
.attrs()
|
||||
.map(|attr| (attr.syntax().text_range().start(), Attr::from_src(attr, hygiene)));
|
||||
// sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
|
||||
let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect();
|
||||
let entries = if attrs.is_empty() {
|
||||
|
@ -184,6 +191,41 @@ impl Attrs {
|
|||
}
|
||||
}
|
||||
|
||||
fn inner_attributes(
|
||||
syntax: &SyntaxNode,
|
||||
) -> Option<(impl Iterator<Item = ast::Attr>, impl Iterator<Item = ast::Comment>)> {
|
||||
let (attrs, docs) = match_ast! {
|
||||
match syntax {
|
||||
ast::SourceFile(it) => (it.attrs(), ast::CommentIter::from_syntax_node(it.syntax())),
|
||||
ast::ExternBlock(it) => {
|
||||
let extern_item_list = it.extern_item_list()?;
|
||||
(extern_item_list.attrs(), ast::CommentIter::from_syntax_node(extern_item_list.syntax()))
|
||||
},
|
||||
ast::Fn(it) => {
|
||||
let body = it.body()?;
|
||||
(body.attrs(), ast::CommentIter::from_syntax_node(body.syntax()))
|
||||
},
|
||||
ast::Impl(it) => {
|
||||
let assoc_item_list = it.assoc_item_list()?;
|
||||
(assoc_item_list.attrs(), ast::CommentIter::from_syntax_node(assoc_item_list.syntax()))
|
||||
},
|
||||
ast::Module(it) => {
|
||||
let item_list = it.item_list()?;
|
||||
(item_list.attrs(), ast::CommentIter::from_syntax_node(item_list.syntax()))
|
||||
},
|
||||
// FIXME: BlockExpr's only accept inner attributes in specific cases
|
||||
// Excerpt from the reference:
|
||||
// Block expressions accept outer and inner attributes, but only when they are the outer
|
||||
// expression of an expression statement or the final expression of another block expression.
|
||||
ast::BlockExpr(it) => return None,
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
let attrs = attrs.filter(|attr| attr.excl_token().is_some());
|
||||
let docs = docs.filter(|doc| doc.is_inner());
|
||||
Some((attrs, docs))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Attr {
|
||||
pub(crate) path: ModPath,
|
||||
|
|
|
@ -3357,4 +3357,66 @@ impl Foo {
|
|||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_doc_outer_inner() {
|
||||
check(
|
||||
r#"
|
||||
/// Be quick;
|
||||
mod Foo<|> {
|
||||
//! time is mana
|
||||
|
||||
/// This comment belongs to the function
|
||||
fn foo() {}
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
*Foo*
|
||||
|
||||
```rust
|
||||
test
|
||||
```
|
||||
|
||||
```rust
|
||||
mod Foo
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Be quick;
|
||||
time is mana
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_doc_outer_inner_attribue() {
|
||||
check(
|
||||
r#"
|
||||
#[doc = "Be quick;"]
|
||||
mod Foo<|> {
|
||||
#![doc = "time is mana"]
|
||||
|
||||
#[doc = "This comment belongs to the function"]
|
||||
fn foo() {}
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
*Foo*
|
||||
|
||||
```rust
|
||||
test
|
||||
```
|
||||
|
||||
```rust
|
||||
mod Foo
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Be quick;
|
||||
time is mana
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,14 @@ impl ast::Comment {
|
|||
CommentKind::from_text(self.text())
|
||||
}
|
||||
|
||||
pub fn is_inner(&self) -> bool {
|
||||
self.kind().doc == Some(CommentPlacement::Inner)
|
||||
}
|
||||
|
||||
pub fn is_outer(&self) -> bool {
|
||||
self.kind().doc == Some(CommentPlacement::Outer)
|
||||
}
|
||||
|
||||
pub fn prefix(&self) -> &'static str {
|
||||
let &(prefix, _kind) = CommentKind::BY_PREFIX
|
||||
.iter()
|
||||
|
|
Loading…
Reference in a new issue