aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-06-03 17:17:25 +0100
committerGitHub <[email protected]>2021-06-03 17:17:25 +0100
commit14153671caaca852c94bd1d0d7f279acb8eb1913 (patch)
treeeb377999a5e8677c210791440b18593f909198e6 /crates/hir_def
parent7f9c4a59d9a84cd8c734286937439b5cd215be27 (diff)
parent17565f4deafab800d8d87208cff1e27d028e9b0e (diff)
Merge #9128
9128: feat: expand procedural attribute macros r=jonas-schievink a=jonas-schievink This adds experimental support for attribute macros. They can be enabled by setting `rust-analyzer.experimental.procAttrMacros` to `true`. Known issues: * Tokens aren't remapped, presumably because we edit the input syntax tree (this causes IDE features to not work inside items with attribute macros on them) * Macro errors aren't reported correctly Closes https://github.com/rust-analyzer/rust-analyzer/issues/8971 Fixes https://github.com/rust-analyzer/rust-analyzer/issues/8964 / https://github.com/la10736/rstest/issues/120 Fixes https://github.com/rust-analyzer/rust-analyzer/issues/2984 Fixes https://github.com/rust-analyzer/rust-analyzer/issues/5412 Fixes https://github.com/rust-analyzer/rust-analyzer/issues/6029 Fixes https://github.com/rust-analyzer/rust-analyzer/issues/6687 https://github.com/rust-analyzer/rust-analyzer/issues/6740 is still not fixed – we now expand `#[proc_macro_hack]`, but fail to expand the resulting `proc_macro_call!()` macro. Co-authored-by: Jonas Schievink <[email protected]>
Diffstat (limited to 'crates/hir_def')
-rw-r--r--crates/hir_def/src/builtin_attr.rs38
-rw-r--r--crates/hir_def/src/db.rs3
-rw-r--r--crates/hir_def/src/lib.rs40
-rw-r--r--crates/hir_def/src/nameres/collector.rs60
-rw-r--r--crates/hir_def/src/test_db.rs9
5 files changed, 127 insertions, 23 deletions
diff --git a/crates/hir_def/src/builtin_attr.rs b/crates/hir_def/src/builtin_attr.rs
index d5d7f0f47..39c7f84f7 100644
--- a/crates/hir_def/src/builtin_attr.rs
+++ b/crates/hir_def/src/builtin_attr.rs
@@ -2,7 +2,7 @@
2//! 2//!
3//! The actual definitions were copied from rustc's `compiler/rustc_feature/src/builtin_attrs.rs`. 3//! The actual definitions were copied from rustc's `compiler/rustc_feature/src/builtin_attrs.rs`.
4//! 4//!
5//! It was last synchronized with upstream commit 2225ee1b62ff089917434aefd9b2bf509cfa087f. 5//! It was last synchronized with upstream commit 835150e70288535bc57bb624792229b9dc94991d.
6//! 6//!
7//! The macros were adjusted to only expand to the attribute name, since that is all we need to do 7//! The macros were adjusted to only expand to the attribute name, since that is all we need to do
8//! name resolution, and `BUILTIN_ATTRIBUTES` is almost entirely unchanged from the original, to 8//! name resolution, and `BUILTIN_ATTRIBUTES` is almost entirely unchanged from the original, to
@@ -58,7 +58,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
58 ungated!(reexport_test_harness_main, Normal, template!(NameValueStr: "name")), 58 ungated!(reexport_test_harness_main, Normal, template!(NameValueStr: "name")),
59 59
60 // Macros: 60 // Macros:
61 ungated!(derive, Normal, template!(List: "Trait1, Trait2, ...")),
62 ungated!(automatically_derived, Normal, template!(Word)), 61 ungated!(automatically_derived, Normal, template!(Word)),
63 // FIXME(#14407) 62 // FIXME(#14407)
64 ungated!(macro_use, Normal, template!(Word, List: "name1, name2, ...")), 63 ungated!(macro_use, Normal, template!(Word, List: "name1, name2, ...")),
@@ -98,8 +97,8 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
98 template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...""#), 97 template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...""#),
99 ), 98 ),
100 ungated!(link_name, AssumedUsed, template!(NameValueStr: "name")), 99 ungated!(link_name, AssumedUsed, template!(NameValueStr: "name")),
101 ungated!(no_link, Normal, template!(Word)), 100 ungated!(no_link, AssumedUsed, template!(Word)),
102 ungated!(repr, Normal, template!(List: "C")), 101 ungated!(repr, AssumedUsed, template!(List: "C")),
103 ungated!(export_name, AssumedUsed, template!(NameValueStr: "name")), 102 ungated!(export_name, AssumedUsed, template!(NameValueStr: "name")),
104 ungated!(link_section, AssumedUsed, template!(NameValueStr: "name")), 103 ungated!(link_section, AssumedUsed, template!(NameValueStr: "name")),
105 ungated!(no_mangle, AssumedUsed, template!(Word)), 104 ungated!(no_mangle, AssumedUsed, template!(Word)),
@@ -112,6 +111,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
112 const_eval_limit, CrateLevel, template!(NameValueStr: "N"), const_eval_limit, 111 const_eval_limit, CrateLevel, template!(NameValueStr: "N"), const_eval_limit,
113 experimental!(const_eval_limit) 112 experimental!(const_eval_limit)
114 ), 113 ),
114 gated!(
115 move_size_limit, CrateLevel, template!(NameValueStr: "N"), large_assignments,
116 experimental!(move_size_limit)
117 ),
115 118
116 // Entry point: 119 // Entry point:
117 ungated!(main, Normal, template!(Word)), 120 ungated!(main, Normal, template!(Word)),
@@ -140,6 +143,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
140 template!(List: "address, memory, thread"), 143 template!(List: "address, memory, thread"),
141 experimental!(no_sanitize) 144 experimental!(no_sanitize)
142 ), 145 ),
146 gated!(no_coverage, AssumedUsed, template!(Word), experimental!(no_coverage)),
143 147
144 // FIXME: #14408 assume docs are used since rustdoc looks at them. 148 // FIXME: #14408 assume docs are used since rustdoc looks at them.
145 ungated!(doc, AssumedUsed, template!(List: "hidden|inline|...", NameValueStr: "string")), 149 ungated!(doc, AssumedUsed, template!(List: "hidden|inline|...", NameValueStr: "string")),
@@ -151,11 +155,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
151 // Linking: 155 // Linking:
152 gated!(naked, AssumedUsed, template!(Word), naked_functions, experimental!(naked)), 156 gated!(naked, AssumedUsed, template!(Word), naked_functions, experimental!(naked)),
153 gated!( 157 gated!(
154 link_args, Normal, template!(NameValueStr: "args"),
155 "the `link_args` attribute is experimental and not portable across platforms, \
156 it is recommended to use `#[link(name = \"foo\")] instead",
157 ),
158 gated!(
159 link_ordinal, AssumedUsed, template!(List: "ordinal"), raw_dylib, 158 link_ordinal, AssumedUsed, template!(List: "ordinal"), raw_dylib,
160 experimental!(link_ordinal) 159 experimental!(link_ordinal)
161 ), 160 ),
@@ -172,7 +171,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
172 "custom test frameworks are an unstable feature", 171 "custom test frameworks are an unstable feature",
173 ), 172 ),
174 // RFC #1268 173 // RFC #1268
175 gated!(marker, Normal, template!(Word), marker_trait_attr, experimental!(marker)), 174 gated!(marker, AssumedUsed, template!(Word), marker_trait_attr, experimental!(marker)),
176 gated!( 175 gated!(
177 thread_local, AssumedUsed, template!(Word), 176 thread_local, AssumedUsed, template!(Word),
178 "`#[thread_local]` is an experimental feature, and does not currently handle destructors", 177 "`#[thread_local]` is an experimental feature, and does not currently handle destructors",
@@ -291,7 +290,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
291 // Internal attributes, Macro related: 290 // Internal attributes, Macro related:
292 // ========================================================================== 291 // ==========================================================================
293 292
294 rustc_attr!(rustc_builtin_macro, AssumedUsed, template!(Word), IMPL_DETAIL), 293 rustc_attr!(rustc_builtin_macro, AssumedUsed, template!(Word, NameValueStr: "name"), IMPL_DETAIL),
295 rustc_attr!(rustc_proc_macro_decls, Normal, template!(Word), INTERNAL_UNSTABLE), 294 rustc_attr!(rustc_proc_macro_decls, Normal, template!(Word), INTERNAL_UNSTABLE),
296 rustc_attr!( 295 rustc_attr!(
297 rustc_macro_transparency, AssumedUsed, 296 rustc_macro_transparency, AssumedUsed,
@@ -319,7 +318,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
319 // ========================================================================== 318 // ==========================================================================
320 319
321 rustc_attr!(rustc_promotable, AssumedUsed, template!(Word), IMPL_DETAIL), 320 rustc_attr!(rustc_promotable, AssumedUsed, template!(Word), IMPL_DETAIL),
322 rustc_attr!(rustc_args_required_const, AssumedUsed, template!(List: "N"), INTERNAL_UNSTABLE), 321 rustc_attr!(rustc_legacy_const_generics, AssumedUsed, template!(List: "N"), INTERNAL_UNSTABLE),
323 322
324 // ========================================================================== 323 // ==========================================================================
325 // Internal attributes, Layout related: 324 // Internal attributes, Layout related:
@@ -380,6 +379,15 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
380 rustc_specialization_trait, Normal, template!(Word), 379 rustc_specialization_trait, Normal, template!(Word),
381 "the `#[rustc_specialization_trait]` attribute is used to check specializations" 380 "the `#[rustc_specialization_trait]` attribute is used to check specializations"
382 ), 381 ),
382 rustc_attr!(
383 rustc_main, Normal, template!(Word),
384 "the `#[rustc_main]` attribute is used internally to specify test entry point function",
385 ),
386 rustc_attr!(
387 rustc_skip_array_during_method_dispatch, Normal, template!(Word),
388 "the `#[rustc_skip_array_during_method_dispatch]` attribute is used to exclude a trait \
389 from method dispatch when the receiver is an array, for compatibility in editions < 2021."
390 ),
383 391
384 // ========================================================================== 392 // ==========================================================================
385 // Internal attributes, Testing: 393 // Internal attributes, Testing:
@@ -387,6 +395,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
387 395
388 rustc_attr!(TEST, rustc_outlives, Normal, template!(Word)), 396 rustc_attr!(TEST, rustc_outlives, Normal, template!(Word)),
389 rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word)), 397 rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word)),
398 rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word)),
390 rustc_attr!(TEST, rustc_variance, Normal, template!(Word)), 399 rustc_attr!(TEST, rustc_variance, Normal, template!(Word)),
391 rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ...")), 400 rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ...")),
392 rustc_attr!(TEST, rustc_regions, Normal, template!(Word)), 401 rustc_attr!(TEST, rustc_regions, Normal, template!(Word)),
@@ -395,13 +404,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
395 template!(Word, List: "delay_span_bug_from_inside_query") 404 template!(Word, List: "delay_span_bug_from_inside_query")
396 ), 405 ),
397 rustc_attr!(TEST, rustc_dump_user_substs, AssumedUsed, template!(Word)), 406 rustc_attr!(TEST, rustc_dump_user_substs, AssumedUsed, template!(Word)),
407 rustc_attr!(TEST, rustc_evaluate_where_clauses, AssumedUsed, template!(Word)),
398 rustc_attr!(TEST, rustc_if_this_changed, AssumedUsed, template!(Word, List: "DepNode")), 408 rustc_attr!(TEST, rustc_if_this_changed, AssumedUsed, template!(Word, List: "DepNode")),
399 rustc_attr!(TEST, rustc_then_this_would_need, AssumedUsed, template!(List: "DepNode")), 409 rustc_attr!(TEST, rustc_then_this_would_need, AssumedUsed, template!(List: "DepNode")),
400 rustc_attr!( 410 rustc_attr!(
401 TEST, rustc_dirty, AssumedUsed,
402 template!(List: r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#),
403 ),
404 rustc_attr!(
405 TEST, rustc_clean, AssumedUsed, 411 TEST, rustc_clean, AssumedUsed,
406 template!(List: r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#), 412 template!(List: r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#),
407 ), 413 ),
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs
index 7eadc8e0d..c977971cd 100644
--- a/crates/hir_def/src/db.rs
+++ b/crates/hir_def/src/db.rs
@@ -51,6 +51,9 @@ pub trait InternDatabase: SourceDatabase {
51 51
52#[salsa::query_group(DefDatabaseStorage)] 52#[salsa::query_group(DefDatabaseStorage)]
53pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { 53pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
54 #[salsa::input]
55 fn enable_proc_attr_macros(&self) -> bool;
56
54 #[salsa::invoke(ItemTree::file_item_tree_query)] 57 #[salsa::invoke(ItemTree::file_item_tree_query)]
55 fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>; 58 fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>;
56 59
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index 9aa95720a..987485acc 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -55,6 +55,7 @@ use std::{
55 sync::Arc, 55 sync::Arc,
56}; 56};
57 57
58use attr::Attr;
58use base_db::{impl_intern_key, salsa, CrateId}; 59use base_db::{impl_intern_key, salsa, CrateId};
59use hir_expand::{ 60use hir_expand::{
60 ast_id_map::FileAstId, 61 ast_id_map::FileAstId,
@@ -768,3 +769,42 @@ fn derive_macro_as_call_id(
768 .into(); 769 .into();
769 Ok(res) 770 Ok(res)
770} 771}
772
773fn attr_macro_as_call_id(
774 item_attr: &AstIdWithPath<ast::Item>,
775 macro_attr: &Attr,
776 db: &dyn db::DefDatabase,
777 krate: CrateId,
778 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
779) -> Result<MacroCallId, UnresolvedMacro> {
780 let def: MacroDefId = resolver(item_attr.path.clone())
781 .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
782 let last_segment = item_attr
783 .path
784 .segments()
785 .last()
786 .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
787 let mut arg = match &macro_attr.input {
788 Some(input) => match &**input {
789 attr::AttrInput::Literal(_) => tt::Subtree::default(),
790 attr::AttrInput::TokenTree(tt) => tt.clone(),
791 },
792 None => tt::Subtree::default(),
793 };
794 // The parentheses are always disposed here.
795 arg.delimiter = None;
796
797 let res = def
798 .as_lazy_macro(
799 db.upcast(),
800 krate,
801 MacroCallKind::Attr {
802 ast_id: item_attr.ast_id,
803 attr_name: last_segment.to_string(),
804 attr_args: arg,
805 invoc_attr_index: macro_attr.id.ast_index,
806 },
807 )
808 .into();
809 Ok(res)
810}
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 6b41921ae..b2ce739bd 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -23,7 +23,7 @@ use syntax::ast;
23 23
24use crate::{ 24use crate::{
25 attr::{Attr, AttrId, AttrInput, Attrs}, 25 attr::{Attr, AttrId, AttrInput, Attrs},
26 builtin_attr, 26 attr_macro_as_call_id, builtin_attr,
27 db::DefDatabase, 27 db::DefDatabase,
28 derive_macro_as_call_id, 28 derive_macro_as_call_id,
29 intern::Interned, 29 intern::Interned,
@@ -223,7 +223,7 @@ struct MacroDirective {
223enum MacroDirectiveKind { 223enum MacroDirectiveKind {
224 FnLike { ast_id: AstIdWithPath<ast::MacroCall>, fragment: FragmentKind }, 224 FnLike { ast_id: AstIdWithPath<ast::MacroCall>, fragment: FragmentKind },
225 Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId }, 225 Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId },
226 Attr { ast_id: AstIdWithPath<ast::Item>, attr: AttrId, mod_item: ModItem }, 226 Attr { ast_id: AstIdWithPath<ast::Item>, attr: Attr, mod_item: ModItem },
227} 227}
228 228
229struct DefData<'a> { 229struct DefData<'a> {
@@ -419,7 +419,7 @@ impl DefCollector<'_> {
419 let mut unresolved_macros = std::mem::replace(&mut self.unresolved_macros, Vec::new()); 419 let mut unresolved_macros = std::mem::replace(&mut self.unresolved_macros, Vec::new());
420 let pos = unresolved_macros.iter().position(|directive| { 420 let pos = unresolved_macros.iter().position(|directive| {
421 if let MacroDirectiveKind::Attr { ast_id, mod_item, attr } = &directive.kind { 421 if let MacroDirectiveKind::Attr { ast_id, mod_item, attr } = &directive.kind {
422 self.skip_attrs.insert(ast_id.ast_id.with_value(*mod_item), *attr); 422 self.skip_attrs.insert(ast_id.ast_id.with_value(*mod_item), attr.id);
423 423
424 let file_id = ast_id.ast_id.file_id; 424 let file_id = ast_id.ast_id.file_id;
425 let item_tree = self.db.file_item_tree(file_id); 425 let item_tree = self.db.file_item_tree(file_id);
@@ -1050,7 +1050,7 @@ impl DefCollector<'_> {
1050 let file_id = ast_id.ast_id.file_id; 1050 let file_id = ast_id.ast_id.file_id;
1051 let item_tree = self.db.file_item_tree(file_id); 1051 let item_tree = self.db.file_item_tree(file_id);
1052 let mod_dir = self.mod_dirs[&directive.module_id].clone(); 1052 let mod_dir = self.mod_dirs[&directive.module_id].clone();
1053 self.skip_attrs.insert(InFile::new(file_id, *mod_item), *attr); 1053 self.skip_attrs.insert(InFile::new(file_id, *mod_item), attr.id);
1054 ModCollector { 1054 ModCollector {
1055 def_collector: &mut *self, 1055 def_collector: &mut *self,
1056 macro_depth: directive.depth, 1056 macro_depth: directive.depth,
@@ -1067,8 +1067,56 @@ impl DefCollector<'_> {
1067 } 1067 }
1068 } 1068 }
1069 1069
1070 if !self.db.enable_proc_attr_macros() {
1071 return true;
1072 }
1073
1070 // Not resolved to a derive helper, so try to resolve as a macro. 1074 // Not resolved to a derive helper, so try to resolve as a macro.
1071 // FIXME: not yet :) 1075 match attr_macro_as_call_id(
1076 ast_id,
1077 attr,
1078 self.db,
1079 self.def_map.krate,
1080 &resolver,
1081 ) {
1082 Ok(call_id) => {
1083 let loc: MacroCallLoc = self.db.lookup_intern_macro(call_id);
1084 if let MacroDefKind::ProcMacro(exp, ..) = &loc.def.kind {
1085 if exp.is_dummy() {
1086 // Proc macros that cannot be expanded are treated as not
1087 // resolved, in order to fall back later.
1088 self.def_map.diagnostics.push(
1089 DefDiagnostic::unresolved_proc_macro(
1090 directive.module_id,
1091 loc.kind,
1092 ),
1093 );
1094
1095 let file_id = ast_id.ast_id.file_id;
1096 let item_tree = self.db.file_item_tree(file_id);
1097 let mod_dir = self.mod_dirs[&directive.module_id].clone();
1098 self.skip_attrs
1099 .insert(InFile::new(file_id, *mod_item), attr.id);
1100 ModCollector {
1101 def_collector: &mut *self,
1102 macro_depth: directive.depth,
1103 module_id: directive.module_id,
1104 file_id,
1105 item_tree: &item_tree,
1106 mod_dir,
1107 }
1108 .collect(&[*mod_item]);
1109
1110 // Remove the macro directive.
1111 return false;
1112 }
1113 }
1114 resolved.push((directive.module_id, call_id, directive.depth));
1115 res = ReachedFixedPoint::No;
1116 return false;
1117 }
1118 Err(UnresolvedMacro { .. }) => (),
1119 }
1072 } 1120 }
1073 } 1121 }
1074 1122
@@ -1628,7 +1676,7 @@ impl ModCollector<'_, '_> {
1628 self.def_collector.unresolved_macros.push(MacroDirective { 1676 self.def_collector.unresolved_macros.push(MacroDirective {
1629 module_id: self.module_id, 1677 module_id: self.module_id,
1630 depth: self.macro_depth + 1, 1678 depth: self.macro_depth + 1,
1631 kind: MacroDirectiveKind::Attr { ast_id, attr: attr.id, mod_item }, 1679 kind: MacroDirectiveKind::Attr { ast_id, attr: attr.clone(), mod_item },
1632 }); 1680 });
1633 1681
1634 return Err(()); 1682 return Err(());
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs
index e840fe5e8..b20b066e2 100644
--- a/crates/hir_def/src/test_db.rs
+++ b/crates/hir_def/src/test_db.rs
@@ -30,12 +30,19 @@ use crate::{
30 crate::db::InternDatabaseStorage, 30 crate::db::InternDatabaseStorage,
31 crate::db::DefDatabaseStorage 31 crate::db::DefDatabaseStorage
32)] 32)]
33#[derive(Default)]
34pub(crate) struct TestDB { 33pub(crate) struct TestDB {
35 storage: salsa::Storage<TestDB>, 34 storage: salsa::Storage<TestDB>,
36 events: Mutex<Option<Vec<salsa::Event>>>, 35 events: Mutex<Option<Vec<salsa::Event>>>,
37} 36}
38 37
38impl Default for TestDB {
39 fn default() -> Self {
40 let mut this = Self { storage: Default::default(), events: Default::default() };
41 this.set_enable_proc_attr_macros(true);
42 this
43 }
44}
45
39impl Upcast<dyn AstDatabase> for TestDB { 46impl Upcast<dyn AstDatabase> for TestDB {
40 fn upcast(&self) -> &(dyn AstDatabase + 'static) { 47 fn upcast(&self) -> &(dyn AstDatabase + 'static) {
41 &*self 48 &*self