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::{
/// macro_rules! foo { () => {} }
/// use foo as bar;
/// ```
- fn define_macro(
+ fn define_macro_rules(
&mut self,
module_id: LocalModuleId,
name: Name,
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.
}
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 =
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,
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<MacroDef>) {
+ 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) {
_ => 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
+ "#]],
+ );
+}
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,
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum TokenExpander {
MacroRules(mbe::MacroRules),
+ MacroDef(mbe::MacroDef),
Builtin(BuiltinFnLikeExpander),
BuiltinDerive(BuiltinDeriveExpander),
ProcMacro(ProcMacroExpander),
) -> mbe::ExpandResult<tt::Subtree> {
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(),
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,
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),
fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
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())))
}
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))
});
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))
});
}
#[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 {
TokenText(first_token)
}
+#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Macro {
MacroRules(ast::MacroRules),
MacroDef(ast::MacroDef),