Skip to content

Commit efa72c8

Browse files
Merge #3591
3591: Support local macro_rules r=matklad a=edwin0cheng This PR implement local `macro_rules` in function body, by adding following things: 1. While lowering, add a `MacroDefId` in body's `ItemScope` as a textual legacy macro. 2. Make `Expander::enter_expand` search with given `ItemScope`. 3. Make `Resolver::resolve_path_as_macro` search with `LocalItemScope`. Fix #2181 Co-authored-by: Edwin Cheng <edwin0cheng@gmail.com>
2 parents 5429e68 + fe78a14 commit efa72c8

File tree

6 files changed

+96
-16
lines changed

6 files changed

+96
-16
lines changed

crates/ra_hir_def/src/body.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,19 @@ impl Expander {
4747
pub(crate) fn enter_expand<T: ast::AstNode, DB: DefDatabase>(
4848
&mut self,
4949
db: &DB,
50+
local_scope: Option<&ItemScope>,
5051
macro_call: ast::MacroCall,
5152
) -> Option<(Mark, T)> {
5253
let macro_call = InFile::new(self.current_file_id, &macro_call);
5354

54-
if let Some(call_id) =
55-
macro_call.as_call_id(db, |path| self.resolve_path_as_macro(db, &path))
56-
{
55+
if let Some(call_id) = macro_call.as_call_id(db, |path| {
56+
if let Some(local_scope) = local_scope {
57+
if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) {
58+
return Some(def);
59+
}
60+
}
61+
self.resolve_path_as_macro(db, &path)
62+
}) {
5763
let file_id = call_id.as_file();
5864
if let Some(node) = db.parse_or_expand(file_id) {
5965
if let Some(expr) = T::cast(node) {

crates/ra_hir_def/src/body/lower.rs

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
44
use either::Either;
55

6-
use hir_expand::name::{name, AsName, Name};
6+
use hir_expand::{
7+
name::{name, AsName, Name},
8+
MacroDefId, MacroDefKind,
9+
};
710
use ra_arena::Arena;
811
use ra_syntax::{
912
ast::{
@@ -452,19 +455,30 @@ where
452455
None => self.alloc_expr(Expr::Missing, syntax_ptr),
453456
}
454457
}
455-
// FIXME expand to statements in statement position
456458
ast::Expr::MacroCall(e) => {
457-
let macro_call = self.expander.to_source(AstPtr::new(&e));
458-
match self.expander.enter_expand(self.db, e) {
459-
Some((mark, expansion)) => {
460-
self.source_map
461-
.expansions
462-
.insert(macro_call, self.expander.current_file_id);
463-
let id = self.collect_expr(expansion);
464-
self.expander.exit(self.db, mark);
465-
id
459+
if let Some(name) = is_macro_rules(&e) {
460+
let mac = MacroDefId {
461+
krate: Some(self.expander.module.krate),
462+
ast_id: Some(self.expander.ast_id(&e)),
463+
kind: MacroDefKind::Declarative,
464+
};
465+
self.body.item_scope.define_legacy_macro(name, mac);
466+
467+
// FIXME: do we still need to allocate this as missing ?
468+
self.alloc_expr(Expr::Missing, syntax_ptr)
469+
} else {
470+
let macro_call = self.expander.to_source(AstPtr::new(&e));
471+
match self.expander.enter_expand(self.db, Some(&self.body.item_scope), e) {
472+
Some((mark, expansion)) => {
473+
self.source_map
474+
.expansions
475+
.insert(macro_call, self.expander.current_file_id);
476+
let id = self.collect_expr(expansion);
477+
self.expander.exit(self.db, mark);
478+
id
479+
}
480+
None => self.alloc_expr(Expr::Missing, syntax_ptr),
466481
}
467-
None => self.alloc_expr(Expr::Missing, syntax_ptr),
468482
}
469483
}
470484

@@ -686,6 +700,16 @@ where
686700
}
687701
}
688702

703+
fn is_macro_rules(m: &ast::MacroCall) -> Option<Name> {
704+
let name = m.path()?.segment()?.name_ref()?.as_name();
705+
706+
if name == name![macro_rules] {
707+
Some(m.name()?.as_name())
708+
} else {
709+
None
710+
}
711+
}
712+
689713
impl From<ast::BinOp> for BinaryOp {
690714
fn from(ast_op: ast::BinOp) -> Self {
691715
match ast_op {

crates/ra_hir_def/src/data.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ fn collect_impl_items_in_macro(
290290
return Vec::new();
291291
}
292292

293-
if let Some((mark, items)) = expander.enter_expand(db, m) {
293+
if let Some((mark, items)) = expander.enter_expand(db, None, m) {
294294
let items: InFile<ast::MacroItems> = expander.to_source(items);
295295
let mut res = collect_impl_items(
296296
db,

crates/ra_hir_def/src/resolver.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,11 @@ impl Resolver {
381381
db: &impl DefDatabase,
382382
path: &ModPath,
383383
) -> Option<MacroDefId> {
384+
// Search item scope legacy macro first
385+
if let Some(def) = self.resolve_local_macro_def(path) {
386+
return Some(def);
387+
}
388+
384389
let (item_map, module) = self.module_scope()?;
385390
item_map.resolve_path(db, module, &path, BuiltinShadowMode::Other).0.take_macros()
386391
}
@@ -413,6 +418,16 @@ impl Resolver {
413418
})
414419
}
415420

421+
fn resolve_local_macro_def(&self, path: &ModPath) -> Option<MacroDefId> {
422+
let name = path.as_ident()?;
423+
self.scopes.iter().rev().find_map(|scope| {
424+
if let Scope::LocalItemsScope(body) = scope {
425+
return body.item_scope.get_legacy_macro(name);
426+
}
427+
None
428+
})
429+
}
430+
416431
pub fn module(&self) -> Option<ModuleId> {
417432
let (def_map, local_id) = self.module_scope()?;
418433
Some(ModuleId { krate: def_map.krate, local_id })

crates/ra_hir_ty/src/tests/macros.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,26 @@ fn main() {
362362
);
363363
}
364364

365+
#[test]
366+
fn infer_local_macro() {
367+
assert_snapshot!(
368+
infer(r#"
369+
fn main() {
370+
macro_rules! foo {
371+
() => { 1usize }
372+
}
373+
let _a = foo!();
374+
}
375+
"#),
376+
@r###"
377+
![0; 6) '1usize': usize
378+
[11; 90) '{ ...!(); }': ()
379+
[17; 66) 'macro_... }': {unknown}
380+
[75; 77) '_a': usize
381+
"###
382+
);
383+
}
384+
365385
#[test]
366386
fn infer_builtin_macros_line() {
367387
assert_snapshot!(

crates/ra_ide/src/goto_definition.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,21 @@ mod tests {
787787
);
788788
}
789789

790+
#[test]
791+
fn goto_def_in_local_macro() {
792+
check_goto(
793+
"
794+
//- /lib.rs
795+
fn bar() {
796+
macro_rules! foo { () => { () } }
797+
<|>foo!();
798+
}
799+
",
800+
"foo MACRO_CALL FileId(1) [15; 48) [28; 31)",
801+
"macro_rules! foo { () => { () } }|foo",
802+
);
803+
}
804+
790805
#[test]
791806
fn goto_def_for_field_init_shorthand() {
792807
covers!(ra_ide_db::goto_def_for_field_init_shorthand);

0 commit comments

Comments
 (0)