Skip to content

Basic support macro 2.0 #8212

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/hir/src/semantics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,7 @@ to_def_impls![
(crate::TypeParam, ast::TypeParam, type_param_to_def),
(crate::LifetimeParam, ast::LifetimeParam, lifetime_param_to_def),
(crate::ConstParam, ast::ConstParam, const_param_to_def),
(crate::MacroDef, ast::MacroRules, macro_rules_to_def),
(crate::MacroDef, ast::Macro, macro_to_def),
(crate::Local, ast::IdentPat, bind_pat_to_def),
(crate::Local, ast::SelfParam, self_param_to_def),
(crate::Label, ast::Label, label_to_def),
Expand Down
5 changes: 1 addition & 4 deletions crates/hir/src/semantics/source_to_def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,7 @@ impl SourceToDefCtx<'_, '_> {
}

// FIXME: use DynMap as well?
pub(super) fn macro_rules_to_def(
&mut self,
src: InFile<ast::MacroRules>,
) -> Option<MacroDefId> {
pub(super) fn macro_to_def(&mut self, src: InFile<ast::Macro>) -> Option<MacroDefId> {
let file_ast_id = self.db.ast_id_map(src.file_id).ast_id(&src.value);
let ast_id = AstId::new(src.file_id, file_ast_id.upcast());
let kind = MacroDefKind::Declarative(ast_id);
Expand Down
102 changes: 63 additions & 39 deletions crates/hir_def/src/nameres/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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,
Expand All @@ -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<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) {
Expand Down
18 changes: 18 additions & 0 deletions crates/hir_def/src/nameres/tests/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -837,3 +837,21 @@ 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);
"#,
expect![[r#"
crate
Foo: t
structs: m
"#]],
);
}
60 changes: 39 additions & 21 deletions crates/hir_expand/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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),
Expand All @@ -42,6 +43,7 @@ impl TokenExpander {
) -> 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(),
Expand All @@ -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,
Expand All @@ -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),
Expand Down Expand Up @@ -136,26 +140,40 @@ fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {

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())))
}
Expand Down
2 changes: 1 addition & 1 deletion crates/hir_expand/src/hygiene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
});
Expand Down
2 changes: 1 addition & 1 deletion crates/hir_expand/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
});
Expand Down
83 changes: 82 additions & 1 deletion crates/hir_ty/src/tests/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading