diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-05-11 18:47:45 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2021-05-11 18:47:45 +0100 |
commit | 6afd9b2b8dcfaa9338303f29d8fc6c90dbcdd6e7 (patch) | |
tree | b1534743851c3805c64fe764f9d0bf8f7ab9f7ef /crates | |
parent | 9fa9d166d8141bb9ca4fcf0544c49b903fb85e09 (diff) | |
parent | 8ea9d939d2c44feb71e2d1c8ec390a9471b75e57 (diff) |
Merge #8796
8796: internal: rewrite `#[derive]` removal to be based on AST (take 2) r=jonas-schievink a=jonas-schievink
Second attempt of https://github.com/rust-analyzer/rust-analyzer/pull/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]>
Diffstat (limited to 'crates')
-rw-r--r-- | crates/hir_def/src/attr.rs | 144 | ||||
-rw-r--r-- | crates/hir_def/src/lib.rs | 6 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/collector.rs | 4 | ||||
-rw-r--r-- | crates/hir_expand/src/builtin_derive.rs | 4 | ||||
-rw-r--r-- | crates/hir_expand/src/db.rs | 7 | ||||
-rw-r--r-- | crates/hir_expand/src/input.rs | 94 | ||||
-rw-r--r-- | crates/hir_expand/src/lib.rs | 19 | ||||
-rw-r--r-- | crates/hir_expand/src/proc_macro.rs | 102 |
8 files changed, 205 insertions, 175 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index a2479016e..aadd4e44a 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -9,7 +9,7 @@ use std::{ | |||
9 | use base_db::CrateId; | 9 | use base_db::CrateId; |
10 | use cfg::{CfgExpr, CfgOptions}; | 10 | use cfg::{CfgExpr, CfgOptions}; |
11 | use either::Either; | 11 | use either::Either; |
12 | use hir_expand::{hygiene::Hygiene, name::AsName, AstId, AttrId, InFile}; | 12 | use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile}; |
13 | use itertools::Itertools; | 13 | use itertools::Itertools; |
14 | use la_arena::ArenaMap; | 14 | use la_arena::ArenaMap; |
15 | use mbe::ast_to_token_tree; | 15 | use mbe::ast_to_token_tree; |
@@ -101,17 +101,13 @@ impl RawAttrs { | |||
101 | hygiene: &Hygiene, | 101 | hygiene: &Hygiene, |
102 | ) -> Self { | 102 | ) -> Self { |
103 | let entries = collect_attrs(owner) | 103 | let entries = collect_attrs(owner) |
104 | .enumerate() | 104 | .flat_map(|(id, attr)| match attr { |
105 | .flat_map(|(i, attr)| { | 105 | Either::Left(attr) => Attr::from_src(db, attr, hygiene, id), |
106 | let index = AttrId(i as u32); | 106 | Either::Right(comment) => comment.doc_comment().map(|doc| Attr { |
107 | match attr { | 107 | id, |
108 | Either::Left(attr) => Attr::from_src(db, attr, hygiene, index), | 108 | input: Some(AttrInput::Literal(SmolStr::new(doc))), |
109 | Either::Right(comment) => comment.doc_comment().map(|doc| Attr { | 109 | path: Interned::new(ModPath::from(hir_expand::name!(doc))), |
110 | id: index, | 110 | }), |
111 | input: Some(AttrInput::Literal(SmolStr::new(doc))), | ||
112 | path: Interned::new(ModPath::from(hir_expand::name!(doc))), | ||
113 | }), | ||
114 | } | ||
115 | }) | 111 | }) |
116 | .collect::<Arc<_>>(); | 112 | .collect::<Arc<_>>(); |
117 | 113 | ||
@@ -124,6 +120,7 @@ impl RawAttrs { | |||
124 | } | 120 | } |
125 | 121 | ||
126 | pub(crate) fn merge(&self, other: Self) -> Self { | 122 | pub(crate) fn merge(&self, other: Self) -> Self { |
123 | // FIXME: This needs to fixup `AttrId`s | ||
127 | match (&self.entries, &other.entries) { | 124 | match (&self.entries, &other.entries) { |
128 | (None, None) => Self::EMPTY, | 125 | (None, None) => Self::EMPTY, |
129 | (Some(entries), None) | (None, Some(entries)) => { | 126 | (Some(entries), None) | (None, Some(entries)) => { |
@@ -375,39 +372,26 @@ impl AttrsWithOwner { | |||
375 | 372 | ||
376 | let def_map = module.def_map(db); | 373 | let def_map = module.def_map(db); |
377 | let mod_data = &def_map[module.local_id]; | 374 | let mod_data = &def_map[module.local_id]; |
378 | let attrs = match mod_data.declaration_source(db) { | 375 | match mod_data.declaration_source(db) { |
379 | Some(it) => { | 376 | Some(it) => { |
380 | let mut attrs: Vec<_> = collect_attrs(&it.value as &dyn ast::AttrsOwner) | 377 | let mut map = AttrSourceMap::new(InFile::new(it.file_id, &it.value)); |
381 | .map(|attr| InFile::new(it.file_id, attr)) | ||
382 | .collect(); | ||
383 | if let InFile { file_id, value: ModuleSource::SourceFile(file) } = | 378 | if let InFile { file_id, value: ModuleSource::SourceFile(file) } = |
384 | mod_data.definition_source(db) | 379 | mod_data.definition_source(db) |
385 | { | 380 | { |
386 | attrs.extend( | 381 | map.merge(AttrSourceMap::new(InFile::new(file_id, &file))); |
387 | collect_attrs(&file as &dyn ast::AttrsOwner) | ||
388 | .map(|attr| InFile::new(file_id, attr)), | ||
389 | ) | ||
390 | } | 382 | } |
391 | attrs | 383 | return map; |
392 | } | 384 | } |
393 | None => { | 385 | None => { |
394 | let InFile { file_id, value } = mod_data.definition_source(db); | 386 | let InFile { file_id, value } = mod_data.definition_source(db); |
395 | match &value { | 387 | let attrs_owner = match &value { |
396 | ModuleSource::SourceFile(file) => { | 388 | ModuleSource::SourceFile(file) => file as &dyn ast::AttrsOwner, |
397 | collect_attrs(file as &dyn ast::AttrsOwner) | 389 | ModuleSource::Module(module) => module as &dyn ast::AttrsOwner, |
398 | } | 390 | ModuleSource::BlockExpr(block) => block as &dyn ast::AttrsOwner, |
399 | ModuleSource::Module(module) => { | 391 | }; |
400 | collect_attrs(module as &dyn ast::AttrsOwner) | 392 | return AttrSourceMap::new(InFile::new(file_id, attrs_owner)); |
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() | ||
408 | } | 393 | } |
409 | }; | 394 | } |
410 | return AttrSourceMap { attrs }; | ||
411 | } | 395 | } |
412 | AttrDefId::FieldId(id) => { | 396 | AttrDefId::FieldId(id) => { |
413 | let map = db.fields_attrs_source_map(id.parent); | 397 | let map = db.fields_attrs_source_map(id.parent); |
@@ -462,11 +446,7 @@ impl AttrsWithOwner { | |||
462 | }, | 446 | }, |
463 | }; | 447 | }; |
464 | 448 | ||
465 | AttrSourceMap { | 449 | AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn AttrsOwner)) |
466 | attrs: collect_attrs(&owner.value) | ||
467 | .map(|attr| InFile::new(owner.file_id, attr)) | ||
468 | .collect(), | ||
469 | } | ||
470 | } | 450 | } |
471 | 451 | ||
472 | pub fn docs_with_rangemap( | 452 | pub fn docs_with_rangemap( |
@@ -518,7 +498,7 @@ impl AttrsWithOwner { | |||
518 | if buf.is_empty() { | 498 | if buf.is_empty() { |
519 | None | 499 | None |
520 | } else { | 500 | } 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) })) |
522 | } | 502 | } |
523 | } | 503 | } |
524 | } | 504 | } |
@@ -559,27 +539,59 @@ fn inner_attributes( | |||
559 | } | 539 | } |
560 | 540 | ||
561 | pub struct AttrSourceMap { | 541 | 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>>, | ||
563 | } | 544 | } |
564 | 545 | ||
565 | impl AttrSourceMap { | 546 | 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 | |||
566 | /// Maps the lowered `Attr` back to its original syntax node. | 565 | /// Maps the lowered `Attr` back to its original syntax node. |
567 | /// | 566 | /// |
568 | /// `attr` must come from the `owner` used for AttrSourceMap | 567 | /// `attr` must come from the `owner` used for AttrSourceMap |
569 | /// | 568 | /// |
570 | /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of | 569 | /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of |
571 | /// the attribute represented by `Attr`. | 570 | /// the attribute represented by `Attr`. |
572 | pub fn source_of(&self, attr: &Attr) -> InFile<&Either<ast::Attr, ast::Comment>> { | 571 | pub fn source_of(&self, attr: &Attr) -> InFile<Either<ast::Attr, ast::Comment>> { |
573 | self.attrs | 572 | self.source_of_id(attr.id) |
574 | .get(attr.id.0 as usize) | 573 | } |
575 | .unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", attr.id)) | 574 | |
576 | .as_ref() | 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 | } | ||
577 | } | 589 | } |
578 | } | 590 | } |
579 | 591 | ||
580 | /// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree. | 592 | /// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree. |
581 | pub struct DocsRangeMap { | 593 | pub struct DocsRangeMap { |
582 | source: Vec<InFile<Either<ast::Attr, ast::Comment>>>, | 594 | source_map: AttrSourceMap, |
583 | // (docstring-line-range, attr_index, attr-string-range) | 595 | // (docstring-line-range, attr_index, attr-string-range) |
584 | // a mapping from the text range of a line of the [`Documentation`] to the attribute index and | 596 | // a mapping from the text range of a line of the [`Documentation`] to the attribute index and |
585 | // the original (untrimmed) syntax doc line | 597 | // the original (untrimmed) syntax doc line |
@@ -596,7 +608,7 @@ impl DocsRangeMap { | |||
596 | 608 | ||
597 | let relative_range = range - line_docs_range.start(); | 609 | let relative_range = range - line_docs_range.start(); |
598 | 610 | ||
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); |
600 | match source { | 612 | match source { |
601 | Either::Left(_) => None, // FIXME, figure out a nice way to handle doc attributes here | 613 | Either::Left(_) => None, // FIXME, figure out a nice way to handle doc attributes here |
602 | // as well as for whats done in syntax highlight doc injection | 614 | // as well as for whats done in syntax highlight doc injection |
@@ -615,6 +627,12 @@ impl DocsRangeMap { | |||
615 | } | 627 | } |
616 | } | 628 | } |
617 | 629 | ||
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 | |||
618 | #[derive(Debug, Clone, PartialEq, Eq)] | 636 | #[derive(Debug, Clone, PartialEq, Eq)] |
619 | pub struct Attr { | 637 | pub struct Attr { |
620 | pub(crate) id: AttrId, | 638 | pub(crate) id: AttrId, |
@@ -749,22 +767,32 @@ fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase | |||
749 | 767 | ||
750 | fn collect_attrs( | 768 | fn collect_attrs( |
751 | owner: &dyn ast::AttrsOwner, | 769 | owner: &dyn ast::AttrsOwner, |
752 | ) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> { | 770 | ) -> impl Iterator<Item = (AttrId, Either<ast::Attr, ast::Comment>)> { |
753 | let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) | 771 | let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) |
754 | .map_or((None, None), |(attrs, docs)| (Some(attrs), Some(docs))); | 772 | .map_or((None, None), |(attrs, docs)| (Some(attrs), Some(docs))); |
755 | 773 | ||
756 | let outer_attrs = owner.attrs().filter(|attr| attr.kind().is_outer()); | 774 | let outer_attrs = owner.attrs().filter(|attr| attr.kind().is_outer()); |
757 | let attrs = outer_attrs | 775 | let attrs = |
758 | .chain(inner_attrs.into_iter().flatten()) | 776 | outer_attrs.chain(inner_attrs.into_iter().flatten()).enumerate().map(|(idx, attr)| { |
759 | .map(|attr| (attr.syntax().text_range().start(), Either::Left(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 | }); | ||
760 | 783 | ||
761 | let outer_docs = | 784 | let outer_docs = |
762 | ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer); | 785 | ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer); |
763 | let docs = outer_docs | 786 | let docs = |
764 | .chain(inner_docs.into_iter().flatten()) | 787 | outer_docs.chain(inner_docs.into_iter().flatten()).enumerate().map(|(idx, docs_text)| { |
765 | .map(|docs_text| (docs_text.syntax().text_range().start(), Either::Right(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 | }); | ||
766 | // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved | 794 | // 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)) |
768 | } | 796 | } |
769 | 797 | ||
770 | pub(crate) fn variants_attrs_source_map( | 798 | pub(crate) fn variants_attrs_source_map( |
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index e96ca953f..a82ea5957 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs | |||
@@ -62,14 +62,14 @@ use hir_expand::{ | |||
62 | ast_id_map::FileAstId, | 62 | ast_id_map::FileAstId, |
63 | eager::{expand_eager_macro, ErrorEmitted, ErrorSink}, | 63 | eager::{expand_eager_macro, ErrorEmitted, ErrorSink}, |
64 | hygiene::Hygiene, | 64 | hygiene::Hygiene, |
65 | AstId, AttrId, FragmentKind, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, | 65 | AstId, FragmentKind, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, |
66 | MacroDefKind, | ||
67 | }; | 66 | }; |
68 | use la_arena::Idx; | 67 | use la_arena::Idx; |
69 | use nameres::DefMap; | 68 | use nameres::DefMap; |
70 | use path::ModPath; | 69 | use path::ModPath; |
71 | use syntax::ast; | 70 | use syntax::ast; |
72 | 71 | ||
72 | use crate::attr::AttrId; | ||
73 | use crate::builtin_type::BuiltinType; | 73 | use crate::builtin_type::BuiltinType; |
74 | use item_tree::{ | 74 | use item_tree::{ |
75 | Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait, | 75 | Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait, |
@@ -753,7 +753,7 @@ fn derive_macro_as_call_id( | |||
753 | MacroCallKind::Derive { | 753 | MacroCallKind::Derive { |
754 | ast_id: item_attr.ast_id, | 754 | ast_id: item_attr.ast_id, |
755 | derive_name: last_segment.to_string(), | 755 | derive_name: last_segment.to_string(), |
756 | derive_attr, | 756 | derive_attr_index: derive_attr.ast_index, |
757 | }, | 757 | }, |
758 | ) | 758 | ) |
759 | .into(); | 759 | .into(); |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index e89136ed1..adfb78c94 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -13,14 +13,14 @@ use hir_expand::{ | |||
13 | builtin_macro::find_builtin_macro, | 13 | builtin_macro::find_builtin_macro, |
14 | name::{AsName, Name}, | 14 | name::{AsName, Name}, |
15 | proc_macro::ProcMacroExpander, | 15 | proc_macro::ProcMacroExpander, |
16 | AttrId, FragmentKind, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, | 16 | FragmentKind, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, |
17 | }; | 17 | }; |
18 | use hir_expand::{InFile, MacroCallLoc}; | 18 | use hir_expand::{InFile, MacroCallLoc}; |
19 | use rustc_hash::{FxHashMap, FxHashSet}; | 19 | use rustc_hash::{FxHashMap, FxHashSet}; |
20 | use syntax::ast; | 20 | use syntax::ast; |
21 | 21 | ||
22 | use crate::{ | 22 | use crate::{ |
23 | attr::Attrs, | 23 | attr::{AttrId, Attrs}, |
24 | db::DefDatabase, | 24 | db::DefDatabase, |
25 | derive_macro_as_call_id, | 25 | derive_macro_as_call_id, |
26 | intern::Interned, | 26 | intern::Interned, |
diff --git a/crates/hir_expand/src/builtin_derive.rs b/crates/hir_expand/src/builtin_derive.rs index 537c03028..b6a6d602f 100644 --- a/crates/hir_expand/src/builtin_derive.rs +++ b/crates/hir_expand/src/builtin_derive.rs | |||
@@ -269,7 +269,7 @@ mod tests { | |||
269 | use expect_test::{expect, Expect}; | 269 | use expect_test::{expect, Expect}; |
270 | use name::AsName; | 270 | use name::AsName; |
271 | 271 | ||
272 | use crate::{test_db::TestDB, AstId, AttrId, MacroCallId, MacroCallKind, MacroCallLoc}; | 272 | use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc}; |
273 | 273 | ||
274 | use super::*; | 274 | use super::*; |
275 | 275 | ||
@@ -320,7 +320,7 @@ $0 | |||
320 | kind: MacroCallKind::Derive { | 320 | kind: MacroCallKind::Derive { |
321 | ast_id, | 321 | ast_id, |
322 | derive_name: name.to_string(), | 322 | derive_name: name.to_string(), |
323 | derive_attr: AttrId(0), | 323 | derive_attr_index: 0, |
324 | }, | 324 | }, |
325 | }; | 325 | }; |
326 | 326 | ||
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index 6647e57e7..9fa419fcf 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -12,9 +12,9 @@ use syntax::{ | |||
12 | }; | 12 | }; |
13 | 13 | ||
14 | use crate::{ | 14 | use crate::{ |
15 | ast_id_map::AstIdMap, hygiene::HygieneFrame, BuiltinDeriveExpander, BuiltinFnLikeExpander, | 15 | ast_id_map::AstIdMap, hygiene::HygieneFrame, input::process_macro_input, BuiltinDeriveExpander, |
16 | EagerCallLoc, EagerMacroId, HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc, | 16 | BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId, HirFileId, HirFileIdRepr, LazyMacroId, |
17 | MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander, | 17 | MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander, |
18 | }; | 18 | }; |
19 | 19 | ||
20 | /// Total limit on the number of tokens produced by any macro invocation. | 20 | /// 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> { | |||
281 | }; | 281 | }; |
282 | let loc = db.lookup_intern_macro(id); | 282 | let loc = db.lookup_intern_macro(id); |
283 | let arg = loc.kind.arg(db)?; | 283 | let arg = loc.kind.arg(db)?; |
284 | let arg = process_macro_input(db, arg, id); | ||
284 | Some(arg.green().into()) | 285 | Some(arg.green().into()) |
285 | } | 286 | } |
286 | 287 | ||
diff --git a/crates/hir_expand/src/input.rs b/crates/hir_expand/src/input.rs new file mode 100644 index 000000000..112216859 --- /dev/null +++ b/crates/hir_expand/src/input.rs | |||
@@ -0,0 +1,94 @@ | |||
1 | //! Macro input conditioning. | ||
2 | |||
3 | use syntax::{ | ||
4 | ast::{self, AttrsOwner}, | ||
5 | AstNode, SyntaxNode, | ||
6 | }; | ||
7 | |||
8 | use crate::{ | ||
9 | db::AstDatabase, | ||
10 | name::{name, AsName}, | ||
11 | LazyMacroId, MacroCallKind, MacroCallLoc, | ||
12 | }; | ||
13 | |||
14 | pub(crate) fn process_macro_input( | ||
15 | db: &dyn AstDatabase, | ||
16 | node: SyntaxNode, | ||
17 | id: LazyMacroId, | ||
18 | ) -> SyntaxNode { | ||
19 | let loc: MacroCallLoc = db.lookup_intern_macro(id); | ||
20 | |||
21 | match loc.kind { | ||
22 | MacroCallKind::FnLike { .. } => node, | ||
23 | MacroCallKind::Derive { derive_attr_index, .. } => { | ||
24 | let item = match ast::Item::cast(node.clone()) { | ||
25 | Some(item) => item, | ||
26 | None => return node, | ||
27 | }; | ||
28 | |||
29 | remove_derives_up_to(item, derive_attr_index as usize).syntax().clone() | ||
30 | } | ||
31 | } | ||
32 | } | ||
33 | |||
34 | /// Removes `#[derive]` attributes from `item`, up to `attr_index`. | ||
35 | fn remove_derives_up_to(item: ast::Item, attr_index: usize) -> ast::Item { | ||
36 | let item = item.clone_for_update(); | ||
37 | for attr in item.attrs().take(attr_index + 1) { | ||
38 | if let Some(name) = | ||
39 | attr.path().and_then(|path| path.as_single_segment()).and_then(|seg| seg.name_ref()) | ||
40 | { | ||
41 | if name.as_name() == name![derive] { | ||
42 | attr.syntax().detach(); | ||
43 | } | ||
44 | } | ||
45 | } | ||
46 | item | ||
47 | } | ||
48 | |||
49 | #[cfg(test)] | ||
50 | mod tests { | ||
51 | use base_db::fixture::WithFixture; | ||
52 | use base_db::SourceDatabase; | ||
53 | use expect_test::{expect, Expect}; | ||
54 | |||
55 | use crate::test_db::TestDB; | ||
56 | |||
57 | use super::*; | ||
58 | |||
59 | fn test_remove_derives_up_to(attr: usize, ra_fixture: &str, expect: Expect) { | ||
60 | let (db, file_id) = TestDB::with_single_file(&ra_fixture); | ||
61 | let parsed = db.parse(file_id); | ||
62 | |||
63 | let mut items: Vec<_> = | ||
64 | parsed.syntax_node().descendants().filter_map(ast::Item::cast).collect(); | ||
65 | assert_eq!(items.len(), 1); | ||
66 | |||
67 | let item = remove_derives_up_to(items.pop().unwrap(), attr); | ||
68 | expect.assert_eq(&item.to_string()); | ||
69 | } | ||
70 | |||
71 | #[test] | ||
72 | fn remove_derive() { | ||
73 | test_remove_derives_up_to( | ||
74 | 2, | ||
75 | r#" | ||
76 | #[allow(unused)] | ||
77 | #[derive(Copy)] | ||
78 | #[derive(Hello)] | ||
79 | #[derive(Clone)] | ||
80 | struct A { | ||
81 | bar: u32 | ||
82 | } | ||
83 | "#, | ||
84 | expect![[r#" | ||
85 | #[allow(unused)] | ||
86 | |||
87 | |||
88 | #[derive(Clone)] | ||
89 | struct A { | ||
90 | bar: u32 | ||
91 | }"#]], | ||
92 | ); | ||
93 | } | ||
94 | } | ||
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index 80ab3aeee..5df11856e 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs | |||
@@ -14,6 +14,7 @@ pub mod builtin_macro; | |||
14 | pub mod proc_macro; | 14 | pub mod proc_macro; |
15 | pub mod quote; | 15 | pub mod quote; |
16 | pub mod eager; | 16 | pub mod eager; |
17 | mod input; | ||
17 | 18 | ||
18 | use either::Either; | 19 | use either::Either; |
19 | 20 | ||
@@ -292,13 +293,21 @@ pub struct MacroCallLoc { | |||
292 | 293 | ||
293 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 294 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
294 | pub enum MacroCallKind { | 295 | pub enum MacroCallKind { |
295 | FnLike { ast_id: AstId<ast::MacroCall>, fragment: FragmentKind }, | 296 | FnLike { |
296 | Derive { ast_id: AstId<ast::Item>, derive_name: String, derive_attr: AttrId }, | 297 | ast_id: AstId<ast::MacroCall>, |
298 | fragment: FragmentKind, | ||
299 | }, | ||
300 | Derive { | ||
301 | ast_id: AstId<ast::Item>, | ||
302 | derive_name: String, | ||
303 | /// Syntactical index of the invoking `#[derive]` attribute. | ||
304 | /// | ||
305 | /// Outer attributes are counted first, then inner attributes. This does not support | ||
306 | /// out-of-line modules, which may have attributes spread across 2 files! | ||
307 | derive_attr_index: u32, | ||
308 | }, | ||
297 | } | 309 | } |
298 | 310 | ||
299 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
300 | pub struct AttrId(pub u32); | ||
301 | |||
302 | impl MacroCallKind { | 311 | impl MacroCallKind { |
303 | fn file_id(&self) -> HirFileId { | 312 | fn file_id(&self) -> HirFileId { |
304 | match self { | 313 | match self { |
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs index 75e950816..d5643393a 100644 --- a/crates/hir_expand/src/proc_macro.rs +++ b/crates/hir_expand/src/proc_macro.rs | |||
@@ -2,7 +2,6 @@ | |||
2 | 2 | ||
3 | use crate::db::AstDatabase; | 3 | use crate::db::AstDatabase; |
4 | use base_db::{CrateId, ProcMacroId}; | 4 | use base_db::{CrateId, ProcMacroId}; |
5 | use tt::buffer::{Cursor, TokenBuffer}; | ||
6 | 5 | ||
7 | #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] | 6 | #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] |
8 | pub struct ProcMacroExpander { | 7 | pub struct ProcMacroExpander { |
@@ -44,9 +43,6 @@ impl ProcMacroExpander { | |||
44 | .clone() | 43 | .clone() |
45 | .ok_or_else(|| err!("No derive macro found."))?; | 44 | .ok_or_else(|| err!("No derive macro found."))?; |
46 | 45 | ||
47 | let tt = remove_derive_attrs(tt) | ||
48 | .ok_or_else(|| err!("Fail to remove derive for custom derive"))?; | ||
49 | |||
50 | // Proc macros have access to the environment variables of the invoking crate. | 46 | // Proc macros have access to the environment variables of the invoking crate. |
51 | let env = &krate_graph[calling_crate].env; | 47 | let env = &krate_graph[calling_crate].env; |
52 | 48 | ||
@@ -56,101 +52,3 @@ impl ProcMacroExpander { | |||
56 | } | 52 | } |
57 | } | 53 | } |
58 | } | 54 | } |
59 | |||
60 | fn eat_punct(cursor: &mut Cursor, c: char) -> bool { | ||
61 | if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(punct), _)) = cursor.token_tree() { | ||
62 | if punct.char == c { | ||
63 | *cursor = cursor.bump(); | ||
64 | return true; | ||
65 | } | ||
66 | } | ||
67 | false | ||
68 | } | ||
69 | |||
70 | fn eat_subtree(cursor: &mut Cursor, kind: tt::DelimiterKind) -> bool { | ||
71 | if let Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) = cursor.token_tree() { | ||
72 | if Some(kind) == subtree.delimiter_kind() { | ||
73 | *cursor = cursor.bump_subtree(); | ||
74 | return true; | ||
75 | } | ||
76 | } | ||
77 | false | ||
78 | } | ||
79 | |||
80 | fn eat_ident(cursor: &mut Cursor, t: &str) -> bool { | ||
81 | if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Ident(ident), _)) = cursor.token_tree() { | ||
82 | if t == ident.text.as_str() { | ||
83 | *cursor = cursor.bump(); | ||
84 | return true; | ||
85 | } | ||
86 | } | ||
87 | false | ||
88 | } | ||
89 | |||
90 | fn remove_derive_attrs(tt: &tt::Subtree) -> Option<tt::Subtree> { | ||
91 | let buffer = TokenBuffer::from_tokens(&tt.token_trees); | ||
92 | let mut p = buffer.begin(); | ||
93 | let mut result = tt::Subtree::default(); | ||
94 | |||
95 | while !p.eof() { | ||
96 | let curr = p; | ||
97 | |||
98 | if eat_punct(&mut p, '#') { | ||
99 | eat_punct(&mut p, '!'); | ||
100 | let parent = p; | ||
101 | if eat_subtree(&mut p, tt::DelimiterKind::Bracket) { | ||
102 | if eat_ident(&mut p, "derive") { | ||
103 | p = parent.bump(); | ||
104 | continue; | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | |||
109 | result.token_trees.push(curr.token_tree()?.cloned()); | ||
110 | p = curr.bump(); | ||
111 | } | ||
112 | |||
113 | Some(result) | ||
114 | } | ||
115 | |||
116 | #[cfg(test)] | ||
117 | mod tests { | ||
118 | use super::*; | ||
119 | use test_utils::assert_eq_text; | ||
120 | |||
121 | #[test] | ||
122 | fn test_remove_derive_attrs() { | ||
123 | let tt = mbe::parse_to_token_tree( | ||
124 | r#" | ||
125 | #[allow(unused)] | ||
126 | #[derive(Copy)] | ||
127 | #[derive(Hello)] | ||
128 | struct A { | ||
129 | bar: u32 | ||
130 | } | ||
131 | "#, | ||
132 | ) | ||
133 | .unwrap() | ||
134 | .0; | ||
135 | let result = format!("{:#?}", remove_derive_attrs(&tt).unwrap()); | ||
136 | |||
137 | assert_eq_text!( | ||
138 | r#" | ||
139 | SUBTREE $ | ||
140 | PUNCH # [alone] 0 | ||
141 | SUBTREE [] 1 | ||
142 | IDENT allow 2 | ||
143 | SUBTREE () 3 | ||
144 | IDENT unused 4 | ||
145 | IDENT struct 15 | ||
146 | IDENT A 16 | ||
147 | SUBTREE {} 17 | ||
148 | IDENT bar 18 | ||
149 | PUNCH : [alone] 19 | ||
150 | IDENT u32 20 | ||
151 | "# | ||
152 | .trim(), | ||
153 | &result | ||
154 | ); | ||
155 | } | ||
156 | } | ||