Skip to content

Commit 08adce6

Browse files
committed
Better interface for doc comment and attribute processing
1 parent 69dbfc7 commit 08adce6

File tree

5 files changed

+66
-64
lines changed

5 files changed

+66
-64
lines changed

crates/hir_def/src/attr.rs

Lines changed: 38 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::{fmt, hash::Hash, ops, sync::Arc};
55
use base_db::CrateId;
66
use cfg::{CfgExpr, CfgOptions};
77
use either::Either;
8-
use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile};
8+
use hir_expand::{hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile};
99
use itertools::Itertools;
1010
use la_arena::ArenaMap;
1111
use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct};
@@ -84,6 +84,14 @@ impl ops::Deref for Attrs {
8484
}
8585
}
8686

87+
impl ops::Index<AttrId> for Attrs {
88+
type Output = Attr;
89+
90+
fn index(&self, AttrId { ast_index, .. }: AttrId) -> &Self::Output {
91+
&(**self)[ast_index as usize]
92+
}
93+
}
94+
8795
impl ops::Deref for AttrsWithOwner {
8896
type Target = Attrs;
8997

@@ -509,23 +517,23 @@ fn inner_attributes(
509517
) -> Option<(impl Iterator<Item = ast::Attr>, impl Iterator<Item = ast::Comment>)> {
510518
let (attrs, docs) = match_ast! {
511519
match syntax {
512-
ast::SourceFile(it) => (it.attrs(), ast::CommentIter::from_syntax_node(it.syntax())),
520+
ast::SourceFile(it) => (it.attrs(), ast::DocCommentIter::from_syntax_node(it.syntax())),
513521
ast::ExternBlock(it) => {
514522
let extern_item_list = it.extern_item_list()?;
515-
(extern_item_list.attrs(), ast::CommentIter::from_syntax_node(extern_item_list.syntax()))
523+
(extern_item_list.attrs(), ast::DocCommentIter::from_syntax_node(extern_item_list.syntax()))
516524
},
517525
ast::Fn(it) => {
518526
let body = it.body()?;
519527
let stmt_list = body.stmt_list()?;
520-
(stmt_list.attrs(), ast::CommentIter::from_syntax_node(body.syntax()))
528+
(stmt_list.attrs(), ast::DocCommentIter::from_syntax_node(body.syntax()))
521529
},
522530
ast::Impl(it) => {
523531
let assoc_item_list = it.assoc_item_list()?;
524-
(assoc_item_list.attrs(), ast::CommentIter::from_syntax_node(assoc_item_list.syntax()))
532+
(assoc_item_list.attrs(), ast::DocCommentIter::from_syntax_node(assoc_item_list.syntax()))
525533
},
526534
ast::Module(it) => {
527535
let item_list = it.item_list()?;
528-
(item_list.attrs(), ast::CommentIter::from_syntax_node(item_list.syntax()))
536+
(item_list.attrs(), ast::DocCommentIter::from_syntax_node(item_list.syntax()))
529537
},
530538
// FIXME: BlockExpr's only accept inner attributes in specific cases
531539
// Excerpt from the reference:
@@ -542,27 +550,20 @@ fn inner_attributes(
542550

543551
#[derive(Debug)]
544552
pub struct AttrSourceMap {
545-
attrs: Vec<InFile<ast::Attr>>,
546-
doc_comments: Vec<InFile<ast::Comment>>,
553+
source: Vec<Either<ast::Attr, ast::Comment>>,
554+
file_id: HirFileId,
547555
}
548556

549557
impl AttrSourceMap {
550558
fn new(owner: InFile<&dyn ast::HasAttrs>) -> Self {
551-
let mut attrs = Vec::new();
552-
let mut doc_comments = Vec::new();
553-
for (_, attr) in collect_attrs(owner.value) {
554-
match attr {
555-
Either::Left(attr) => attrs.push(owner.with_value(attr)),
556-
Either::Right(comment) => doc_comments.push(owner.with_value(comment)),
557-
}
559+
Self {
560+
source: collect_attrs(owner.value).map(|(_, it)| it).collect(),
561+
file_id: owner.file_id,
558562
}
559-
560-
Self { attrs, doc_comments }
561563
}
562564

563565
fn merge(&mut self, other: Self) {
564-
self.attrs.extend(other.attrs);
565-
self.doc_comments.extend(other.doc_comments);
566+
self.source.extend(other.source);
566567
}
567568

568569
/// Maps the lowered `Attr` back to its original syntax node.
@@ -571,24 +572,15 @@ impl AttrSourceMap {
571572
///
572573
/// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of
573574
/// the attribute represented by `Attr`.
574-
pub fn source_of(&self, attr: &Attr) -> InFile<Either<ast::Attr, ast::Comment>> {
575+
pub fn source_of(&self, attr: &Attr) -> InFile<&Either<ast::Attr, ast::Comment>> {
575576
self.source_of_id(attr.id)
576577
}
577578

578-
fn source_of_id(&self, id: AttrId) -> InFile<Either<ast::Attr, ast::Comment>> {
579-
if id.is_doc_comment {
580-
self.doc_comments
581-
.get(id.ast_index as usize)
582-
.unwrap_or_else(|| panic!("cannot find doc comment at index {:?}", id))
583-
.clone()
584-
.map(Either::Right)
585-
} else {
586-
self.attrs
587-
.get(id.ast_index as usize)
588-
.unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", id))
589-
.clone()
590-
.map(Either::Left)
591-
}
579+
fn source_of_id(&self, id: AttrId) -> InFile<&Either<ast::Attr, ast::Comment>> {
580+
self.source
581+
.get(id.ast_index as usize)
582+
.map(|it| InFile::new(self.file_id, it))
583+
.unwrap_or_else(|| panic!("cannot find attr at index {:?}", id))
592584
}
593585
}
594586

@@ -656,8 +648,7 @@ fn get_doc_string_in_attr(it: &ast::Attr) -> Option<ast::String> {
656648
}
657649

658650
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
659-
pub(crate) struct AttrId {
660-
is_doc_comment: bool,
651+
pub struct AttrId {
661652
pub(crate) ast_index: u32,
662653
}
663654

@@ -816,27 +807,20 @@ fn collect_attrs(
816807
.map_or((None, None), |(attrs, docs)| (Some(attrs), Some(docs)));
817808

818809
let outer_attrs = owner.attrs().filter(|attr| attr.kind().is_outer());
819-
let attrs =
820-
outer_attrs.chain(inner_attrs.into_iter().flatten()).enumerate().map(|(idx, attr)| {
821-
(
822-
AttrId { ast_index: idx as u32, is_doc_comment: false },
823-
attr.syntax().text_range().start(),
824-
Either::Left(attr),
825-
)
826-
});
810+
let attrs = outer_attrs
811+
.chain(inner_attrs.into_iter().flatten())
812+
.map(|attr| (attr.syntax().text_range().start(), Either::Left(attr)));
827813

828814
let outer_docs =
829-
ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer);
830-
let docs =
831-
outer_docs.chain(inner_docs.into_iter().flatten()).enumerate().map(|(idx, docs_text)| {
832-
(
833-
AttrId { ast_index: idx as u32, is_doc_comment: true },
834-
docs_text.syntax().text_range().start(),
835-
Either::Right(docs_text),
836-
)
837-
});
815+
ast::DocCommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer);
816+
let docs = outer_docs
817+
.chain(inner_docs.into_iter().flatten())
818+
.map(|docs_text| (docs_text.syntax().text_range().start(), Either::Right(docs_text)));
838819
// sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
839-
docs.chain(attrs).sorted_by_key(|&(_, offset, _)| offset).map(|(id, _, attr)| (id, attr))
820+
docs.chain(attrs)
821+
.sorted_by_key(|&(offset, _)| offset)
822+
.enumerate()
823+
.map(|(id, (_, attr))| (AttrId { ast_index: id as u32 }, attr))
840824
}
841825

842826
pub(crate) fn variants_attrs_source_map(

crates/syntax/src/ast.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub use self::{
3030
QuoteOffsets, Radix,
3131
},
3232
traits::{
33-
CommentIter, HasArgList, HasAttrs, HasDocComments, HasGenericParams, HasLoopBody,
33+
DocCommentIter, HasArgList, HasAttrs, HasDocComments, HasGenericParams, HasLoopBody,
3434
HasModuleItem, HasName, HasTypeBounds, HasVisibility,
3535
},
3636
};

crates/syntax/src/ast/node_ext.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,3 +772,13 @@ impl ast::HasLoopBody for ast::ForExpr {
772772
}
773773

774774
impl ast::HasAttrs for ast::AnyHasDocComments {}
775+
776+
impl From<ast::Adt> for ast::Item {
777+
fn from(it: ast::Adt) -> Self {
778+
match it {
779+
ast::Adt::Enum(it) => ast::Item::Enum(it),
780+
ast::Adt::Struct(it) => ast::Item::Struct(it),
781+
ast::Adt::Union(it) => ast::Item::Union(it),
782+
}
783+
}
784+
}

crates/syntax/src/ast/token_ext.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ impl ast::Comment {
1414
CommentKind::from_text(self.text())
1515
}
1616

17+
pub fn is_doc(&self) -> bool {
18+
self.kind().doc.is_some()
19+
}
20+
1721
pub fn is_inner(&self) -> bool {
1822
self.kind().doc == Some(CommentPlacement::Inner)
1923
}

crates/syntax/src/ast/traits.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,17 +73,17 @@ pub trait HasAttrs: AstNode {
7373
}
7474

7575
pub trait HasDocComments: HasAttrs {
76-
fn doc_comments(&self) -> CommentIter {
77-
CommentIter { iter: self.syntax().children_with_tokens() }
76+
fn doc_comments(&self) -> DocCommentIter {
77+
DocCommentIter { iter: self.syntax().children_with_tokens() }
7878
}
7979
fn doc_comments_and_attrs(&self) -> AttrCommentIter {
8080
AttrCommentIter { iter: self.syntax().children_with_tokens() }
8181
}
8282
}
8383

84-
impl CommentIter {
85-
pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> CommentIter {
86-
CommentIter { iter: syntax_node.children_with_tokens() }
84+
impl DocCommentIter {
85+
pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> DocCommentIter {
86+
DocCommentIter { iter: syntax_node.children_with_tokens() }
8787
}
8888

8989
#[cfg(test)]
@@ -100,14 +100,16 @@ impl CommentIter {
100100
}
101101
}
102102

103-
pub struct CommentIter {
103+
pub struct DocCommentIter {
104104
iter: SyntaxElementChildren,
105105
}
106106

107-
impl Iterator for CommentIter {
107+
impl Iterator for DocCommentIter {
108108
type Item = ast::Comment;
109109
fn next(&mut self) -> Option<ast::Comment> {
110-
self.iter.by_ref().find_map(|el| el.into_token().and_then(ast::Comment::cast))
110+
self.iter.by_ref().find_map(|el| {
111+
el.into_token().and_then(ast::Comment::cast).filter(ast::Comment::is_doc)
112+
})
111113
}
112114
}
113115

@@ -120,7 +122,9 @@ impl Iterator for AttrCommentIter {
120122
fn next(&mut self) -> Option<Self::Item> {
121123
self.iter.by_ref().find_map(|el| match el {
122124
SyntaxElement::Node(node) => ast::Attr::cast(node).map(Either::Right),
123-
SyntaxElement::Token(tok) => ast::Comment::cast(tok).map(Either::Left),
125+
SyntaxElement::Token(tok) => {
126+
ast::Comment::cast(tok).filter(ast::Comment::is_doc).map(Either::Left)
127+
}
124128
})
125129
}
126130
}

0 commit comments

Comments
 (0)