diff options
-rw-r--r-- | crates/hir/src/lib.rs | 16 | ||||
-rw-r--r-- | crates/hir_def/src/builtin_attr.rs | 38 | ||||
-rw-r--r-- | crates/hir_def/src/db.rs | 3 | ||||
-rw-r--r-- | crates/hir_def/src/lib.rs | 40 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/collector.rs | 60 | ||||
-rw-r--r-- | crates/hir_def/src/test_db.rs | 9 | ||||
-rw-r--r-- | crates/hir_expand/src/db.rs | 11 | ||||
-rw-r--r-- | crates/hir_expand/src/input.rs | 19 | ||||
-rw-r--r-- | crates/hir_expand/src/lib.rs | 24 | ||||
-rw-r--r-- | crates/hir_expand/src/proc_macro.rs | 7 | ||||
-rw-r--r-- | crates/hir_ty/src/test_db.rs | 10 | ||||
-rw-r--r-- | crates/ide_db/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/rust-analyzer/src/config.rs | 6 | ||||
-rw-r--r-- | crates/rust-analyzer/src/global_state.rs | 9 | ||||
-rw-r--r-- | crates/rust-analyzer/src/reload.rs | 6 | ||||
-rw-r--r-- | docs/user/generated_config.adoc | 5 | ||||
-rw-r--r-- | editors/code/package.json | 5 |
17 files changed, 234 insertions, 35 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index d3ef29db4..b43d61d0e 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -534,6 +534,18 @@ impl Module { | |||
534 | Some(derive_name.clone()), | 534 | Some(derive_name.clone()), |
535 | ) | 535 | ) |
536 | } | 536 | } |
537 | MacroCallKind::Attr { ast_id, invoc_attr_index, attr_name, .. } => { | ||
538 | let node = ast_id.to_node(db.upcast()); | ||
539 | let attr = | ||
540 | node.attrs().nth((*invoc_attr_index) as usize).unwrap_or_else( | ||
541 | || panic!("cannot find attribute #{}", invoc_attr_index), | ||
542 | ); | ||
543 | ( | ||
544 | ast_id.file_id, | ||
545 | SyntaxNodePtr::from(AstPtr::new(&attr)), | ||
546 | Some(attr_name.clone()), | ||
547 | ) | ||
548 | } | ||
537 | }; | 549 | }; |
538 | sink.push(UnresolvedProcMacro { | 550 | sink.push(UnresolvedProcMacro { |
539 | file, | 551 | file, |
@@ -558,7 +570,9 @@ impl Module { | |||
558 | let node = ast_id.to_node(db.upcast()); | 570 | let node = ast_id.to_node(db.upcast()); |
559 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) | 571 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) |
560 | } | 572 | } |
561 | MacroCallKind::Derive { ast_id, .. } => { | 573 | MacroCallKind::Derive { ast_id, .. } |
574 | | MacroCallKind::Attr { ast_id, .. } => { | ||
575 | // FIXME: point to the attribute instead, this creates very large diagnostics | ||
562 | let node = ast_id.to_node(db.upcast()); | 576 | let node = ast_id.to_node(db.upcast()); |
563 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) | 577 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) |
564 | } | 578 | } |
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)] |
53 | pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | 53 | pub 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 | ||
58 | use attr::Attr; | ||
58 | use base_db::{impl_intern_key, salsa, CrateId}; | 59 | use base_db::{impl_intern_key, salsa, CrateId}; |
59 | use hir_expand::{ | 60 | use 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 | |||
773 | fn 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 ¯o_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 | ||
24 | use crate::{ | 24 | use 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 { | |||
223 | enum MacroDirectiveKind { | 223 | enum 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 | ||
229 | struct DefData<'a> { | 229 | struct 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)] | ||
34 | pub(crate) struct TestDB { | 33 | pub(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 | ||
38 | impl 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 | |||
39 | impl Upcast<dyn AstDatabase> for TestDB { | 46 | impl Upcast<dyn AstDatabase> for TestDB { |
40 | fn upcast(&self) -> &(dyn AstDatabase + 'static) { | 47 | fn upcast(&self) -> &(dyn AstDatabase + 'static) { |
41 | &*self | 48 | &*self |
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index e8f4af309..3ebe194e4 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -13,8 +13,8 @@ use syntax::{ | |||
13 | 13 | ||
14 | use crate::{ | 14 | use crate::{ |
15 | ast_id_map::AstIdMap, hygiene::HygieneFrame, input::process_macro_input, BuiltinDeriveExpander, | 15 | ast_id_map::AstIdMap, hygiene::HygieneFrame, input::process_macro_input, BuiltinDeriveExpander, |
16 | BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, MacroCallId, MacroCallLoc, MacroDefId, | 16 | BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, |
17 | MacroDefKind, MacroFile, ProcMacroExpander, | 17 | 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. |
@@ -377,7 +377,12 @@ fn expand_proc_macro( | |||
377 | _ => unreachable!(), | 377 | _ => unreachable!(), |
378 | }; | 378 | }; |
379 | 379 | ||
380 | expander.expand(db, loc.krate, ¯o_arg.0) | 380 | let attr_arg = match &loc.kind { |
381 | MacroCallKind::Attr { attr_args, .. } => Some(attr_args), | ||
382 | _ => None, | ||
383 | }; | ||
384 | |||
385 | expander.expand(db, loc.krate, ¯o_arg.0, attr_arg) | ||
381 | } | 386 | } |
382 | 387 | ||
383 | fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool { | 388 | fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool { |
diff --git a/crates/hir_expand/src/input.rs b/crates/hir_expand/src/input.rs index fe4790e7b..40116a479 100644 --- a/crates/hir_expand/src/input.rs +++ b/crates/hir_expand/src/input.rs | |||
@@ -28,6 +28,14 @@ pub(crate) fn process_macro_input( | |||
28 | 28 | ||
29 | remove_derives_up_to(item, derive_attr_index as usize).syntax().clone() | 29 | remove_derives_up_to(item, derive_attr_index as usize).syntax().clone() |
30 | } | 30 | } |
31 | MacroCallKind::Attr { invoc_attr_index, .. } => { | ||
32 | let item = match ast::Item::cast(node.clone()) { | ||
33 | Some(item) => item, | ||
34 | None => return node, | ||
35 | }; | ||
36 | |||
37 | remove_attr_invoc(item, invoc_attr_index as usize).syntax().clone() | ||
38 | } | ||
31 | } | 39 | } |
32 | } | 40 | } |
33 | 41 | ||
@@ -46,6 +54,17 @@ fn remove_derives_up_to(item: ast::Item, attr_index: usize) -> ast::Item { | |||
46 | item | 54 | item |
47 | } | 55 | } |
48 | 56 | ||
57 | /// Removes the attribute invoking an attribute macro from `item`. | ||
58 | fn remove_attr_invoc(item: ast::Item, attr_index: usize) -> ast::Item { | ||
59 | let item = item.clone_for_update(); | ||
60 | let attr = item | ||
61 | .attrs() | ||
62 | .nth(attr_index) | ||
63 | .unwrap_or_else(|| panic!("cannot find attribute #{}", attr_index)); | ||
64 | attr.syntax().detach(); | ||
65 | item | ||
66 | } | ||
67 | |||
49 | #[cfg(test)] | 68 | #[cfg(test)] |
50 | mod tests { | 69 | mod tests { |
51 | use base_db::fixture::WithFixture; | 70 | use base_db::fixture::WithFixture; |
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index 90d8ae240..618f26b95 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs | |||
@@ -258,14 +258,29 @@ pub enum MacroCallKind { | |||
258 | /// out-of-line modules, which may have attributes spread across 2 files! | 258 | /// out-of-line modules, which may have attributes spread across 2 files! |
259 | derive_attr_index: u32, | 259 | derive_attr_index: u32, |
260 | }, | 260 | }, |
261 | Attr { | ||
262 | ast_id: AstId<ast::Item>, | ||
263 | attr_name: String, | ||
264 | attr_args: tt::Subtree, | ||
265 | /// Syntactical index of the invoking `#[attribute]`. | ||
266 | /// | ||
267 | /// Outer attributes are counted first, then inner attributes. This does not support | ||
268 | /// out-of-line modules, which may have attributes spread across 2 files! | ||
269 | invoc_attr_index: u32, | ||
270 | }, | ||
261 | } | 271 | } |
262 | 272 | ||
273 | // FIXME: attribute indices do not account for `cfg_attr`, which means that we'll strip the whole | ||
274 | // `cfg_attr` instead of just one of the attributes it expands to | ||
275 | |||
263 | impl MacroCallKind { | 276 | impl MacroCallKind { |
264 | /// Returns the file containing the macro invocation. | 277 | /// Returns the file containing the macro invocation. |
265 | fn file_id(&self) -> HirFileId { | 278 | fn file_id(&self) -> HirFileId { |
266 | match self { | 279 | match self { |
267 | MacroCallKind::FnLike { ast_id, .. } => ast_id.file_id, | 280 | MacroCallKind::FnLike { ast_id, .. } => ast_id.file_id, |
268 | MacroCallKind::Derive { ast_id, .. } => ast_id.file_id, | 281 | MacroCallKind::Derive { ast_id, .. } | MacroCallKind::Attr { ast_id, .. } => { |
282 | ast_id.file_id | ||
283 | } | ||
269 | } | 284 | } |
270 | } | 285 | } |
271 | 286 | ||
@@ -274,7 +289,7 @@ impl MacroCallKind { | |||
274 | MacroCallKind::FnLike { ast_id, .. } => { | 289 | MacroCallKind::FnLike { ast_id, .. } => { |
275 | ast_id.with_value(ast_id.to_node(db).syntax().clone()) | 290 | ast_id.with_value(ast_id.to_node(db).syntax().clone()) |
276 | } | 291 | } |
277 | MacroCallKind::Derive { ast_id, .. } => { | 292 | MacroCallKind::Derive { ast_id, .. } | MacroCallKind::Attr { ast_id, .. } => { |
278 | ast_id.with_value(ast_id.to_node(db).syntax().clone()) | 293 | ast_id.with_value(ast_id.to_node(db).syntax().clone()) |
279 | } | 294 | } |
280 | } | 295 | } |
@@ -285,7 +300,9 @@ impl MacroCallKind { | |||
285 | MacroCallKind::FnLike { ast_id, .. } => { | 300 | MacroCallKind::FnLike { ast_id, .. } => { |
286 | Some(ast_id.to_node(db).token_tree()?.syntax().clone()) | 301 | Some(ast_id.to_node(db).token_tree()?.syntax().clone()) |
287 | } | 302 | } |
288 | MacroCallKind::Derive { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()), | 303 | MacroCallKind::Derive { ast_id, .. } | MacroCallKind::Attr { ast_id, .. } => { |
304 | Some(ast_id.to_node(db).syntax().clone()) | ||
305 | } | ||
289 | } | 306 | } |
290 | } | 307 | } |
291 | 308 | ||
@@ -293,6 +310,7 @@ impl MacroCallKind { | |||
293 | match self { | 310 | match self { |
294 | MacroCallKind::FnLike { fragment, .. } => *fragment, | 311 | MacroCallKind::FnLike { fragment, .. } => *fragment, |
295 | MacroCallKind::Derive { .. } => FragmentKind::Items, | 312 | MacroCallKind::Derive { .. } => FragmentKind::Items, |
313 | MacroCallKind::Attr { .. } => FragmentKind::Items, // is this always correct? | ||
296 | } | 314 | } |
297 | } | 315 | } |
298 | } | 316 | } |
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs index d5643393a..dbe1b446e 100644 --- a/crates/hir_expand/src/proc_macro.rs +++ b/crates/hir_expand/src/proc_macro.rs | |||
@@ -28,11 +28,16 @@ impl ProcMacroExpander { | |||
28 | Self { krate, proc_macro_id: None } | 28 | Self { krate, proc_macro_id: None } |
29 | } | 29 | } |
30 | 30 | ||
31 | pub fn is_dummy(&self) -> bool { | ||
32 | self.proc_macro_id.is_none() | ||
33 | } | ||
34 | |||
31 | pub fn expand( | 35 | pub fn expand( |
32 | self, | 36 | self, |
33 | db: &dyn AstDatabase, | 37 | db: &dyn AstDatabase, |
34 | calling_crate: CrateId, | 38 | calling_crate: CrateId, |
35 | tt: &tt::Subtree, | 39 | tt: &tt::Subtree, |
40 | attr_arg: Option<&tt::Subtree>, | ||
36 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 41 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
37 | match self.proc_macro_id { | 42 | match self.proc_macro_id { |
38 | Some(id) => { | 43 | Some(id) => { |
@@ -46,7 +51,7 @@ impl ProcMacroExpander { | |||
46 | // Proc macros have access to the environment variables of the invoking crate. | 51 | // Proc macros have access to the environment variables of the invoking crate. |
47 | let env = &krate_graph[calling_crate].env; | 52 | let env = &krate_graph[calling_crate].env; |
48 | 53 | ||
49 | proc_macro.expander.expand(&tt, None, &env).map_err(mbe::ExpandError::from) | 54 | proc_macro.expander.expand(&tt, attr_arg, &env).map_err(mbe::ExpandError::from) |
50 | } | 55 | } |
51 | None => Err(mbe::ExpandError::UnresolvedProcMacro), | 56 | None => Err(mbe::ExpandError::UnresolvedProcMacro), |
52 | } | 57 | } |
diff --git a/crates/hir_ty/src/test_db.rs b/crates/hir_ty/src/test_db.rs index 381b98ba8..4640ea821 100644 --- a/crates/hir_ty/src/test_db.rs +++ b/crates/hir_ty/src/test_db.rs | |||
@@ -22,11 +22,19 @@ use test_utils::extract_annotations; | |||
22 | hir_def::db::DefDatabaseStorage, | 22 | hir_def::db::DefDatabaseStorage, |
23 | crate::db::HirDatabaseStorage | 23 | crate::db::HirDatabaseStorage |
24 | )] | 24 | )] |
25 | #[derive(Default)] | ||
26 | pub(crate) struct TestDB { | 25 | pub(crate) struct TestDB { |
27 | storage: salsa::Storage<TestDB>, | 26 | storage: salsa::Storage<TestDB>, |
28 | events: Mutex<Option<Vec<salsa::Event>>>, | 27 | events: Mutex<Option<Vec<salsa::Event>>>, |
29 | } | 28 | } |
29 | |||
30 | impl Default for TestDB { | ||
31 | fn default() -> Self { | ||
32 | let mut this = Self { storage: Default::default(), events: Default::default() }; | ||
33 | this.set_enable_proc_attr_macros(true); | ||
34 | this | ||
35 | } | ||
36 | } | ||
37 | |||
30 | impl fmt::Debug for TestDB { | 38 | impl fmt::Debug for TestDB { |
31 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 39 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
32 | f.debug_struct("TestDB").finish() | 40 | f.debug_struct("TestDB").finish() |
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index 1f900aef4..105607dca 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs | |||
@@ -93,6 +93,7 @@ impl RootDatabase { | |||
93 | db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); | 93 | db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); |
94 | db.set_local_roots_with_durability(Default::default(), Durability::HIGH); | 94 | db.set_local_roots_with_durability(Default::default(), Durability::HIGH); |
95 | db.set_library_roots_with_durability(Default::default(), Durability::HIGH); | 95 | db.set_library_roots_with_durability(Default::default(), Durability::HIGH); |
96 | db.set_enable_proc_attr_macros(Default::default()); | ||
96 | db.update_lru_capacity(lru_capacity); | 97 | db.update_lru_capacity(lru_capacity); |
97 | db | 98 | db |
98 | } | 99 | } |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index c33cdb740..d1f3c1b06 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -126,6 +126,9 @@ config_data! { | |||
126 | /// and a blue icon in the `Problems Panel`. | 126 | /// and a blue icon in the `Problems Panel`. |
127 | diagnostics_warningsAsInfo: Vec<String> = "[]", | 127 | diagnostics_warningsAsInfo: Vec<String> = "[]", |
128 | 128 | ||
129 | /// Expand attribute macros. | ||
130 | experimental_procAttrMacros: bool = "false", | ||
131 | |||
129 | /// Controls file watching implementation. | 132 | /// Controls file watching implementation. |
130 | files_watcher: String = "\"client\"", | 133 | files_watcher: String = "\"client\"", |
131 | /// These directories will be ignored by rust-analyzer. | 134 | /// These directories will be ignored by rust-analyzer. |
@@ -546,6 +549,9 @@ impl Config { | |||
546 | let path = self.data.procMacro_server.clone().or_else(|| std::env::current_exe().ok())?; | 549 | let path = self.data.procMacro_server.clone().or_else(|| std::env::current_exe().ok())?; |
547 | Some((path, vec!["proc-macro".into()])) | 550 | Some((path, vec!["proc-macro".into()])) |
548 | } | 551 | } |
552 | pub fn expand_proc_attr_macros(&self) -> bool { | ||
553 | self.data.experimental_procAttrMacros | ||
554 | } | ||
549 | pub fn files(&self) -> FilesConfig { | 555 | pub fn files(&self) -> FilesConfig { |
550 | FilesConfig { | 556 | FilesConfig { |
551 | watcher: match self.data.files_watcher.as_str() { | 557 | watcher: match self.data.files_watcher.as_str() { |
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index ea9dbf7fc..582a89667 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -119,12 +119,12 @@ impl GlobalState { | |||
119 | 119 | ||
120 | let analysis_host = AnalysisHost::new(config.lru_capacity()); | 120 | let analysis_host = AnalysisHost::new(config.lru_capacity()); |
121 | let (flycheck_sender, flycheck_receiver) = unbounded(); | 121 | let (flycheck_sender, flycheck_receiver) = unbounded(); |
122 | GlobalState { | 122 | let mut this = GlobalState { |
123 | sender, | 123 | sender, |
124 | req_queue: ReqQueue::default(), | 124 | req_queue: ReqQueue::default(), |
125 | task_pool, | 125 | task_pool, |
126 | loader, | 126 | loader, |
127 | config: Arc::new(config), | 127 | config: Arc::new(config.clone()), |
128 | analysis_host, | 128 | analysis_host, |
129 | diagnostics: Default::default(), | 129 | diagnostics: Default::default(), |
130 | mem_docs: FxHashMap::default(), | 130 | mem_docs: FxHashMap::default(), |
@@ -151,7 +151,10 @@ impl GlobalState { | |||
151 | 151 | ||
152 | fetch_build_data_queue: OpQueue::default(), | 152 | fetch_build_data_queue: OpQueue::default(), |
153 | latest_requests: Default::default(), | 153 | latest_requests: Default::default(), |
154 | } | 154 | }; |
155 | // Apply any required database inputs from the config. | ||
156 | this.update_configuration(config); | ||
157 | this | ||
155 | } | 158 | } |
156 | 159 | ||
157 | pub(crate) fn process_changes(&mut self) -> bool { | 160 | pub(crate) fn process_changes(&mut self) -> bool { |
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 93b5ff55f..bd31d1d13 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs | |||
@@ -2,6 +2,7 @@ | |||
2 | use std::{mem, sync::Arc}; | 2 | use std::{mem, sync::Arc}; |
3 | 3 | ||
4 | use flycheck::{FlycheckConfig, FlycheckHandle}; | 4 | use flycheck::{FlycheckConfig, FlycheckHandle}; |
5 | use hir::db::DefDatabase; | ||
5 | use ide::Change; | 6 | use ide::Change; |
6 | use ide_db::base_db::{CrateGraph, SourceRoot, VfsPath}; | 7 | use ide_db::base_db::{CrateGraph, SourceRoot, VfsPath}; |
7 | use project_model::{BuildDataCollector, BuildDataResult, ProcMacroClient, ProjectWorkspace}; | 8 | use project_model::{BuildDataCollector, BuildDataResult, ProcMacroClient, ProjectWorkspace}; |
@@ -47,6 +48,11 @@ impl GlobalState { | |||
47 | } else if self.config.flycheck() != old_config.flycheck() { | 48 | } else if self.config.flycheck() != old_config.flycheck() { |
48 | self.reload_flycheck(); | 49 | self.reload_flycheck(); |
49 | } | 50 | } |
51 | |||
52 | // Apply experimental feature flags. | ||
53 | self.analysis_host | ||
54 | .raw_database_mut() | ||
55 | .set_enable_proc_attr_macros(self.config.expand_proc_attr_macros()); | ||
50 | } | 56 | } |
51 | pub(crate) fn maybe_refresh(&mut self, changes: &[(AbsPathBuf, ChangeKind)]) { | 57 | pub(crate) fn maybe_refresh(&mut self, changes: &[(AbsPathBuf, ChangeKind)]) { |
52 | if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) { | 58 | if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) { |
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 7f405b4d7..4eec8455d 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc | |||
@@ -181,6 +181,11 @@ List of warnings that should be displayed with info severity. | |||
181 | The warnings will be indicated by a blue squiggly underline in code | 181 | The warnings will be indicated by a blue squiggly underline in code |
182 | and a blue icon in the `Problems Panel`. | 182 | and a blue icon in the `Problems Panel`. |
183 | -- | 183 | -- |
184 | [[rust-analyzer.experimental.procAttrMacros]]rust-analyzer.experimental.procAttrMacros (default: `false`):: | ||
185 | + | ||
186 | -- | ||
187 | Expand attribute macros. | ||
188 | -- | ||
184 | [[rust-analyzer.files.watcher]]rust-analyzer.files.watcher (default: `"client"`):: | 189 | [[rust-analyzer.files.watcher]]rust-analyzer.files.watcher (default: `"client"`):: |
185 | + | 190 | + |
186 | -- | 191 | -- |
diff --git a/editors/code/package.json b/editors/code/package.json index 4a5070d02..bffc1e05b 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -617,6 +617,11 @@ | |||
617 | "type": "string" | 617 | "type": "string" |
618 | } | 618 | } |
619 | }, | 619 | }, |
620 | "rust-analyzer.experimental.procAttrMacros": { | ||
621 | "markdownDescription": "Expand attribute macros.", | ||
622 | "default": false, | ||
623 | "type": "boolean" | ||
624 | }, | ||
620 | "rust-analyzer.files.watcher": { | 625 | "rust-analyzer.files.watcher": { |
621 | "markdownDescription": "Controls file watching implementation.", | 626 | "markdownDescription": "Controls file watching implementation.", |
622 | "default": "client", | 627 | "default": "client", |