From fe78a14bbb9769c8ccd5cc41415702f5176a8e88 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sat, 14 Mar 2020 14:25:30 +0800 Subject: Support local macro_rules --- crates/ra_hir_def/src/body.rs | 12 ++++++--- crates/ra_hir_def/src/body/lower.rs | 48 +++++++++++++++++++++++++++--------- crates/ra_hir_def/src/data.rs | 2 +- crates/ra_hir_def/src/resolver.rs | 15 +++++++++++ crates/ra_hir_ty/src/tests/macros.rs | 20 +++++++++++++++ crates/ra_ide/src/goto_definition.rs | 15 +++++++++++ 6 files changed, 96 insertions(+), 16 deletions(-) diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs index 57ba45b45..2bc405a59 100644 --- a/crates/ra_hir_def/src/body.rs +++ b/crates/ra_hir_def/src/body.rs @@ -47,13 +47,19 @@ impl Expander { pub(crate) fn enter_expand( &mut self, db: &DB, + local_scope: Option<&ItemScope>, macro_call: ast::MacroCall, ) -> Option<(Mark, T)> { let macro_call = InFile::new(self.current_file_id, ¯o_call); - if let Some(call_id) = - macro_call.as_call_id(db, |path| self.resolve_path_as_macro(db, &path)) - { + if let Some(call_id) = macro_call.as_call_id(db, |path| { + if let Some(local_scope) = local_scope { + if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) { + return Some(def); + } + } + self.resolve_path_as_macro(db, &path) + }) { let file_id = call_id.as_file(); if let Some(node) = db.parse_or_expand(file_id) { if let Some(expr) = T::cast(node) { diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs index ec1b0c2e7..54b5591d3 100644 --- a/crates/ra_hir_def/src/body/lower.rs +++ b/crates/ra_hir_def/src/body/lower.rs @@ -3,7 +3,10 @@ use either::Either; -use hir_expand::name::{name, AsName, Name}; +use hir_expand::{ + name::{name, AsName, Name}, + MacroDefId, MacroDefKind, +}; use ra_arena::Arena; use ra_syntax::{ ast::{ @@ -452,19 +455,30 @@ where None => self.alloc_expr(Expr::Missing, syntax_ptr), } } - // FIXME expand to statements in statement position ast::Expr::MacroCall(e) => { - let macro_call = self.expander.to_source(AstPtr::new(&e)); - match self.expander.enter_expand(self.db, e) { - Some((mark, expansion)) => { - self.source_map - .expansions - .insert(macro_call, self.expander.current_file_id); - let id = self.collect_expr(expansion); - self.expander.exit(self.db, mark); - id + if let Some(name) = is_macro_rules(&e) { + let mac = MacroDefId { + krate: Some(self.expander.module.krate), + ast_id: Some(self.expander.ast_id(&e)), + kind: MacroDefKind::Declarative, + }; + self.body.item_scope.define_legacy_macro(name, mac); + + // FIXME: do we still need to allocate this as missing ? + self.alloc_expr(Expr::Missing, syntax_ptr) + } else { + let macro_call = self.expander.to_source(AstPtr::new(&e)); + match self.expander.enter_expand(self.db, Some(&self.body.item_scope), e) { + Some((mark, expansion)) => { + self.source_map + .expansions + .insert(macro_call, self.expander.current_file_id); + let id = self.collect_expr(expansion); + self.expander.exit(self.db, mark); + id + } + None => self.alloc_expr(Expr::Missing, syntax_ptr), } - None => self.alloc_expr(Expr::Missing, syntax_ptr), } } @@ -686,6 +700,16 @@ where } } +fn is_macro_rules(m: &ast::MacroCall) -> Option { + let name = m.path()?.segment()?.name_ref()?.as_name(); + + if name == name![macro_rules] { + Some(m.name()?.as_name()) + } else { + None + } +} + impl From for BinaryOp { fn from(ast_op: ast::BinOp) -> Self { match ast_op { diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index a72eb5369..eb4c5de45 100644 --- a/crates/ra_hir_def/src/data.rs +++ b/crates/ra_hir_def/src/data.rs @@ -280,7 +280,7 @@ fn collect_impl_items_in_macro( return Vec::new(); } - if let Some((mark, items)) = expander.enter_expand(db, m) { + if let Some((mark, items)) = expander.enter_expand(db, None, m) { let items: InFile = expander.to_source(items); let mut res = collect_impl_items( db, diff --git a/crates/ra_hir_def/src/resolver.rs b/crates/ra_hir_def/src/resolver.rs index 2734d51a0..123fae72a 100644 --- a/crates/ra_hir_def/src/resolver.rs +++ b/crates/ra_hir_def/src/resolver.rs @@ -381,6 +381,11 @@ impl Resolver { db: &impl DefDatabase, path: &ModPath, ) -> Option { + // Search item scope legacy macro first + if let Some(def) = self.resolve_local_macro_def(path) { + return Some(def); + } + let (item_map, module) = self.module_scope()?; item_map.resolve_path(db, module, &path, BuiltinShadowMode::Other).0.take_macros() } @@ -413,6 +418,16 @@ impl Resolver { }) } + fn resolve_local_macro_def(&self, path: &ModPath) -> Option { + let name = path.as_ident()?; + self.scopes.iter().rev().find_map(|scope| { + if let Scope::LocalItemsScope(body) = scope { + return body.item_scope.get_legacy_macro(name); + } + None + }) + } + pub fn module(&self) -> Option { let (def_map, local_id) = self.module_scope()?; Some(ModuleId { krate: def_map.krate, local_id }) diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs index 32457bbf7..3b7022ad5 100644 --- a/crates/ra_hir_ty/src/tests/macros.rs +++ b/crates/ra_hir_ty/src/tests/macros.rs @@ -362,6 +362,26 @@ fn main() { ); } +#[test] +fn infer_local_macro() { + assert_snapshot!( + infer(r#" +fn main() { + macro_rules! foo { + () => { 1usize } + } + let _a = foo!(); +} +"#), + @r###" + ![0; 6) '1usize': usize + [11; 90) '{ ...!(); }': () + [17; 66) 'macro_... }': {unknown} + [75; 77) '_a': usize + "### + ); +} + #[test] fn infer_builtin_macros_line() { assert_snapshot!( diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index a55a13ffc..a7be92ce3 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs @@ -787,6 +787,21 @@ mod tests { ); } + #[test] + fn goto_def_in_local_macro() { + check_goto( + " + //- /lib.rs + fn bar() { + macro_rules! foo { () => { () } } + <|>foo!(); + } + ", + "foo MACRO_CALL FileId(1) [15; 48) [28; 31)", + "macro_rules! foo { () => { () } }|foo", + ); + } + #[test] fn goto_def_for_field_init_shorthand() { covers!(ra_ide_db::goto_def_for_field_init_shorthand); -- cgit v1.2.3