Unnamed repository; edit this file 'description' to name the repository.
Better interface for doc comment and attribute processing
Lukas Wirth 2022-01-07
parent 69dbfc7 · commit 08adce6
-rw-r--r--crates/hir_def/src/attr.rs92
-rw-r--r--crates/syntax/src/ast.rs2
-rw-r--r--crates/syntax/src/ast/node_ext.rs10
-rw-r--r--crates/syntax/src/ast/token_ext.rs4
-rw-r--r--crates/syntax/src/ast/traits.rs22
5 files changed, 66 insertions, 64 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index b0be892c7d..30642f6cc6 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -5,7 +5,7 @@ use std::{fmt, hash::Hash, ops, sync::Arc};
use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
use either::Either;
-use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile};
+use hir_expand::{hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile};
use itertools::Itertools;
use la_arena::ArenaMap;
use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct};
@@ -84,6 +84,14 @@ impl ops::Deref for Attrs {
}
}
+impl ops::Index<AttrId> for Attrs {
+ type Output = Attr;
+
+ fn index(&self, AttrId { ast_index, .. }: AttrId) -> &Self::Output {
+ &(**self)[ast_index as usize]
+ }
+}
+
impl ops::Deref for AttrsWithOwner {
type Target = Attrs;
@@ -509,23 +517,23 @@ fn inner_attributes(
) -> 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::SourceFile(it) => (it.attrs(), ast::DocCommentIter::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()))
+ (extern_item_list.attrs(), ast::DocCommentIter::from_syntax_node(extern_item_list.syntax()))
},
ast::Fn(it) => {
let body = it.body()?;
let stmt_list = body.stmt_list()?;
- (stmt_list.attrs(), ast::CommentIter::from_syntax_node(body.syntax()))
+ (stmt_list.attrs(), ast::DocCommentIter::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()))
+ (assoc_item_list.attrs(), ast::DocCommentIter::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()))
+ (item_list.attrs(), ast::DocCommentIter::from_syntax_node(item_list.syntax()))
},
// FIXME: BlockExpr's only accept inner attributes in specific cases
// Excerpt from the reference:
@@ -542,27 +550,20 @@ fn inner_attributes(
#[derive(Debug)]
pub struct AttrSourceMap {
- attrs: Vec<InFile<ast::Attr>>,
- doc_comments: Vec<InFile<ast::Comment>>,
+ source: Vec<Either<ast::Attr, ast::Comment>>,
+ file_id: HirFileId,
}
impl AttrSourceMap {
fn new(owner: InFile<&dyn ast::HasAttrs>) -> Self {
- let mut attrs = Vec::new();
- let mut doc_comments = Vec::new();
- for (_, attr) in collect_attrs(owner.value) {
- match attr {
- Either::Left(attr) => attrs.push(owner.with_value(attr)),
- Either::Right(comment) => doc_comments.push(owner.with_value(comment)),
- }
+ Self {
+ source: collect_attrs(owner.value).map(|(_, it)| it).collect(),
+ file_id: owner.file_id,
}
-
- Self { attrs, doc_comments }
}
fn merge(&mut self, other: Self) {
- self.attrs.extend(other.attrs);
- self.doc_comments.extend(other.doc_comments);
+ self.source.extend(other.source);
}
/// Maps the lowered `Attr` back to its original syntax node.
@@ -571,24 +572,15 @@ impl AttrSourceMap {
///
/// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of
/// the attribute represented by `Attr`.
- pub fn source_of(&self, attr: &Attr) -> InFile<Either<ast::Attr, ast::Comment>> {
+ pub fn source_of(&self, attr: &Attr) -> InFile<&Either<ast::Attr, ast::Comment>> {
self.source_of_id(attr.id)
}
- fn source_of_id(&self, id: AttrId) -> InFile<Either<ast::Attr, ast::Comment>> {
- if id.is_doc_comment {
- self.doc_comments
- .get(id.ast_index as usize)
- .unwrap_or_else(|| panic!("cannot find doc comment at index {:?}", id))
- .clone()
- .map(Either::Right)
- } else {
- self.attrs
- .get(id.ast_index as usize)
- .unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", id))
- .clone()
- .map(Either::Left)
- }
+ fn source_of_id(&self, id: AttrId) -> InFile<&Either<ast::Attr, ast::Comment>> {
+ self.source
+ .get(id.ast_index as usize)
+ .map(|it| InFile::new(self.file_id, it))
+ .unwrap_or_else(|| panic!("cannot find attr at index {:?}", id))
}
}
@@ -656,8 +648,7 @@ fn get_doc_string_in_attr(it: &ast::Attr) -> Option<ast::String> {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub(crate) struct AttrId {
- is_doc_comment: bool,
+pub struct AttrId {
pub(crate) ast_index: u32,
}
@@ -816,27 +807,20 @@ fn collect_attrs(
.map_or((None, None), |(attrs, docs)| (Some(attrs), Some(docs)));
let outer_attrs = owner.attrs().filter(|attr| attr.kind().is_outer());
- let attrs =
- outer_attrs.chain(inner_attrs.into_iter().flatten()).enumerate().map(|(idx, attr)| {
- (
- AttrId { ast_index: idx as u32, is_doc_comment: false },
- attr.syntax().text_range().start(),
- Either::Left(attr),
- )
- });
+ let attrs = outer_attrs
+ .chain(inner_attrs.into_iter().flatten())
+ .map(|attr| (attr.syntax().text_range().start(), Either::Left(attr)));
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()).enumerate().map(|(idx, docs_text)| {
- (
- AttrId { ast_index: idx as u32, is_doc_comment: true },
- docs_text.syntax().text_range().start(),
- Either::Right(docs_text),
- )
- });
+ ast::DocCommentIter::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(), Either::Right(docs_text)));
// sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
- docs.chain(attrs).sorted_by_key(|&(_, offset, _)| offset).map(|(id, _, attr)| (id, attr))
+ docs.chain(attrs)
+ .sorted_by_key(|&(offset, _)| offset)
+ .enumerate()
+ .map(|(id, (_, attr))| (AttrId { ast_index: id as u32 }, attr))
}
pub(crate) fn variants_attrs_source_map(
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs
index 8732593af9..421120602e 100644
--- a/crates/syntax/src/ast.rs
+++ b/crates/syntax/src/ast.rs
@@ -30,7 +30,7 @@ pub use self::{
QuoteOffsets, Radix,
},
traits::{
- CommentIter, HasArgList, HasAttrs, HasDocComments, HasGenericParams, HasLoopBody,
+ DocCommentIter, HasArgList, HasAttrs, HasDocComments, HasGenericParams, HasLoopBody,
HasModuleItem, HasName, HasTypeBounds, HasVisibility,
},
};
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index dbde2a5351..705aa5edac 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -772,3 +772,13 @@ impl ast::HasLoopBody for ast::ForExpr {
}
impl ast::HasAttrs for ast::AnyHasDocComments {}
+
+impl From<ast::Adt> for ast::Item {
+ fn from(it: ast::Adt) -> Self {
+ match it {
+ ast::Adt::Enum(it) => ast::Item::Enum(it),
+ ast::Adt::Struct(it) => ast::Item::Struct(it),
+ ast::Adt::Union(it) => ast::Item::Union(it),
+ }
+ }
+}
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs
index c32ab686cc..cc7e5150b3 100644
--- a/crates/syntax/src/ast/token_ext.rs
+++ b/crates/syntax/src/ast/token_ext.rs
@@ -14,6 +14,10 @@ impl ast::Comment {
CommentKind::from_text(self.text())
}
+ pub fn is_doc(&self) -> bool {
+ self.kind().doc.is_some()
+ }
+
pub fn is_inner(&self) -> bool {
self.kind().doc == Some(CommentPlacement::Inner)
}
diff --git a/crates/syntax/src/ast/traits.rs b/crates/syntax/src/ast/traits.rs
index 2817f75d07..98b1087e64 100644
--- a/crates/syntax/src/ast/traits.rs
+++ b/crates/syntax/src/ast/traits.rs
@@ -73,17 +73,17 @@ pub trait HasAttrs: AstNode {
}
pub trait HasDocComments: HasAttrs {
- fn doc_comments(&self) -> CommentIter {
- CommentIter { iter: self.syntax().children_with_tokens() }
+ fn doc_comments(&self) -> DocCommentIter {
+ DocCommentIter { iter: self.syntax().children_with_tokens() }
}
fn doc_comments_and_attrs(&self) -> AttrCommentIter {
AttrCommentIter { iter: self.syntax().children_with_tokens() }
}
}
-impl CommentIter {
- pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> CommentIter {
- CommentIter { iter: syntax_node.children_with_tokens() }
+impl DocCommentIter {
+ pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> DocCommentIter {
+ DocCommentIter { iter: syntax_node.children_with_tokens() }
}
#[cfg(test)]
@@ -100,14 +100,16 @@ impl CommentIter {
}
}
-pub struct CommentIter {
+pub struct DocCommentIter {
iter: SyntaxElementChildren,
}
-impl Iterator for CommentIter {
+impl Iterator for DocCommentIter {
type Item = ast::Comment;
fn next(&mut self) -> Option<ast::Comment> {
- self.iter.by_ref().find_map(|el| el.into_token().and_then(ast::Comment::cast))
+ self.iter.by_ref().find_map(|el| {
+ el.into_token().and_then(ast::Comment::cast).filter(ast::Comment::is_doc)
+ })
}
}
@@ -120,7 +122,9 @@ impl Iterator for AttrCommentIter {
fn next(&mut self) -> Option<Self::Item> {
self.iter.by_ref().find_map(|el| match el {
SyntaxElement::Node(node) => ast::Attr::cast(node).map(Either::Right),
- SyntaxElement::Token(tok) => ast::Comment::cast(tok).map(Either::Left),
+ SyntaxElement::Token(tok) => {
+ ast::Comment::cast(tok).filter(ast::Comment::is_doc).map(Either::Left)
+ }
})
}
}