From a193666361f6ea9725b927a35f5baf77da713c0a Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sat, 27 Mar 2021 13:44:54 +0800 Subject: Basic Support Macro 2.0 --- crates/hir_def/src/nameres/collector.rs | 102 ++++++++++++++++++----------- crates/hir_def/src/nameres/tests/macros.rs | 22 +++++++ crates/hir_expand/src/db.rs | 60 +++++++++++------ crates/hir_expand/src/hygiene.rs | 2 +- crates/hir_expand/src/lib.rs | 2 +- crates/hir_ty/src/tests/macros.rs | 83 ++++++++++++++++++++++- crates/syntax/src/ast/node_ext.rs | 1 + 7 files changed, 209 insertions(+), 63 deletions(-) diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index d8fabe49b..d58135ec9 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs @@ -25,8 +25,8 @@ use crate::{ derive_macro_as_call_id, item_scope::{ImportType, PerNsGlobImports}, item_tree::{ - self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, MacroRules, Mod, ModItem, ModKind, - StructDefKind, + self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, MacroDef, MacroRules, Mod, ModItem, + ModKind, StructDefKind, }, macro_call_as_call_id, nameres::{ @@ -395,7 +395,7 @@ impl DefCollector<'_> { /// macro_rules! foo { () => {} } /// use foo as bar; /// ``` - fn define_macro( + fn define_macro_rules( &mut self, module_id: LocalModuleId, name: Name, @@ -430,6 +430,21 @@ impl DefCollector<'_> { self.def_map.modules[module_id].scope.define_legacy_macro(name, mac); } + /// Define a macro 2.0 macro + /// + /// The scoped of macro 2.0 macro is equal to normal function + fn define_macro_def( + &mut self, + module_id: LocalModuleId, + name: Name, + macro_: MacroDefId, + vis: &RawVisibility, + ) { + let vis = + self.def_map.resolve_visibility(self.db, module_id, vis).unwrap_or(Visibility::Public); + self.update(module_id, &[(Some(name), PerNs::macros(macro_, vis))], vis, ImportType::Named); + } + /// Define a proc macro /// /// A proc macro is similar to normal macro scope, but it would not visible in legacy textual scoped. @@ -1067,40 +1082,7 @@ impl ModCollector<'_, '_> { } ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac]), ModItem::MacroRules(id) => self.collect_macro_rules(id), - ModItem::MacroDef(id) => { - let mac = &self.item_tree[id]; - let ast_id = InFile::new(self.file_id, mac.ast_id.upcast()); - - // "Macro 2.0" is not currently supported by rust-analyzer, but libcore uses it - // to define builtin macros, so we support at least that part. - let attrs = self.item_tree.attrs( - self.def_collector.db, - krate, - ModItem::from(id).into(), - ); - if attrs.by_key("rustc_builtin_macro").exists() { - let krate = self.def_collector.def_map.krate; - let macro_id = find_builtin_macro(&mac.name, krate, ast_id) - .or_else(|| find_builtin_derive(&mac.name, krate, ast_id)); - if let Some(macro_id) = macro_id { - let vis = self - .def_collector - .def_map - .resolve_visibility( - self.def_collector.db, - self.module_id, - &self.item_tree[mac.visibility], - ) - .unwrap_or(Visibility::Public); - self.def_collector.update( - self.module_id, - &[(Some(mac.name.clone()), PerNs::macros(macro_id, vis))], - vis, - ImportType::Named, - ); - } - } - } + ModItem::MacroDef(id) => self.collect_macro_def(id), ModItem::Impl(imp) => { let module = self.def_collector.def_map.module_id(self.module_id); let impl_id = @@ -1420,7 +1402,7 @@ impl ModCollector<'_, '_> { if attrs.by_key("rustc_builtin_macro").exists() { let krate = self.def_collector.def_map.krate; if let Some(macro_id) = find_builtin_macro(&mac.name, krate, ast_id) { - self.def_collector.define_macro( + self.def_collector.define_macro_rules( self.module_id, mac.name.clone(), macro_id, @@ -1436,7 +1418,49 @@ impl ModCollector<'_, '_> { kind: MacroDefKind::Declarative(ast_id), local_inner: is_local_inner, }; - self.def_collector.define_macro(self.module_id, mac.name.clone(), macro_id, is_export); + self.def_collector.define_macro_rules( + self.module_id, + mac.name.clone(), + macro_id, + is_export, + ); + } + + fn collect_macro_def(&mut self, id: FileItemTreeId) { + let krate = self.def_collector.def_map.krate; + let mac = &self.item_tree[id]; + let ast_id = InFile::new(self.file_id, mac.ast_id.upcast()); + + // Case 1: bulitin macros + let attrs = self.item_tree.attrs(self.def_collector.db, krate, ModItem::from(id).into()); + if attrs.by_key("rustc_builtin_macro").exists() { + let macro_id = find_builtin_macro(&mac.name, krate, ast_id) + .or_else(|| find_builtin_derive(&mac.name, krate, ast_id)); + + if let Some(macro_id) = macro_id { + self.def_collector.define_macro_def( + self.module_id, + mac.name.clone(), + macro_id, + &self.item_tree[mac.visibility], + ); + } + return; + } + + // Case 2: normal `macro` + let macro_id = MacroDefId { + krate: self.def_collector.def_map.krate, + kind: MacroDefKind::Declarative(ast_id), + local_inner: false, + }; + + self.def_collector.define_macro_def( + self.module_id, + mac.name.clone(), + macro_id, + &self.item_tree[mac.visibility], + ); } fn collect_macro_call(&mut self, mac: &MacroCall) { diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs index 6d3cb8d7a..9986e99e4 100644 --- a/crates/hir_def/src/nameres/tests/macros.rs +++ b/crates/hir_def/src/nameres/tests/macros.rs @@ -837,3 +837,25 @@ fn collects_derive_helpers() { _ => unreachable!(), } } + +#[test] +fn resolve_macro_def() { + check( + r#" +//- /lib.rs +pub macro structs($($i:ident),*) { + $(struct $i { field: u32 } )* +} + +structs!(Foo); + +//- /nested.rs +structs!(Bar, Baz); +"#, + expect![[r#" + crate + Foo: t + structs: m + "#]], + ); +} diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index d672f6723..c0ab70b60 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use base_db::{salsa, SourceDatabase}; -use mbe::{ExpandError, ExpandResult, MacroRules}; +use mbe::{ExpandError, ExpandResult, MacroDef, MacroRules}; use parser::FragmentKind; use syntax::{ algo::diff, @@ -28,6 +28,7 @@ const TOKEN_LIMIT: usize = 524288; #[derive(Debug, Clone, Eq, PartialEq)] pub enum TokenExpander { MacroRules(mbe::MacroRules), + MacroDef(mbe::MacroDef), Builtin(BuiltinFnLikeExpander), BuiltinDerive(BuiltinDeriveExpander), ProcMacro(ProcMacroExpander), @@ -42,6 +43,7 @@ impl TokenExpander { ) -> mbe::ExpandResult { match self { TokenExpander::MacroRules(it) => it.expand(tt), + TokenExpander::MacroDef(it) => it.expand(tt), TokenExpander::Builtin(it) => it.expand(db, id, tt), // FIXME switch these to ExpandResult as well TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), @@ -57,6 +59,7 @@ impl TokenExpander { pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId { match self { TokenExpander::MacroRules(it) => it.map_id_down(id), + TokenExpander::MacroDef(it) => it.map_id_down(id), TokenExpander::Builtin(..) => id, TokenExpander::BuiltinDerive(..) => id, TokenExpander::ProcMacro(..) => id, @@ -66,6 +69,7 @@ impl TokenExpander { pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) { match self { TokenExpander::MacroRules(it) => it.map_id_up(id), + TokenExpander::MacroDef(it) => it.map_id_up(id), TokenExpander::Builtin(..) => (id, mbe::Origin::Call), TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call), TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call), @@ -136,26 +140,40 @@ fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc { fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option> { match id.kind { - MacroDefKind::Declarative(ast_id) => { - let macro_rules = match ast_id.to_node(db) { - syntax::ast::Macro::MacroRules(mac) => mac, - syntax::ast::Macro::MacroDef(_) => return None, - }; - let arg = macro_rules.token_tree()?; - let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| { - log::warn!("fail on macro_def to token tree: {:#?}", arg); - None - })?; - let rules = match MacroRules::parse(&tt) { - Ok(it) => it, - Err(err) => { - let name = macro_rules.name().map(|n| n.to_string()).unwrap_or_default(); - log::warn!("fail on macro_def parse ({}): {:?} {:#?}", name, err, tt); - return None; - } - }; - Some(Arc::new((TokenExpander::MacroRules(rules), tmap))) - } + MacroDefKind::Declarative(ast_id) => match ast_id.to_node(db) { + syntax::ast::Macro::MacroRules(macro_rules) => { + let arg = macro_rules.token_tree()?; + let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| { + log::warn!("fail on macro_rules to token tree: {:#?}", arg); + None + })?; + let rules = match MacroRules::parse(&tt) { + Ok(it) => it, + Err(err) => { + let name = macro_rules.name().map(|n| n.to_string()).unwrap_or_default(); + log::warn!("fail on macro_def parse ({}): {:?} {:#?}", name, err, tt); + return None; + } + }; + Some(Arc::new((TokenExpander::MacroRules(rules), tmap))) + } + syntax::ast::Macro::MacroDef(macro_def) => { + let arg = macro_def.body()?; + let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| { + log::warn!("fail on macro_def to token tree: {:#?}", arg); + None + })?; + let rules = match MacroDef::parse(&tt) { + Ok(it) => it, + Err(err) => { + let name = macro_def.name().map(|n| n.to_string()).unwrap_or_default(); + log::warn!("fail on macro_def parse ({}): {:?} {:#?}", name, err, tt); + return None; + } + }; + Some(Arc::new((TokenExpander::MacroDef(rules), tmap))) + } + }, MacroDefKind::BuiltIn(expander, _) => { Some(Arc::new((TokenExpander::Builtin(expander), mbe::TokenMap::default()))) } diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs index 0e0f7214a..779725629 100644 --- a/crates/hir_expand/src/hygiene.rs +++ b/crates/hir_expand/src/hygiene.rs @@ -148,7 +148,7 @@ fn make_hygiene_info( let def_offset = loc.def.ast_id().left().and_then(|id| { let def_tt = match id.to_node(db) { ast::Macro::MacroRules(mac) => mac.token_tree()?.syntax().text_range().start(), - ast::Macro::MacroDef(_) => return None, + ast::Macro::MacroDef(mac) => mac.body()?.syntax().text_range().start(), }; Some(InFile::new(id.file_id, def_tt)) }); diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index b8045fda9..3e332ee47 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs @@ -151,7 +151,7 @@ impl HirFileId { let def = loc.def.ast_id().left().and_then(|id| { let def_tt = match id.to_node(db) { ast::Macro::MacroRules(mac) => mac.token_tree()?, - ast::Macro::MacroDef(_) => return None, + ast::Macro::MacroDef(mac) => mac.body()?, }; Some(InFile::new(id.file_id, def_tt)) }); diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs index 01935ec99..3eb01dbd0 100644 --- a/crates/hir_ty/src/tests/macros.rs +++ b/crates/hir_ty/src/tests/macros.rs @@ -135,7 +135,88 @@ fn infer_path_qualified_macros_expanded() { } #[test] -fn expr_macro_expanded_in_various_places() { +fn expr_macro_def_expanded_in_various_places() { + check_infer( + r#" + macro spam() { + 1isize + } + + fn spam() { + spam!(); + (spam!()); + spam!().spam(spam!()); + for _ in spam!() {} + || spam!(); + while spam!() {} + break spam!(); + return spam!(); + match spam!() { + _ if spam!() => spam!(), + } + spam!()(spam!()); + Spam { spam: spam!() }; + spam!()[spam!()]; + await spam!(); + spam!() as usize; + &spam!(); + -spam!(); + spam!()..spam!(); + spam!() + spam!(); + } + "#, + expect![[r#" + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + !0..6 '1isize': isize + 39..442 '{ ...!(); }': () + 73..94 'spam!(...am!())': {unknown} + 100..119 'for _ ...!() {}': () + 104..105 '_': {unknown} + 117..119 '{}': () + 124..134 '|| spam!()': || -> isize + 140..156 'while ...!() {}': () + 154..156 '{}': () + 161..174 'break spam!()': ! + 180..194 'return spam!()': ! + 200..254 'match ... }': isize + 224..225 '_': isize + 259..275 'spam!(...am!())': {unknown} + 281..303 'Spam {...m!() }': {unknown} + 309..325 'spam!(...am!()]': {unknown} + 350..366 'spam!(... usize': usize + 372..380 '&spam!()': &isize + 386..394 '-spam!()': isize + 400..416 'spam!(...pam!()': {unknown} + 422..439 'spam!(...pam!()': isize + "#]], + ); +} + +#[test] +fn expr_macro_rules_expanded_in_various_places() { check_infer( r#" macro_rules! spam { diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 2772d7364..ae98dbd26 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -40,6 +40,7 @@ fn text_of_first_token(node: &SyntaxNode) -> TokenText { TokenText(first_token) } +#[derive(Debug, PartialEq, Eq, Clone)] pub enum Macro { MacroRules(ast::MacroRules), MacroDef(ast::MacroDef), -- cgit v1.2.3