diff options
Diffstat (limited to 'crates/hir_expand/src')
-rw-r--r-- | crates/hir_expand/src/builtin_attr.rs | 67 | ||||
-rw-r--r-- | crates/hir_expand/src/builtin_derive.rs | 2 | ||||
-rw-r--r-- | crates/hir_expand/src/builtin_macro.rs | 14 | ||||
-rw-r--r-- | crates/hir_expand/src/db.rs | 27 | ||||
-rw-r--r-- | crates/hir_expand/src/eager.rs | 20 | ||||
-rw-r--r-- | crates/hir_expand/src/hygiene.rs | 1 | ||||
-rw-r--r-- | crates/hir_expand/src/input.rs | 25 | ||||
-rw-r--r-- | crates/hir_expand/src/lib.rs | 30 | ||||
-rw-r--r-- | crates/hir_expand/src/name.rs | 9 | ||||
-rw-r--r-- | crates/hir_expand/src/proc_macro.rs | 8 |
10 files changed, 168 insertions, 35 deletions
diff --git a/crates/hir_expand/src/builtin_attr.rs b/crates/hir_expand/src/builtin_attr.rs new file mode 100644 index 000000000..c8432005e --- /dev/null +++ b/crates/hir_expand/src/builtin_attr.rs | |||
@@ -0,0 +1,67 @@ | |||
1 | //! Builtin derives. | ||
2 | |||
3 | use syntax::ast; | ||
4 | |||
5 | use crate::{db::AstDatabase, name, AstId, CrateId, MacroCallId, MacroDefId, MacroDefKind}; | ||
6 | |||
7 | macro_rules! register_builtin { | ||
8 | ( $(($name:ident, $variant:ident) => $expand:ident),* ) => { | ||
9 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
10 | pub enum BuiltinAttrExpander { | ||
11 | $($variant),* | ||
12 | } | ||
13 | |||
14 | impl BuiltinAttrExpander { | ||
15 | pub fn expand( | ||
16 | &self, | ||
17 | db: &dyn AstDatabase, | ||
18 | id: MacroCallId, | ||
19 | tt: &tt::Subtree, | ||
20 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
21 | let expander = match *self { | ||
22 | $( BuiltinAttrExpander::$variant => $expand, )* | ||
23 | }; | ||
24 | expander(db, id, tt) | ||
25 | } | ||
26 | |||
27 | fn find_by_name(name: &name::Name) -> Option<Self> { | ||
28 | match name { | ||
29 | $( id if id == &name::name![$name] => Some(BuiltinAttrExpander::$variant), )* | ||
30 | _ => None, | ||
31 | } | ||
32 | } | ||
33 | } | ||
34 | |||
35 | }; | ||
36 | } | ||
37 | |||
38 | register_builtin! { | ||
39 | (bench, Bench) => dummy_attr_expand, | ||
40 | (cfg_accessible, CfgAccessible) => dummy_attr_expand, | ||
41 | (cfg_eval, CfgEval) => dummy_attr_expand, | ||
42 | (derive, Derive) => dummy_attr_expand, | ||
43 | (global_allocator, GlobalAllocator) => dummy_attr_expand, | ||
44 | (test, Test) => dummy_attr_expand, | ||
45 | (test_case, TestCase) => dummy_attr_expand | ||
46 | } | ||
47 | |||
48 | pub fn find_builtin_attr( | ||
49 | ident: &name::Name, | ||
50 | krate: CrateId, | ||
51 | ast_id: AstId<ast::Macro>, | ||
52 | ) -> Option<MacroDefId> { | ||
53 | let expander = BuiltinAttrExpander::find_by_name(ident)?; | ||
54 | Some(MacroDefId { | ||
55 | krate, | ||
56 | kind: MacroDefKind::BuiltInAttr(expander, ast_id), | ||
57 | local_inner: false, | ||
58 | }) | ||
59 | } | ||
60 | |||
61 | fn dummy_attr_expand( | ||
62 | _db: &dyn AstDatabase, | ||
63 | _id: MacroCallId, | ||
64 | tt: &tt::Subtree, | ||
65 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
66 | Ok(tt.clone()) | ||
67 | } | ||
diff --git a/crates/hir_expand/src/builtin_derive.rs b/crates/hir_expand/src/builtin_derive.rs index fe9497b50..4610f6f91 100644 --- a/crates/hir_expand/src/builtin_derive.rs +++ b/crates/hir_expand/src/builtin_derive.rs | |||
@@ -325,7 +325,7 @@ $0 | |||
325 | }, | 325 | }, |
326 | }; | 326 | }; |
327 | 327 | ||
328 | let id: MacroCallId = db.intern_macro(loc).into(); | 328 | let id: MacroCallId = db.intern_macro(loc); |
329 | let parsed = db.parse_or_expand(id.as_file()).unwrap(); | 329 | let parsed = db.parse_or_expand(id.as_file()).unwrap(); |
330 | 330 | ||
331 | // FIXME text() for syntax nodes parsed from token tree looks weird | 331 | // FIXME text() for syntax nodes parsed from token tree looks weird |
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs index 0b310ba2f..f24d1d919 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs | |||
@@ -354,7 +354,7 @@ fn concat_expand( | |||
354 | // concat works with string and char literals, so remove any quotes. | 354 | // concat works with string and char literals, so remove any quotes. |
355 | // It also works with integer, float and boolean literals, so just use the rest | 355 | // It also works with integer, float and boolean literals, so just use the rest |
356 | // as-is. | 356 | // as-is. |
357 | let component = unquote_str(&it).unwrap_or_else(|| it.text.to_string()); | 357 | let component = unquote_str(it).unwrap_or_else(|| it.text.to_string()); |
358 | text.push_str(&component); | 358 | text.push_str(&component); |
359 | } | 359 | } |
360 | // handle boolean literals | 360 | // handle boolean literals |
@@ -417,7 +417,7 @@ fn parse_string(tt: &tt::Subtree) -> Result<String, mbe::ExpandError> { | |||
417 | tt.token_trees | 417 | tt.token_trees |
418 | .get(0) | 418 | .get(0) |
419 | .and_then(|tt| match tt { | 419 | .and_then(|tt| match tt { |
420 | tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => unquote_str(&it), | 420 | tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => unquote_str(it), |
421 | _ => None, | 421 | _ => None, |
422 | }) | 422 | }) |
423 | .ok_or_else(|| mbe::ExpandError::ConversionError) | 423 | .ok_or_else(|| mbe::ExpandError::ConversionError) |
@@ -430,7 +430,7 @@ fn include_expand( | |||
430 | ) -> ExpandResult<Option<ExpandedEager>> { | 430 | ) -> ExpandResult<Option<ExpandedEager>> { |
431 | let res = (|| { | 431 | let res = (|| { |
432 | let path = parse_string(tt)?; | 432 | let path = parse_string(tt)?; |
433 | let file_id = relative_file(db, arg_id.into(), &path, false)?; | 433 | let file_id = relative_file(db, arg_id, &path, false)?; |
434 | 434 | ||
435 | let subtree = parse_to_token_tree(&db.file_text(file_id)) | 435 | let subtree = parse_to_token_tree(&db.file_text(file_id)) |
436 | .ok_or_else(|| mbe::ExpandError::ConversionError)? | 436 | .ok_or_else(|| mbe::ExpandError::ConversionError)? |
@@ -480,7 +480,7 @@ fn include_str_expand( | |||
480 | // it's unusual to `include_str!` a Rust file), but we can return an empty string. | 480 | // it's unusual to `include_str!` a Rust file), but we can return an empty string. |
481 | // Ideally, we'd be able to offer a precise expansion if the user asks for macro | 481 | // Ideally, we'd be able to offer a precise expansion if the user asks for macro |
482 | // expansion. | 482 | // expansion. |
483 | let file_id = match relative_file(db, arg_id.into(), &path, true) { | 483 | let file_id = match relative_file(db, arg_id, &path, true) { |
484 | Ok(file_id) => file_id, | 484 | Ok(file_id) => file_id, |
485 | Err(_) => { | 485 | Err(_) => { |
486 | return ExpandResult::ok(Some(ExpandedEager::new(quote!("")))); | 486 | return ExpandResult::ok(Some(ExpandedEager::new(quote!("")))); |
@@ -561,7 +561,7 @@ mod tests { | |||
561 | use syntax::ast::NameOwner; | 561 | use syntax::ast::NameOwner; |
562 | 562 | ||
563 | fn expand_builtin_macro(ra_fixture: &str) -> String { | 563 | fn expand_builtin_macro(ra_fixture: &str) -> String { |
564 | let (db, file_id) = TestDB::with_single_file(&ra_fixture); | 564 | let (db, file_id) = TestDB::with_single_file(ra_fixture); |
565 | let parsed = db.parse(file_id); | 565 | let parsed = db.parse(file_id); |
566 | let mut macro_rules: Vec<_> = | 566 | let mut macro_rules: Vec<_> = |
567 | parsed.syntax_node().descendants().filter_map(ast::MacroRules::cast).collect(); | 567 | parsed.syntax_node().descendants().filter_map(ast::MacroRules::cast).collect(); |
@@ -598,7 +598,7 @@ mod tests { | |||
598 | }, | 598 | }, |
599 | }; | 599 | }; |
600 | 600 | ||
601 | let id: MacroCallId = db.intern_macro(loc).into(); | 601 | let id: MacroCallId = db.intern_macro(loc); |
602 | id.as_file() | 602 | id.as_file() |
603 | } | 603 | } |
604 | Either::Right(expander) => { | 604 | Either::Right(expander) => { |
@@ -635,7 +635,7 @@ mod tests { | |||
635 | kind: MacroCallKind::FnLike { ast_id: call_id, fragment }, | 635 | kind: MacroCallKind::FnLike { ast_id: call_id, fragment }, |
636 | }; | 636 | }; |
637 | 637 | ||
638 | let id: MacroCallId = db.intern_macro(loc).into(); | 638 | let id: MacroCallId = db.intern_macro(loc); |
639 | id.as_file() | 639 | id.as_file() |
640 | } | 640 | } |
641 | }; | 641 | }; |
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index e8f4af309..66f44202b 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, input::process_macro_input, BuiltinDeriveExpander, | 15 | ast_id_map::AstIdMap, hygiene::HygieneFrame, input::process_macro_input, BuiltinAttrExpander, |
16 | BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, MacroCallId, MacroCallLoc, MacroDefId, | 16 | BuiltinDeriveExpander, BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, MacroCallId, |
17 | MacroDefKind, MacroFile, ProcMacroExpander, | 17 | MacroCallKind, 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. |
@@ -31,6 +31,8 @@ pub enum TokenExpander { | |||
31 | MacroDef { mac: mbe::MacroDef, def_site_token_map: mbe::TokenMap }, | 31 | MacroDef { mac: mbe::MacroDef, def_site_token_map: mbe::TokenMap }, |
32 | /// Stuff like `line!` and `file!`. | 32 | /// Stuff like `line!` and `file!`. |
33 | Builtin(BuiltinFnLikeExpander), | 33 | Builtin(BuiltinFnLikeExpander), |
34 | /// `global_allocator` and such. | ||
35 | BuiltinAttr(BuiltinAttrExpander), | ||
34 | /// `derive(Copy)` and such. | 36 | /// `derive(Copy)` and such. |
35 | BuiltinDerive(BuiltinDeriveExpander), | 37 | BuiltinDerive(BuiltinDeriveExpander), |
36 | /// The thing we love the most here in rust-analyzer -- procedural macros. | 38 | /// The thing we love the most here in rust-analyzer -- procedural macros. |
@@ -49,12 +51,13 @@ impl TokenExpander { | |||
49 | TokenExpander::MacroDef { mac, .. } => mac.expand(tt), | 51 | TokenExpander::MacroDef { mac, .. } => mac.expand(tt), |
50 | TokenExpander::Builtin(it) => it.expand(db, id, tt), | 52 | TokenExpander::Builtin(it) => it.expand(db, id, tt), |
51 | // FIXME switch these to ExpandResult as well | 53 | // FIXME switch these to ExpandResult as well |
54 | TokenExpander::BuiltinAttr(it) => it.expand(db, id, tt).into(), | ||
52 | TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), | 55 | TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), |
53 | TokenExpander::ProcMacro(_) => { | 56 | TokenExpander::ProcMacro(_) => { |
54 | // We store the result in salsa db to prevent non-deterministic behavior in | 57 | // We store the result in salsa db to prevent non-deterministic behavior in |
55 | // some proc-macro implementation | 58 | // some proc-macro implementation |
56 | // See #4315 for details | 59 | // See #4315 for details |
57 | db.expand_proc_macro(id.into()).into() | 60 | db.expand_proc_macro(id).into() |
58 | } | 61 | } |
59 | } | 62 | } |
60 | } | 63 | } |
@@ -64,6 +67,7 @@ impl TokenExpander { | |||
64 | TokenExpander::MacroRules { mac, .. } => mac.map_id_down(id), | 67 | TokenExpander::MacroRules { mac, .. } => mac.map_id_down(id), |
65 | TokenExpander::MacroDef { mac, .. } => mac.map_id_down(id), | 68 | TokenExpander::MacroDef { mac, .. } => mac.map_id_down(id), |
66 | TokenExpander::Builtin(..) | 69 | TokenExpander::Builtin(..) |
70 | | TokenExpander::BuiltinAttr(..) | ||
67 | | TokenExpander::BuiltinDerive(..) | 71 | | TokenExpander::BuiltinDerive(..) |
68 | | TokenExpander::ProcMacro(..) => id, | 72 | | TokenExpander::ProcMacro(..) => id, |
69 | } | 73 | } |
@@ -74,6 +78,7 @@ impl TokenExpander { | |||
74 | TokenExpander::MacroRules { mac, .. } => mac.map_id_up(id), | 78 | TokenExpander::MacroRules { mac, .. } => mac.map_id_up(id), |
75 | TokenExpander::MacroDef { mac, .. } => mac.map_id_up(id), | 79 | TokenExpander::MacroDef { mac, .. } => mac.map_id_up(id), |
76 | TokenExpander::Builtin(..) | 80 | TokenExpander::Builtin(..) |
81 | | TokenExpander::BuiltinAttr(..) | ||
77 | | TokenExpander::BuiltinDerive(..) | 82 | | TokenExpander::BuiltinDerive(..) |
78 | | TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call), | 83 | | TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call), |
79 | } | 84 | } |
@@ -85,7 +90,7 @@ impl TokenExpander { | |||
85 | pub trait AstDatabase: SourceDatabase { | 90 | pub trait AstDatabase: SourceDatabase { |
86 | fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>; | 91 | fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>; |
87 | 92 | ||
88 | /// Main public API -- parsis a hir file, not caring whether it's a real | 93 | /// Main public API -- parses a hir file, not caring whether it's a real |
89 | /// file or a macro expansion. | 94 | /// file or a macro expansion. |
90 | #[salsa::transparent] | 95 | #[salsa::transparent] |
91 | fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>; | 96 | fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>; |
@@ -236,7 +241,7 @@ fn parse_macro_expansion( | |||
236 | } | 241 | } |
237 | }; | 242 | }; |
238 | if is_self_replicating(&node, &call_node.value) { | 243 | if is_self_replicating(&node, &call_node.value) { |
239 | return ExpandResult::only_err(err); | 244 | ExpandResult::only_err(err) |
240 | } else { | 245 | } else { |
241 | ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: Some(err) } | 246 | ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: Some(err) } |
242 | } | 247 | } |
@@ -299,6 +304,9 @@ fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<TokenExpander>> | |||
299 | } | 304 | } |
300 | }, | 305 | }, |
301 | MacroDefKind::BuiltIn(expander, _) => Some(Arc::new(TokenExpander::Builtin(expander))), | 306 | MacroDefKind::BuiltIn(expander, _) => Some(Arc::new(TokenExpander::Builtin(expander))), |
307 | MacroDefKind::BuiltInAttr(expander, _) => { | ||
308 | Some(Arc::new(TokenExpander::BuiltinAttr(expander))) | ||
309 | } | ||
302 | MacroDefKind::BuiltInDerive(expander, _) => { | 310 | MacroDefKind::BuiltInDerive(expander, _) => { |
303 | Some(Arc::new(TokenExpander::BuiltinDerive(expander))) | 311 | Some(Arc::new(TokenExpander::BuiltinDerive(expander))) |
304 | } | 312 | } |
@@ -377,7 +385,12 @@ fn expand_proc_macro( | |||
377 | _ => unreachable!(), | 385 | _ => unreachable!(), |
378 | }; | 386 | }; |
379 | 387 | ||
380 | expander.expand(db, loc.krate, ¯o_arg.0) | 388 | let attr_arg = match &loc.kind { |
389 | MacroCallKind::Attr { attr_args, .. } => Some(attr_args), | ||
390 | _ => None, | ||
391 | }; | ||
392 | |||
393 | expander.expand(db, loc.krate, ¯o_arg.0, attr_arg) | ||
381 | } | 394 | } |
382 | 395 | ||
383 | fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool { | 396 | fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool { |
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs index 14af628a1..584ddcf9f 100644 --- a/crates/hir_expand/src/eager.rs +++ b/crates/hir_expand/src/eager.rs | |||
@@ -17,7 +17,7 @@ | |||
17 | //! > and we need to live with it because it's available on stable and widely relied upon. | 17 | //! > and we need to live with it because it's available on stable and widely relied upon. |
18 | //! | 18 | //! |
19 | //! | 19 | //! |
20 | //! See the full discussion : https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros | 20 | //! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros> |
21 | 21 | ||
22 | use crate::{ | 22 | use crate::{ |
23 | ast::{self, AstNode}, | 23 | ast::{self, AstNode}, |
@@ -128,7 +128,7 @@ pub fn expand_eager_macro( | |||
128 | }), | 128 | }), |
129 | kind: MacroCallKind::FnLike { ast_id: call_id, fragment: FragmentKind::Expr }, | 129 | kind: MacroCallKind::FnLike { ast_id: call_id, fragment: FragmentKind::Expr }, |
130 | }); | 130 | }); |
131 | let arg_file_id: MacroCallId = arg_id.into(); | 131 | let arg_file_id: MacroCallId = arg_id; |
132 | 132 | ||
133 | let parsed_args = | 133 | let parsed_args = |
134 | diagnostic_sink.result(mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr))?.0; | 134 | diagnostic_sink.result(mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr))?.0; |
@@ -177,13 +177,11 @@ fn lazy_expand( | |||
177 | let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value); | 177 | let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value); |
178 | 178 | ||
179 | let fragment = crate::to_fragment_kind(¯o_call.value); | 179 | let fragment = crate::to_fragment_kind(¯o_call.value); |
180 | let id: MacroCallId = def | 180 | let id: MacroCallId = def.as_lazy_macro( |
181 | .as_lazy_macro( | 181 | db, |
182 | db, | 182 | krate, |
183 | krate, | 183 | MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), fragment }, |
184 | MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), fragment }, | 184 | ); |
185 | ) | ||
186 | .into(); | ||
187 | 185 | ||
188 | let err = db.macro_expand_error(id); | 186 | let err = db.macro_expand_error(id); |
189 | let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)); | 187 | let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)); |
@@ -216,14 +214,14 @@ fn eager_macro_recur( | |||
216 | def, | 214 | def, |
217 | macro_resolver, | 215 | macro_resolver, |
218 | diagnostic_sink, | 216 | diagnostic_sink, |
219 | )? | 217 | )?; |
220 | .into(); | ||
221 | db.parse_or_expand(id.as_file()) | 218 | db.parse_or_expand(id.as_file()) |
222 | .expect("successful macro expansion should be parseable") | 219 | .expect("successful macro expansion should be parseable") |
223 | .clone_for_update() | 220 | .clone_for_update() |
224 | } | 221 | } |
225 | MacroDefKind::Declarative(_) | 222 | MacroDefKind::Declarative(_) |
226 | | MacroDefKind::BuiltIn(..) | 223 | | MacroDefKind::BuiltIn(..) |
224 | | MacroDefKind::BuiltInAttr(..) | ||
227 | | MacroDefKind::BuiltInDerive(..) | 225 | | MacroDefKind::BuiltInDerive(..) |
228 | | MacroDefKind::ProcMacro(..) => { | 226 | | MacroDefKind::ProcMacro(..) => { |
229 | let res = lazy_expand(db, &def, curr.with_value(child.clone()), krate); | 227 | let res = lazy_expand(db, &def, curr.with_value(child.clone()), krate); |
diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs index d98913907..05c6c3fb1 100644 --- a/crates/hir_expand/src/hygiene.rs +++ b/crates/hir_expand/src/hygiene.rs | |||
@@ -192,6 +192,7 @@ impl HygieneFrame { | |||
192 | (info, Some(loc.def.krate), loc.def.local_inner) | 192 | (info, Some(loc.def.krate), loc.def.local_inner) |
193 | } | 193 | } |
194 | MacroDefKind::BuiltIn(..) => (info, Some(loc.def.krate), false), | 194 | MacroDefKind::BuiltIn(..) => (info, Some(loc.def.krate), false), |
195 | MacroDefKind::BuiltInAttr(..) => (info, None, false), | ||
195 | MacroDefKind::BuiltInDerive(..) => (info, None, false), | 196 | MacroDefKind::BuiltInDerive(..) => (info, None, false), |
196 | MacroDefKind::BuiltInEager(..) => (info, None, false), | 197 | MacroDefKind::BuiltInEager(..) => (info, None, false), |
197 | MacroDefKind::ProcMacro(..) => (info, None, false), | 198 | MacroDefKind::ProcMacro(..) => (info, None, false), |
diff --git a/crates/hir_expand/src/input.rs b/crates/hir_expand/src/input.rs index fe4790e7b..bc3ecc593 100644 --- a/crates/hir_expand/src/input.rs +++ b/crates/hir_expand/src/input.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! Macro input conditioning. | 1 | //! Macro input conditioning. |
2 | 2 | ||
3 | use syntax::{ | 3 | use syntax::{ |
4 | ast::{self, AttrsOwner}, | 4 | ast::{self, make, AttrsOwner}, |
5 | AstNode, SyntaxNode, | 5 | AstNode, SyntaxNode, |
6 | }; | 6 | }; |
7 | 7 | ||
@@ -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,19 @@ 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 | let syntax_index = attr.syntax().index(); | ||
65 | let ws = make::tokens::whitespace(&" ".repeat(u32::from(attr.syntax().text().len()) as usize)); | ||
66 | item.syntax().splice_children(syntax_index..syntax_index + 1, vec![ws.into()]); | ||
67 | item | ||
68 | } | ||
69 | |||
49 | #[cfg(test)] | 70 | #[cfg(test)] |
50 | mod tests { | 71 | mod tests { |
51 | use base_db::fixture::WithFixture; | 72 | use base_db::fixture::WithFixture; |
@@ -57,7 +78,7 @@ mod tests { | |||
57 | use super::*; | 78 | use super::*; |
58 | 79 | ||
59 | fn test_remove_derives_up_to(attr: usize, ra_fixture: &str, expect: Expect) { | 80 | fn test_remove_derives_up_to(attr: usize, ra_fixture: &str, expect: Expect) { |
60 | let (db, file_id) = TestDB::with_single_file(&ra_fixture); | 81 | let (db, file_id) = TestDB::with_single_file(ra_fixture); |
61 | let parsed = db.parse(file_id); | 82 | let parsed = db.parse(file_id); |
62 | 83 | ||
63 | let mut items: Vec<_> = | 84 | let mut items: Vec<_> = |
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index 90d8ae240..33107aa24 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs | |||
@@ -8,6 +8,7 @@ pub mod db; | |||
8 | pub mod ast_id_map; | 8 | pub mod ast_id_map; |
9 | pub mod name; | 9 | pub mod name; |
10 | pub mod hygiene; | 10 | pub mod hygiene; |
11 | pub mod builtin_attr; | ||
11 | pub mod builtin_derive; | 12 | pub mod builtin_derive; |
12 | pub mod builtin_macro; | 13 | pub mod builtin_macro; |
13 | pub mod proc_macro; | 14 | pub mod proc_macro; |
@@ -32,6 +33,7 @@ use syntax::{ | |||
32 | }; | 33 | }; |
33 | 34 | ||
34 | use crate::ast_id_map::FileAstId; | 35 | use crate::ast_id_map::FileAstId; |
36 | use crate::builtin_attr::BuiltinAttrExpander; | ||
35 | use crate::builtin_derive::BuiltinDeriveExpander; | 37 | use crate::builtin_derive::BuiltinDeriveExpander; |
36 | use crate::builtin_macro::{BuiltinFnLikeExpander, EagerExpander}; | 38 | use crate::builtin_macro::{BuiltinFnLikeExpander, EagerExpander}; |
37 | use crate::proc_macro::ProcMacroExpander; | 39 | use crate::proc_macro::ProcMacroExpander; |
@@ -51,7 +53,7 @@ mod test_db; | |||
51 | /// this is a recursive definition! However, the size_of of `HirFileId` is | 53 | /// this is a recursive definition! However, the size_of of `HirFileId` is |
52 | /// finite (because everything bottoms out at the real `FileId`) and small | 54 | /// finite (because everything bottoms out at the real `FileId`) and small |
53 | /// (`MacroCallId` uses the location interning. You can check details here: | 55 | /// (`MacroCallId` uses the location interning. You can check details here: |
54 | /// https://en.wikipedia.org/wiki/String_interning). | 56 | /// <https://en.wikipedia.org/wiki/String_interning>). |
55 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 57 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
56 | pub struct HirFileId(HirFileIdRepr); | 58 | pub struct HirFileId(HirFileIdRepr); |
57 | 59 | ||
@@ -206,6 +208,7 @@ impl MacroDefId { | |||
206 | let id = match &self.kind { | 208 | let id = match &self.kind { |
207 | MacroDefKind::Declarative(id) => id, | 209 | MacroDefKind::Declarative(id) => id, |
208 | MacroDefKind::BuiltIn(_, id) => id, | 210 | MacroDefKind::BuiltIn(_, id) => id, |
211 | MacroDefKind::BuiltInAttr(_, id) => id, | ||
209 | MacroDefKind::BuiltInDerive(_, id) => id, | 212 | MacroDefKind::BuiltInDerive(_, id) => id, |
210 | MacroDefKind::BuiltInEager(_, id) => id, | 213 | MacroDefKind::BuiltInEager(_, id) => id, |
211 | MacroDefKind::ProcMacro(.., id) => return Either::Right(*id), | 214 | MacroDefKind::ProcMacro(.., id) => return Either::Right(*id), |
@@ -223,6 +226,7 @@ pub enum MacroDefKind { | |||
223 | Declarative(AstId<ast::Macro>), | 226 | Declarative(AstId<ast::Macro>), |
224 | BuiltIn(BuiltinFnLikeExpander, AstId<ast::Macro>), | 227 | BuiltIn(BuiltinFnLikeExpander, AstId<ast::Macro>), |
225 | // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander | 228 | // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander |
229 | BuiltInAttr(BuiltinAttrExpander, AstId<ast::Macro>), | ||
226 | BuiltInDerive(BuiltinDeriveExpander, AstId<ast::Macro>), | 230 | BuiltInDerive(BuiltinDeriveExpander, AstId<ast::Macro>), |
227 | BuiltInEager(EagerExpander, AstId<ast::Macro>), | 231 | BuiltInEager(EagerExpander, AstId<ast::Macro>), |
228 | ProcMacro(ProcMacroExpander, ProcMacroKind, AstId<ast::Fn>), | 232 | ProcMacro(ProcMacroExpander, ProcMacroKind, AstId<ast::Fn>), |
@@ -258,14 +262,29 @@ pub enum MacroCallKind { | |||
258 | /// out-of-line modules, which may have attributes spread across 2 files! | 262 | /// out-of-line modules, which may have attributes spread across 2 files! |
259 | derive_attr_index: u32, | 263 | derive_attr_index: u32, |
260 | }, | 264 | }, |
265 | Attr { | ||
266 | ast_id: AstId<ast::Item>, | ||
267 | attr_name: String, | ||
268 | attr_args: tt::Subtree, | ||
269 | /// Syntactical index of the invoking `#[attribute]`. | ||
270 | /// | ||
271 | /// Outer attributes are counted first, then inner attributes. This does not support | ||
272 | /// out-of-line modules, which may have attributes spread across 2 files! | ||
273 | invoc_attr_index: u32, | ||
274 | }, | ||
261 | } | 275 | } |
262 | 276 | ||
277 | // FIXME: attribute indices do not account for `cfg_attr`, which means that we'll strip the whole | ||
278 | // `cfg_attr` instead of just one of the attributes it expands to | ||
279 | |||
263 | impl MacroCallKind { | 280 | impl MacroCallKind { |
264 | /// Returns the file containing the macro invocation. | 281 | /// Returns the file containing the macro invocation. |
265 | fn file_id(&self) -> HirFileId { | 282 | fn file_id(&self) -> HirFileId { |
266 | match self { | 283 | match self { |
267 | MacroCallKind::FnLike { ast_id, .. } => ast_id.file_id, | 284 | MacroCallKind::FnLike { ast_id, .. } => ast_id.file_id, |
268 | MacroCallKind::Derive { ast_id, .. } => ast_id.file_id, | 285 | MacroCallKind::Derive { ast_id, .. } | MacroCallKind::Attr { ast_id, .. } => { |
286 | ast_id.file_id | ||
287 | } | ||
269 | } | 288 | } |
270 | } | 289 | } |
271 | 290 | ||
@@ -274,7 +293,7 @@ impl MacroCallKind { | |||
274 | MacroCallKind::FnLike { ast_id, .. } => { | 293 | MacroCallKind::FnLike { ast_id, .. } => { |
275 | ast_id.with_value(ast_id.to_node(db).syntax().clone()) | 294 | ast_id.with_value(ast_id.to_node(db).syntax().clone()) |
276 | } | 295 | } |
277 | MacroCallKind::Derive { ast_id, .. } => { | 296 | MacroCallKind::Derive { ast_id, .. } | MacroCallKind::Attr { ast_id, .. } => { |
278 | ast_id.with_value(ast_id.to_node(db).syntax().clone()) | 297 | ast_id.with_value(ast_id.to_node(db).syntax().clone()) |
279 | } | 298 | } |
280 | } | 299 | } |
@@ -285,7 +304,9 @@ impl MacroCallKind { | |||
285 | MacroCallKind::FnLike { ast_id, .. } => { | 304 | MacroCallKind::FnLike { ast_id, .. } => { |
286 | Some(ast_id.to_node(db).token_tree()?.syntax().clone()) | 305 | Some(ast_id.to_node(db).token_tree()?.syntax().clone()) |
287 | } | 306 | } |
288 | MacroCallKind::Derive { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()), | 307 | MacroCallKind::Derive { ast_id, .. } | MacroCallKind::Attr { ast_id, .. } => { |
308 | Some(ast_id.to_node(db).syntax().clone()) | ||
309 | } | ||
289 | } | 310 | } |
290 | } | 311 | } |
291 | 312 | ||
@@ -293,6 +314,7 @@ impl MacroCallKind { | |||
293 | match self { | 314 | match self { |
294 | MacroCallKind::FnLike { fragment, .. } => *fragment, | 315 | MacroCallKind::FnLike { fragment, .. } => *fragment, |
295 | MacroCallKind::Derive { .. } => FragmentKind::Items, | 316 | MacroCallKind::Derive { .. } => FragmentKind::Items, |
317 | MacroCallKind::Attr { .. } => FragmentKind::Items, // is this always correct? | ||
296 | } | 318 | } |
297 | } | 319 | } |
298 | } | 320 | } |
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs index 00b8adc1e..376fe130f 100644 --- a/crates/hir_expand/src/name.rs +++ b/crates/hir_expand/src/name.rs | |||
@@ -160,7 +160,6 @@ pub mod known { | |||
160 | str, | 160 | str, |
161 | // Special names | 161 | // Special names |
162 | macro_rules, | 162 | macro_rules, |
163 | derive, | ||
164 | doc, | 163 | doc, |
165 | cfg, | 164 | cfg, |
166 | cfg_attr, | 165 | cfg_attr, |
@@ -240,6 +239,14 @@ pub mod known { | |||
240 | PartialOrd, | 239 | PartialOrd, |
241 | Eq, | 240 | Eq, |
242 | PartialEq, | 241 | PartialEq, |
242 | // Builtin attributes | ||
243 | bench, | ||
244 | cfg_accessible, | ||
245 | cfg_eval, | ||
246 | derive, | ||
247 | global_allocator, | ||
248 | test, | ||
249 | test_case, | ||
243 | // Safe intrinsics | 250 | // Safe intrinsics |
244 | abort, | 251 | abort, |
245 | size_of, | 252 | size_of, |
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs index d5643393a..025e10239 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) => { |
@@ -40,13 +45,12 @@ impl ProcMacroExpander { | |||
40 | let proc_macro = krate_graph[self.krate] | 45 | let proc_macro = krate_graph[self.krate] |
41 | .proc_macro | 46 | .proc_macro |
42 | .get(id.0 as usize) | 47 | .get(id.0 as usize) |
43 | .clone() | ||
44 | .ok_or_else(|| err!("No derive macro found."))?; | 48 | .ok_or_else(|| err!("No derive macro found."))?; |
45 | 49 | ||
46 | // Proc macros have access to the environment variables of the invoking crate. | 50 | // Proc macros have access to the environment variables of the invoking crate. |
47 | let env = &krate_graph[calling_crate].env; | 51 | let env = &krate_graph[calling_crate].env; |
48 | 52 | ||
49 | proc_macro.expander.expand(&tt, None, &env).map_err(mbe::ExpandError::from) | 53 | proc_macro.expander.expand(tt, attr_arg, env).map_err(mbe::ExpandError::from) |
50 | } | 54 | } |
51 | None => Err(mbe::ExpandError::UnresolvedProcMacro), | 55 | None => Err(mbe::ExpandError::UnresolvedProcMacro), |
52 | } | 56 | } |