Skip to content

Commit bb1d925

Browse files
Merge #8212
8212: Basic support macro 2.0 r=jonas-schievink a=edwin0cheng Turn out it is quite straight forward :) r @jonas-schievink ![m2](https://user-images.githubusercontent.com/11014119/112712565-6eb99380-8f0b-11eb-88de-5d7f974dfe6d.png) Co-authored-by: Edwin Cheng <[email protected]>
2 parents ce6bb5c + 7729879 commit bb1d925

File tree

21 files changed

+287
-96
lines changed

21 files changed

+287
-96
lines changed

crates/hir/src/semantics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -768,7 +768,7 @@ to_def_impls![
768768
(crate::TypeParam, ast::TypeParam, type_param_to_def),
769769
(crate::LifetimeParam, ast::LifetimeParam, lifetime_param_to_def),
770770
(crate::ConstParam, ast::ConstParam, const_param_to_def),
771-
(crate::MacroDef, ast::MacroRules, macro_rules_to_def),
771+
(crate::MacroDef, ast::Macro, macro_to_def),
772772
(crate::Local, ast::IdentPat, bind_pat_to_def),
773773
(crate::Local, ast::SelfParam, self_param_to_def),
774774
(crate::Label, ast::Label, label_to_def),

crates/hir/src/semantics/source_to_def.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,7 @@ impl SourceToDefCtx<'_, '_> {
191191
}
192192

193193
// FIXME: use DynMap as well?
194-
pub(super) fn macro_rules_to_def(
195-
&mut self,
196-
src: InFile<ast::MacroRules>,
197-
) -> Option<MacroDefId> {
194+
pub(super) fn macro_to_def(&mut self, src: InFile<ast::Macro>) -> Option<MacroDefId> {
198195
let file_ast_id = self.db.ast_id_map(src.file_id).ast_id(&src.value);
199196
let ast_id = AstId::new(src.file_id, file_ast_id.upcast());
200197
let kind = MacroDefKind::Declarative(ast_id);

crates/hir_def/src/nameres/collector.rs

Lines changed: 63 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ use crate::{
2525
derive_macro_as_call_id,
2626
item_scope::{ImportType, PerNsGlobImports},
2727
item_tree::{
28-
self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, MacroRules, Mod, ModItem, ModKind,
29-
StructDefKind,
28+
self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, MacroDef, MacroRules, Mod, ModItem,
29+
ModKind, StructDefKind,
3030
},
3131
macro_call_as_call_id,
3232
nameres::{
@@ -395,7 +395,7 @@ impl DefCollector<'_> {
395395
/// macro_rules! foo { () => {} }
396396
/// use foo as bar;
397397
/// ```
398-
fn define_macro(
398+
fn define_macro_rules(
399399
&mut self,
400400
module_id: LocalModuleId,
401401
name: Name,
@@ -430,6 +430,21 @@ impl DefCollector<'_> {
430430
self.def_map.modules[module_id].scope.define_legacy_macro(name, mac);
431431
}
432432

433+
/// Define a macro 2.0 macro
434+
///
435+
/// The scoped of macro 2.0 macro is equal to normal function
436+
fn define_macro_def(
437+
&mut self,
438+
module_id: LocalModuleId,
439+
name: Name,
440+
macro_: MacroDefId,
441+
vis: &RawVisibility,
442+
) {
443+
let vis =
444+
self.def_map.resolve_visibility(self.db, module_id, vis).unwrap_or(Visibility::Public);
445+
self.update(module_id, &[(Some(name), PerNs::macros(macro_, vis))], vis, ImportType::Named);
446+
}
447+
433448
/// Define a proc macro
434449
///
435450
/// A proc macro is similar to normal macro scope, but it would not visible in legacy textual scoped.
@@ -1067,40 +1082,7 @@ impl ModCollector<'_, '_> {
10671082
}
10681083
ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac]),
10691084
ModItem::MacroRules(id) => self.collect_macro_rules(id),
1070-
ModItem::MacroDef(id) => {
1071-
let mac = &self.item_tree[id];
1072-
let ast_id = InFile::new(self.file_id, mac.ast_id.upcast());
1073-
1074-
// "Macro 2.0" is not currently supported by rust-analyzer, but libcore uses it
1075-
// to define builtin macros, so we support at least that part.
1076-
let attrs = self.item_tree.attrs(
1077-
self.def_collector.db,
1078-
krate,
1079-
ModItem::from(id).into(),
1080-
);
1081-
if attrs.by_key("rustc_builtin_macro").exists() {
1082-
let krate = self.def_collector.def_map.krate;
1083-
let macro_id = find_builtin_macro(&mac.name, krate, ast_id)
1084-
.or_else(|| find_builtin_derive(&mac.name, krate, ast_id));
1085-
if let Some(macro_id) = macro_id {
1086-
let vis = self
1087-
.def_collector
1088-
.def_map
1089-
.resolve_visibility(
1090-
self.def_collector.db,
1091-
self.module_id,
1092-
&self.item_tree[mac.visibility],
1093-
)
1094-
.unwrap_or(Visibility::Public);
1095-
self.def_collector.update(
1096-
self.module_id,
1097-
&[(Some(mac.name.clone()), PerNs::macros(macro_id, vis))],
1098-
vis,
1099-
ImportType::Named,
1100-
);
1101-
}
1102-
}
1103-
}
1085+
ModItem::MacroDef(id) => self.collect_macro_def(id),
11041086
ModItem::Impl(imp) => {
11051087
let module = self.def_collector.def_map.module_id(self.module_id);
11061088
let impl_id =
@@ -1420,7 +1402,7 @@ impl ModCollector<'_, '_> {
14201402
if attrs.by_key("rustc_builtin_macro").exists() {
14211403
let krate = self.def_collector.def_map.krate;
14221404
if let Some(macro_id) = find_builtin_macro(&mac.name, krate, ast_id) {
1423-
self.def_collector.define_macro(
1405+
self.def_collector.define_macro_rules(
14241406
self.module_id,
14251407
mac.name.clone(),
14261408
macro_id,
@@ -1436,7 +1418,49 @@ impl ModCollector<'_, '_> {
14361418
kind: MacroDefKind::Declarative(ast_id),
14371419
local_inner: is_local_inner,
14381420
};
1439-
self.def_collector.define_macro(self.module_id, mac.name.clone(), macro_id, is_export);
1421+
self.def_collector.define_macro_rules(
1422+
self.module_id,
1423+
mac.name.clone(),
1424+
macro_id,
1425+
is_export,
1426+
);
1427+
}
1428+
1429+
fn collect_macro_def(&mut self, id: FileItemTreeId<MacroDef>) {
1430+
let krate = self.def_collector.def_map.krate;
1431+
let mac = &self.item_tree[id];
1432+
let ast_id = InFile::new(self.file_id, mac.ast_id.upcast());
1433+
1434+
// Case 1: bulitin macros
1435+
let attrs = self.item_tree.attrs(self.def_collector.db, krate, ModItem::from(id).into());
1436+
if attrs.by_key("rustc_builtin_macro").exists() {
1437+
let macro_id = find_builtin_macro(&mac.name, krate, ast_id)
1438+
.or_else(|| find_builtin_derive(&mac.name, krate, ast_id));
1439+
1440+
if let Some(macro_id) = macro_id {
1441+
self.def_collector.define_macro_def(
1442+
self.module_id,
1443+
mac.name.clone(),
1444+
macro_id,
1445+
&self.item_tree[mac.visibility],
1446+
);
1447+
}
1448+
return;
1449+
}
1450+
1451+
// Case 2: normal `macro`
1452+
let macro_id = MacroDefId {
1453+
krate: self.def_collector.def_map.krate,
1454+
kind: MacroDefKind::Declarative(ast_id),
1455+
local_inner: false,
1456+
};
1457+
1458+
self.def_collector.define_macro_def(
1459+
self.module_id,
1460+
mac.name.clone(),
1461+
macro_id,
1462+
&self.item_tree[mac.visibility],
1463+
);
14401464
}
14411465

14421466
fn collect_macro_call(&mut self, mac: &MacroCall) {

crates/hir_def/src/nameres/tests/macros.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,3 +837,21 @@ fn collects_derive_helpers() {
837837
_ => unreachable!(),
838838
}
839839
}
840+
841+
#[test]
842+
fn resolve_macro_def() {
843+
check(
844+
r#"
845+
//- /lib.rs
846+
pub macro structs($($i:ident),*) {
847+
$(struct $i { field: u32 } )*
848+
}
849+
structs!(Foo);
850+
"#,
851+
expect![[r#"
852+
crate
853+
Foo: t
854+
structs: m
855+
"#]],
856+
);
857+
}

crates/hir_expand/src/db.rs

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use std::sync::Arc;
44

55
use base_db::{salsa, SourceDatabase};
6-
use mbe::{ExpandError, ExpandResult, MacroRules};
6+
use mbe::{ExpandError, ExpandResult, MacroDef, MacroRules};
77
use parser::FragmentKind;
88
use syntax::{
99
algo::diff,
@@ -28,6 +28,7 @@ const TOKEN_LIMIT: usize = 524288;
2828
#[derive(Debug, Clone, Eq, PartialEq)]
2929
pub enum TokenExpander {
3030
MacroRules(mbe::MacroRules),
31+
MacroDef(mbe::MacroDef),
3132
Builtin(BuiltinFnLikeExpander),
3233
BuiltinDerive(BuiltinDeriveExpander),
3334
ProcMacro(ProcMacroExpander),
@@ -42,6 +43,7 @@ impl TokenExpander {
4243
) -> mbe::ExpandResult<tt::Subtree> {
4344
match self {
4445
TokenExpander::MacroRules(it) => it.expand(tt),
46+
TokenExpander::MacroDef(it) => it.expand(tt),
4547
TokenExpander::Builtin(it) => it.expand(db, id, tt),
4648
// FIXME switch these to ExpandResult as well
4749
TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(),
@@ -57,6 +59,7 @@ impl TokenExpander {
5759
pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId {
5860
match self {
5961
TokenExpander::MacroRules(it) => it.map_id_down(id),
62+
TokenExpander::MacroDef(it) => it.map_id_down(id),
6063
TokenExpander::Builtin(..) => id,
6164
TokenExpander::BuiltinDerive(..) => id,
6265
TokenExpander::ProcMacro(..) => id,
@@ -66,6 +69,7 @@ impl TokenExpander {
6669
pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) {
6770
match self {
6871
TokenExpander::MacroRules(it) => it.map_id_up(id),
72+
TokenExpander::MacroDef(it) => it.map_id_up(id),
6973
TokenExpander::Builtin(..) => (id, mbe::Origin::Call),
7074
TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call),
7175
TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call),
@@ -136,26 +140,40 @@ fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
136140

137141
fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
138142
match id.kind {
139-
MacroDefKind::Declarative(ast_id) => {
140-
let macro_rules = match ast_id.to_node(db) {
141-
syntax::ast::Macro::MacroRules(mac) => mac,
142-
syntax::ast::Macro::MacroDef(_) => return None,
143-
};
144-
let arg = macro_rules.token_tree()?;
145-
let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| {
146-
log::warn!("fail on macro_def to token tree: {:#?}", arg);
147-
None
148-
})?;
149-
let rules = match MacroRules::parse(&tt) {
150-
Ok(it) => it,
151-
Err(err) => {
152-
let name = macro_rules.name().map(|n| n.to_string()).unwrap_or_default();
153-
log::warn!("fail on macro_def parse ({}): {:?} {:#?}", name, err, tt);
154-
return None;
155-
}
156-
};
157-
Some(Arc::new((TokenExpander::MacroRules(rules), tmap)))
158-
}
143+
MacroDefKind::Declarative(ast_id) => match ast_id.to_node(db) {
144+
syntax::ast::Macro::MacroRules(macro_rules) => {
145+
let arg = macro_rules.token_tree()?;
146+
let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| {
147+
log::warn!("fail on macro_rules to token tree: {:#?}", arg);
148+
None
149+
})?;
150+
let rules = match MacroRules::parse(&tt) {
151+
Ok(it) => it,
152+
Err(err) => {
153+
let name = macro_rules.name().map(|n| n.to_string()).unwrap_or_default();
154+
log::warn!("fail on macro_def parse ({}): {:?} {:#?}", name, err, tt);
155+
return None;
156+
}
157+
};
158+
Some(Arc::new((TokenExpander::MacroRules(rules), tmap)))
159+
}
160+
syntax::ast::Macro::MacroDef(macro_def) => {
161+
let arg = macro_def.body()?;
162+
let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| {
163+
log::warn!("fail on macro_def to token tree: {:#?}", arg);
164+
None
165+
})?;
166+
let rules = match MacroDef::parse(&tt) {
167+
Ok(it) => it,
168+
Err(err) => {
169+
let name = macro_def.name().map(|n| n.to_string()).unwrap_or_default();
170+
log::warn!("fail on macro_def parse ({}): {:?} {:#?}", name, err, tt);
171+
return None;
172+
}
173+
};
174+
Some(Arc::new((TokenExpander::MacroDef(rules), tmap)))
175+
}
176+
},
159177
MacroDefKind::BuiltIn(expander, _) => {
160178
Some(Arc::new((TokenExpander::Builtin(expander), mbe::TokenMap::default())))
161179
}

crates/hir_expand/src/hygiene.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ fn make_hygiene_info(
148148
let def_offset = loc.def.ast_id().left().and_then(|id| {
149149
let def_tt = match id.to_node(db) {
150150
ast::Macro::MacroRules(mac) => mac.token_tree()?.syntax().text_range().start(),
151-
ast::Macro::MacroDef(_) => return None,
151+
ast::Macro::MacroDef(mac) => mac.body()?.syntax().text_range().start(),
152152
};
153153
Some(InFile::new(id.file_id, def_tt))
154154
});

crates/hir_expand/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ impl HirFileId {
151151
let def = loc.def.ast_id().left().and_then(|id| {
152152
let def_tt = match id.to_node(db) {
153153
ast::Macro::MacroRules(mac) => mac.token_tree()?,
154-
ast::Macro::MacroDef(_) => return None,
154+
ast::Macro::MacroDef(mac) => mac.body()?,
155155
};
156156
Some(InFile::new(id.file_id, def_tt))
157157
});

crates/hir_ty/src/tests/macros.rs

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,88 @@ fn infer_path_qualified_macros_expanded() {
135135
}
136136

137137
#[test]
138-
fn expr_macro_expanded_in_various_places() {
138+
fn expr_macro_def_expanded_in_various_places() {
139+
check_infer(
140+
r#"
141+
macro spam() {
142+
1isize
143+
}
144+
145+
fn spam() {
146+
spam!();
147+
(spam!());
148+
spam!().spam(spam!());
149+
for _ in spam!() {}
150+
|| spam!();
151+
while spam!() {}
152+
break spam!();
153+
return spam!();
154+
match spam!() {
155+
_ if spam!() => spam!(),
156+
}
157+
spam!()(spam!());
158+
Spam { spam: spam!() };
159+
spam!()[spam!()];
160+
await spam!();
161+
spam!() as usize;
162+
&spam!();
163+
-spam!();
164+
spam!()..spam!();
165+
spam!() + spam!();
166+
}
167+
"#,
168+
expect![[r#"
169+
!0..6 '1isize': isize
170+
!0..6 '1isize': isize
171+
!0..6 '1isize': isize
172+
!0..6 '1isize': isize
173+
!0..6 '1isize': isize
174+
!0..6 '1isize': isize
175+
!0..6 '1isize': isize
176+
!0..6 '1isize': isize
177+
!0..6 '1isize': isize
178+
!0..6 '1isize': isize
179+
!0..6 '1isize': isize
180+
!0..6 '1isize': isize
181+
!0..6 '1isize': isize
182+
!0..6 '1isize': isize
183+
!0..6 '1isize': isize
184+
!0..6 '1isize': isize
185+
!0..6 '1isize': isize
186+
!0..6 '1isize': isize
187+
!0..6 '1isize': isize
188+
!0..6 '1isize': isize
189+
!0..6 '1isize': isize
190+
!0..6 '1isize': isize
191+
!0..6 '1isize': isize
192+
!0..6 '1isize': isize
193+
!0..6 '1isize': isize
194+
39..442 '{ ...!(); }': ()
195+
73..94 'spam!(...am!())': {unknown}
196+
100..119 'for _ ...!() {}': ()
197+
104..105 '_': {unknown}
198+
117..119 '{}': ()
199+
124..134 '|| spam!()': || -> isize
200+
140..156 'while ...!() {}': ()
201+
154..156 '{}': ()
202+
161..174 'break spam!()': !
203+
180..194 'return spam!()': !
204+
200..254 'match ... }': isize
205+
224..225 '_': isize
206+
259..275 'spam!(...am!())': {unknown}
207+
281..303 'Spam {...m!() }': {unknown}
208+
309..325 'spam!(...am!()]': {unknown}
209+
350..366 'spam!(... usize': usize
210+
372..380 '&spam!()': &isize
211+
386..394 '-spam!()': isize
212+
400..416 'spam!(...pam!()': {unknown}
213+
422..439 'spam!(...pam!()': isize
214+
"#]],
215+
);
216+
}
217+
218+
#[test]
219+
fn expr_macro_rules_expanded_in_various_places() {
139220
check_infer(
140221
r#"
141222
macro_rules! spam {

0 commit comments

Comments
 (0)