aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_expand/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_expand/src')
-rw-r--r--crates/hir_expand/src/builtin_attr.rs67
-rw-r--r--crates/hir_expand/src/builtin_derive.rs2
-rw-r--r--crates/hir_expand/src/builtin_macro.rs14
-rw-r--r--crates/hir_expand/src/db.rs27
-rw-r--r--crates/hir_expand/src/eager.rs20
-rw-r--r--crates/hir_expand/src/hygiene.rs1
-rw-r--r--crates/hir_expand/src/input.rs25
-rw-r--r--crates/hir_expand/src/lib.rs30
-rw-r--r--crates/hir_expand/src/name.rs9
-rw-r--r--crates/hir_expand/src/proc_macro.rs8
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
3use syntax::ast;
4
5use crate::{db::AstDatabase, name, AstId, CrateId, MacroCallId, MacroDefId, MacroDefKind};
6
7macro_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
38register_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
48pub 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
61fn 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
14use crate::{ 14use 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 {
85pub trait AstDatabase: SourceDatabase { 90pub 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, &macro_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, &macro_arg.0, attr_arg)
381} 394}
382 395
383fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool { 396fn 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
22use crate::{ 22use 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(&macro_call.value); 177 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
178 178
179 let fragment = crate::to_fragment_kind(&macro_call.value); 179 let fragment = crate::to_fragment_kind(&macro_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
3use syntax::{ 3use 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`.
58fn 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)]
50mod tests { 71mod 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;
8pub mod ast_id_map; 8pub mod ast_id_map;
9pub mod name; 9pub mod name;
10pub mod hygiene; 10pub mod hygiene;
11pub mod builtin_attr;
11pub mod builtin_derive; 12pub mod builtin_derive;
12pub mod builtin_macro; 13pub mod builtin_macro;
13pub mod proc_macro; 14pub mod proc_macro;
@@ -32,6 +33,7 @@ use syntax::{
32}; 33};
33 34
34use crate::ast_id_map::FileAstId; 35use crate::ast_id_map::FileAstId;
36use crate::builtin_attr::BuiltinAttrExpander;
35use crate::builtin_derive::BuiltinDeriveExpander; 37use crate::builtin_derive::BuiltinDeriveExpander;
36use crate::builtin_macro::{BuiltinFnLikeExpander, EagerExpander}; 38use crate::builtin_macro::{BuiltinFnLikeExpander, EagerExpander};
37use crate::proc_macro::ProcMacroExpander; 39use 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)]
56pub struct HirFileId(HirFileIdRepr); 58pub 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
263impl MacroCallKind { 280impl 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 }