Skip to content

Commit 6afd9b2

Browse files
Merge #8796
8796: internal: rewrite `#[derive]` removal to be based on AST (take 2) r=jonas-schievink a=jonas-schievink Second attempt of #8443, this uses syntactical attribute offsets in `hir_expand`, and changes `attr.rs` to make those easy to derive. This will make it easy to add similar attribute removal for attribute macros, unblocking them. Co-authored-by: Jonas Schievink <[email protected]>
2 parents 9fa9d16 + 8ea9d93 commit 6afd9b2

File tree

8 files changed

+205
-175
lines changed

8 files changed

+205
-175
lines changed

crates/hir_def/src/attr.rs

Lines changed: 86 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::{
99
use base_db::CrateId;
1010
use cfg::{CfgExpr, CfgOptions};
1111
use either::Either;
12-
use hir_expand::{hygiene::Hygiene, name::AsName, AstId, AttrId, InFile};
12+
use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile};
1313
use itertools::Itertools;
1414
use la_arena::ArenaMap;
1515
use mbe::ast_to_token_tree;
@@ -101,17 +101,13 @@ impl RawAttrs {
101101
hygiene: &Hygiene,
102102
) -> Self {
103103
let entries = collect_attrs(owner)
104-
.enumerate()
105-
.flat_map(|(i, attr)| {
106-
let index = AttrId(i as u32);
107-
match attr {
108-
Either::Left(attr) => Attr::from_src(db, attr, hygiene, index),
109-
Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
110-
id: index,
111-
input: Some(AttrInput::Literal(SmolStr::new(doc))),
112-
path: Interned::new(ModPath::from(hir_expand::name!(doc))),
113-
}),
114-
}
104+
.flat_map(|(id, attr)| match attr {
105+
Either::Left(attr) => Attr::from_src(db, attr, hygiene, id),
106+
Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
107+
id,
108+
input: Some(AttrInput::Literal(SmolStr::new(doc))),
109+
path: Interned::new(ModPath::from(hir_expand::name!(doc))),
110+
}),
115111
})
116112
.collect::<Arc<_>>();
117113

@@ -124,6 +120,7 @@ impl RawAttrs {
124120
}
125121

126122
pub(crate) fn merge(&self, other: Self) -> Self {
123+
// FIXME: This needs to fixup `AttrId`s
127124
match (&self.entries, &other.entries) {
128125
(None, None) => Self::EMPTY,
129126
(Some(entries), None) | (None, Some(entries)) => {
@@ -375,39 +372,26 @@ impl AttrsWithOwner {
375372

376373
let def_map = module.def_map(db);
377374
let mod_data = &def_map[module.local_id];
378-
let attrs = match mod_data.declaration_source(db) {
375+
match mod_data.declaration_source(db) {
379376
Some(it) => {
380-
let mut attrs: Vec<_> = collect_attrs(&it.value as &dyn ast::AttrsOwner)
381-
.map(|attr| InFile::new(it.file_id, attr))
382-
.collect();
377+
let mut map = AttrSourceMap::new(InFile::new(it.file_id, &it.value));
383378
if let InFile { file_id, value: ModuleSource::SourceFile(file) } =
384379
mod_data.definition_source(db)
385380
{
386-
attrs.extend(
387-
collect_attrs(&file as &dyn ast::AttrsOwner)
388-
.map(|attr| InFile::new(file_id, attr)),
389-
)
381+
map.merge(AttrSourceMap::new(InFile::new(file_id, &file)));
390382
}
391-
attrs
383+
return map;
392384
}
393385
None => {
394386
let InFile { file_id, value } = mod_data.definition_source(db);
395-
match &value {
396-
ModuleSource::SourceFile(file) => {
397-
collect_attrs(file as &dyn ast::AttrsOwner)
398-
}
399-
ModuleSource::Module(module) => {
400-
collect_attrs(module as &dyn ast::AttrsOwner)
401-
}
402-
ModuleSource::BlockExpr(block) => {
403-
collect_attrs(block as &dyn ast::AttrsOwner)
404-
}
405-
}
406-
.map(|attr| InFile::new(file_id, attr))
407-
.collect()
387+
let attrs_owner = match &value {
388+
ModuleSource::SourceFile(file) => file as &dyn ast::AttrsOwner,
389+
ModuleSource::Module(module) => module as &dyn ast::AttrsOwner,
390+
ModuleSource::BlockExpr(block) => block as &dyn ast::AttrsOwner,
391+
};
392+
return AttrSourceMap::new(InFile::new(file_id, attrs_owner));
408393
}
409-
};
410-
return AttrSourceMap { attrs };
394+
}
411395
}
412396
AttrDefId::FieldId(id) => {
413397
let map = db.fields_attrs_source_map(id.parent);
@@ -462,11 +446,7 @@ impl AttrsWithOwner {
462446
},
463447
};
464448

465-
AttrSourceMap {
466-
attrs: collect_attrs(&owner.value)
467-
.map(|attr| InFile::new(owner.file_id, attr))
468-
.collect(),
469-
}
449+
AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn AttrsOwner))
470450
}
471451

472452
pub fn docs_with_rangemap(
@@ -518,7 +498,7 @@ impl AttrsWithOwner {
518498
if buf.is_empty() {
519499
None
520500
} else {
521-
Some((Documentation(buf), DocsRangeMap { mapping, source: self.source_map(db).attrs }))
501+
Some((Documentation(buf), DocsRangeMap { mapping, source_map: self.source_map(db) }))
522502
}
523503
}
524504
}
@@ -559,27 +539,59 @@ fn inner_attributes(
559539
}
560540

561541
pub struct AttrSourceMap {
562-
attrs: Vec<InFile<Either<ast::Attr, ast::Comment>>>,
542+
attrs: Vec<InFile<ast::Attr>>,
543+
doc_comments: Vec<InFile<ast::Comment>>,
563544
}
564545

565546
impl AttrSourceMap {
547+
fn new(owner: InFile<&dyn ast::AttrsOwner>) -> Self {
548+
let mut attrs = Vec::new();
549+
let mut doc_comments = Vec::new();
550+
for (_, attr) in collect_attrs(owner.value) {
551+
match attr {
552+
Either::Left(attr) => attrs.push(owner.with_value(attr)),
553+
Either::Right(comment) => doc_comments.push(owner.with_value(comment)),
554+
}
555+
}
556+
557+
Self { attrs, doc_comments }
558+
}
559+
560+
fn merge(&mut self, other: Self) {
561+
self.attrs.extend(other.attrs);
562+
self.doc_comments.extend(other.doc_comments);
563+
}
564+
566565
/// Maps the lowered `Attr` back to its original syntax node.
567566
///
568567
/// `attr` must come from the `owner` used for AttrSourceMap
569568
///
570569
/// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of
571570
/// the attribute represented by `Attr`.
572-
pub fn source_of(&self, attr: &Attr) -> InFile<&Either<ast::Attr, ast::Comment>> {
573-
self.attrs
574-
.get(attr.id.0 as usize)
575-
.unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", attr.id))
576-
.as_ref()
571+
pub fn source_of(&self, attr: &Attr) -> InFile<Either<ast::Attr, ast::Comment>> {
572+
self.source_of_id(attr.id)
573+
}
574+
575+
fn source_of_id(&self, id: AttrId) -> InFile<Either<ast::Attr, ast::Comment>> {
576+
if id.is_doc_comment {
577+
self.doc_comments
578+
.get(id.ast_index as usize)
579+
.unwrap_or_else(|| panic!("cannot find doc comment at index {:?}", id))
580+
.clone()
581+
.map(|attr| Either::Right(attr))
582+
} else {
583+
self.attrs
584+
.get(id.ast_index as usize)
585+
.unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", id))
586+
.clone()
587+
.map(|attr| Either::Left(attr))
588+
}
577589
}
578590
}
579591

580592
/// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree.
581593
pub struct DocsRangeMap {
582-
source: Vec<InFile<Either<ast::Attr, ast::Comment>>>,
594+
source_map: AttrSourceMap,
583595
// (docstring-line-range, attr_index, attr-string-range)
584596
// a mapping from the text range of a line of the [`Documentation`] to the attribute index and
585597
// the original (untrimmed) syntax doc line
@@ -596,7 +608,7 @@ impl DocsRangeMap {
596608

597609
let relative_range = range - line_docs_range.start();
598610

599-
let &InFile { file_id, value: ref source } = &self.source[idx.0 as usize];
611+
let &InFile { file_id, value: ref source } = &self.source_map.source_of_id(idx);
600612
match source {
601613
Either::Left(_) => None, // FIXME, figure out a nice way to handle doc attributes here
602614
// as well as for whats done in syntax highlight doc injection
@@ -615,6 +627,12 @@ impl DocsRangeMap {
615627
}
616628
}
617629

630+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
631+
pub(crate) struct AttrId {
632+
is_doc_comment: bool,
633+
pub(crate) ast_index: u32,
634+
}
635+
618636
#[derive(Debug, Clone, PartialEq, Eq)]
619637
pub struct Attr {
620638
pub(crate) id: AttrId,
@@ -749,22 +767,32 @@ fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase
749767

750768
fn collect_attrs(
751769
owner: &dyn ast::AttrsOwner,
752-
) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> {
770+
) -> impl Iterator<Item = (AttrId, Either<ast::Attr, ast::Comment>)> {
753771
let (inner_attrs, inner_docs) = inner_attributes(owner.syntax())
754772
.map_or((None, None), |(attrs, docs)| (Some(attrs), Some(docs)));
755773

756774
let outer_attrs = owner.attrs().filter(|attr| attr.kind().is_outer());
757-
let attrs = outer_attrs
758-
.chain(inner_attrs.into_iter().flatten())
759-
.map(|attr| (attr.syntax().text_range().start(), Either::Left(attr)));
775+
let attrs =
776+
outer_attrs.chain(inner_attrs.into_iter().flatten()).enumerate().map(|(idx, attr)| {
777+
(
778+
AttrId { ast_index: idx as u32, is_doc_comment: false },
779+
attr.syntax().text_range().start(),
780+
Either::Left(attr),
781+
)
782+
});
760783

761784
let outer_docs =
762785
ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer);
763-
let docs = outer_docs
764-
.chain(inner_docs.into_iter().flatten())
765-
.map(|docs_text| (docs_text.syntax().text_range().start(), Either::Right(docs_text)));
786+
let docs =
787+
outer_docs.chain(inner_docs.into_iter().flatten()).enumerate().map(|(idx, docs_text)| {
788+
(
789+
AttrId { ast_index: idx as u32, is_doc_comment: true },
790+
docs_text.syntax().text_range().start(),
791+
Either::Right(docs_text),
792+
)
793+
});
766794
// sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
767-
docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).map(|(_, attr)| attr)
795+
docs.chain(attrs).sorted_by_key(|&(_, offset, _)| offset).map(|(id, _, attr)| (id, attr))
768796
}
769797

770798
pub(crate) fn variants_attrs_source_map(

crates/hir_def/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,14 @@ use hir_expand::{
6262
ast_id_map::FileAstId,
6363
eager::{expand_eager_macro, ErrorEmitted, ErrorSink},
6464
hygiene::Hygiene,
65-
AstId, AttrId, FragmentKind, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId,
66-
MacroDefKind,
65+
AstId, FragmentKind, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
6766
};
6867
use la_arena::Idx;
6968
use nameres::DefMap;
7069
use path::ModPath;
7170
use syntax::ast;
7271

72+
use crate::attr::AttrId;
7373
use crate::builtin_type::BuiltinType;
7474
use item_tree::{
7575
Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait,
@@ -753,7 +753,7 @@ fn derive_macro_as_call_id(
753753
MacroCallKind::Derive {
754754
ast_id: item_attr.ast_id,
755755
derive_name: last_segment.to_string(),
756-
derive_attr,
756+
derive_attr_index: derive_attr.ast_index,
757757
},
758758
)
759759
.into();

crates/hir_def/src/nameres/collector.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ use hir_expand::{
1313
builtin_macro::find_builtin_macro,
1414
name::{AsName, Name},
1515
proc_macro::ProcMacroExpander,
16-
AttrId, FragmentKind, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
16+
FragmentKind, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
1717
};
1818
use hir_expand::{InFile, MacroCallLoc};
1919
use rustc_hash::{FxHashMap, FxHashSet};
2020
use syntax::ast;
2121

2222
use crate::{
23-
attr::Attrs,
23+
attr::{AttrId, Attrs},
2424
db::DefDatabase,
2525
derive_macro_as_call_id,
2626
intern::Interned,

crates/hir_expand/src/builtin_derive.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ mod tests {
269269
use expect_test::{expect, Expect};
270270
use name::AsName;
271271

272-
use crate::{test_db::TestDB, AstId, AttrId, MacroCallId, MacroCallKind, MacroCallLoc};
272+
use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
273273

274274
use super::*;
275275

@@ -320,7 +320,7 @@ $0
320320
kind: MacroCallKind::Derive {
321321
ast_id,
322322
derive_name: name.to_string(),
323-
derive_attr: AttrId(0),
323+
derive_attr_index: 0,
324324
},
325325
};
326326

crates/hir_expand/src/db.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ use syntax::{
1212
};
1313

1414
use crate::{
15-
ast_id_map::AstIdMap, hygiene::HygieneFrame, BuiltinDeriveExpander, BuiltinFnLikeExpander,
16-
EagerCallLoc, EagerMacroId, HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc,
17-
MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander,
15+
ast_id_map::AstIdMap, hygiene::HygieneFrame, input::process_macro_input, BuiltinDeriveExpander,
16+
BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId, HirFileId, HirFileIdRepr, LazyMacroId,
17+
MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander,
1818
};
1919

2020
/// Total limit on the number of tokens produced by any macro invocation.
@@ -281,6 +281,7 @@ fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> {
281281
};
282282
let loc = db.lookup_intern_macro(id);
283283
let arg = loc.kind.arg(db)?;
284+
let arg = process_macro_input(db, arg, id);
284285
Some(arg.green().into())
285286
}
286287

0 commit comments

Comments
 (0)