From eac77997bfedd55fa572e3fdcf0058f12a0ebb63 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 8 Dec 2020 23:21:20 +0100 Subject: [PATCH 1/3] Properly fetch inner and outer attributes on hir-level --- crates/hir_def/src/attr.rs | 44 +++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index 12f4b02e299..e9192528afc 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs @@ -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,7 @@ 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 outer_docs = ast::CommentIter::from_syntax_node(owner.syntax()).map(|docs_text| { ( docs_text.syntax().text_range().start(), docs_text.doc_comment().map(|doc| Attr { @@ -119,11 +119,13 @@ impl Attrs { }), ) }); - let attrs = owner - .attrs() + let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none()); + let inner_attrs = inner_attributes(owner.syntax()).into_iter().flatten(); + let attrs = outer_attrs + .chain(inner_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 attrs: Vec<_> = outer_docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect(); let entries = if attrs.is_empty() { // Avoid heap allocation None @@ -184,6 +186,38 @@ impl Attrs { } } +fn inner_attributes(syntax: &SyntaxNode) -> Option> { + let (attrs, _docs) = match_ast! { + match syntax { + ast::SourceFile(it) => (it.attrs(), None::), + ast::ExternBlock(it) => { + let extern_item_list = it.extern_item_list()?; + (extern_item_list.attrs(), None) + }, + ast::Fn(it) => { + let body = it.body()?; + (body.attrs(), None) + }, + ast::Impl(it) => { + let assoc_item_list = it.assoc_item_list()?; + (assoc_item_list.attrs(), None) + }, + ast::Module(it) => { + let item_list = it.item_list()?; + (item_list.attrs(), None) + }, + // 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()); + Some(attrs) +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct Attr { pub(crate) path: ModPath, From f8823e8cbcd4364db4155f5e20989c02b68cf855 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 8 Dec 2020 23:30:51 +0100 Subject: [PATCH 2/3] Properly fetch inner and outer docs on hir-level --- crates/hir_def/src/attr.rs | 42 ++++++++++++++++++------------ crates/syntax/src/ast/token_ext.rs | 8 ++++++ 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index e9192528afc..228d706db99 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs @@ -110,7 +110,17 @@ impl Attrs { } pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs { - let outer_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,13 +129,8 @@ impl Attrs { }), ) }); - let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none()); - let inner_attrs = inner_attributes(owner.syntax()).into_iter().flatten(); - let attrs = outer_attrs - .chain(inner_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<_> = outer_docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect(); + let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect(); let entries = if attrs.is_empty() { // Avoid heap allocation None @@ -186,36 +191,39 @@ impl Attrs { } } -fn inner_attributes(syntax: &SyntaxNode) -> Option> { - let (attrs, _docs) = match_ast! { +fn inner_attributes( + syntax: &SyntaxNode, +) -> Option<(impl Iterator, impl Iterator)> { + let (attrs, docs) = match_ast! { match syntax { - ast::SourceFile(it) => (it.attrs(), None::), + 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(), None) + (extern_item_list.attrs(), ast::CommentIter::from_syntax_node(extern_item_list.syntax())) }, ast::Fn(it) => { let body = it.body()?; - (body.attrs(), None) + (body.attrs(), ast::CommentIter::from_syntax_node(body.syntax())) }, ast::Impl(it) => { let assoc_item_list = it.assoc_item_list()?; - (assoc_item_list.attrs(), None) + (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(), None) + (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. + // 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()); - Some(attrs) + let docs = docs.filter(|doc| doc.is_inner()); + Some((attrs, docs)) } #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index a10b1477804..52b7285dde2 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs @@ -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() From da3b5e35a69fd71ba061169e2fe719e36dbc3ef4 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 9 Dec 2020 09:22:57 +0100 Subject: [PATCH 3/3] Test inner and outer doc comments in hover --- crates/ide/src/hover.rs | 62 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 1b6ff6d2109..cf04c3de08a 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -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 + "#]], + ); + } }