mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
Merge #4234
4234: Support local_inner_macros r=jonas-schievink a=edwin0cheng
This PR implements `#[macro_export(local_inner_macros)]` support.
Note that the rustc implementation is quite [hacky][1] too. :)
[1]: 614f273e93/src/librustc_resolve/macros.rs (L456)
Co-authored-by: Edwin Cheng <edwin0cheng@gmail.com>
This commit is contained in:
commit
fb8fb65131
13 changed files with 139 additions and 17 deletions
|
@ -151,7 +151,7 @@ impl SourceToDefCtx<'_, '_> {
|
||||||
let krate = self.file_to_def(file_id)?.krate;
|
let krate = self.file_to_def(file_id)?.krate;
|
||||||
let file_ast_id = self.db.ast_id_map(src.file_id).ast_id(&src.value);
|
let file_ast_id = self.db.ast_id_map(src.file_id).ast_id(&src.value);
|
||||||
let ast_id = Some(AstId::new(src.file_id, file_ast_id));
|
let ast_id = Some(AstId::new(src.file_id, file_ast_id));
|
||||||
Some(MacroDefId { krate: Some(krate), ast_id, kind })
|
Some(MacroDefId { krate: Some(krate), ast_id, kind, local_inner: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> {
|
pub(super) fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> {
|
||||||
|
|
|
@ -140,6 +140,7 @@ impl Attr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct AttrQuery<'a> {
|
pub struct AttrQuery<'a> {
|
||||||
attrs: &'a Attrs,
|
attrs: &'a Attrs,
|
||||||
key: &'static str,
|
key: &'static str,
|
||||||
|
|
|
@ -466,6 +466,7 @@ impl ExprCollector<'_> {
|
||||||
krate: Some(self.expander.module.krate),
|
krate: Some(self.expander.module.krate),
|
||||||
ast_id: Some(self.expander.ast_id(&e)),
|
ast_id: Some(self.expander.ast_id(&e)),
|
||||||
kind: MacroDefKind::Declarative,
|
kind: MacroDefKind::Declarative,
|
||||||
|
local_inner: false,
|
||||||
};
|
};
|
||||||
self.body.item_scope.define_legacy_macro(name, mac);
|
self.body.item_scope.define_legacy_macro(name, mac);
|
||||||
|
|
||||||
|
|
|
@ -204,6 +204,7 @@ impl DefCollector<'_> {
|
||||||
ast_id: None,
|
ast_id: None,
|
||||||
krate: Some(krate),
|
krate: Some(krate),
|
||||||
kind: MacroDefKind::CustomDerive(expander),
|
kind: MacroDefKind::CustomDerive(expander),
|
||||||
|
local_inner: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.define_proc_macro(name.clone(), macro_id);
|
self.define_proc_macro(name.clone(), macro_id);
|
||||||
|
@ -941,6 +942,7 @@ impl ModCollector<'_, '_> {
|
||||||
ast_id: Some(ast_id.ast_id),
|
ast_id: Some(ast_id.ast_id),
|
||||||
krate: Some(self.def_collector.def_map.krate),
|
krate: Some(self.def_collector.def_map.krate),
|
||||||
kind: MacroDefKind::Declarative,
|
kind: MacroDefKind::Declarative,
|
||||||
|
local_inner: mac.local_inner,
|
||||||
};
|
};
|
||||||
self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export);
|
self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export);
|
||||||
}
|
}
|
||||||
|
|
|
@ -188,6 +188,7 @@ pub(super) struct MacroData {
|
||||||
pub(super) path: ModPath,
|
pub(super) path: ModPath,
|
||||||
pub(super) name: Option<Name>,
|
pub(super) name: Option<Name>,
|
||||||
pub(super) export: bool,
|
pub(super) export: bool,
|
||||||
|
pub(super) local_inner: bool,
|
||||||
pub(super) builtin: bool,
|
pub(super) builtin: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,14 +402,32 @@ impl RawItemsCollector {
|
||||||
|
|
||||||
let name = m.name().map(|it| it.as_name());
|
let name = m.name().map(|it| it.as_name());
|
||||||
let ast_id = self.source_ast_id_map.ast_id(&m);
|
let ast_id = self.source_ast_id_map.ast_id(&m);
|
||||||
// FIXME: cfg_attr
|
|
||||||
let export = m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "macro_export");
|
|
||||||
|
|
||||||
// FIXME: cfg_attr
|
// FIXME: cfg_attr
|
||||||
let builtin =
|
let export_attr = attrs.by_key("macro_export");
|
||||||
m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "rustc_builtin_macro");
|
|
||||||
|
|
||||||
let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export, builtin });
|
let export = export_attr.exists();
|
||||||
|
let local_inner = if export {
|
||||||
|
export_attr.tt_values().map(|it| &it.token_trees).flatten().any(|it| match it {
|
||||||
|
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
|
||||||
|
ident.text.contains("local_inner_macros")
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
let builtin = attrs.by_key("rustc_builtin_macro").exists();
|
||||||
|
|
||||||
|
let m = self.raw_items.macros.alloc(MacroData {
|
||||||
|
ast_id,
|
||||||
|
path,
|
||||||
|
name,
|
||||||
|
export,
|
||||||
|
local_inner,
|
||||||
|
builtin,
|
||||||
|
});
|
||||||
self.push_item(current_module, attrs, RawItemKind::Macro(m));
|
self.push_item(current_module, attrs, RawItemKind::Macro(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,6 +135,43 @@ fn macro_rules_export_with_local_inner_macros_are_visible() {
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn local_inner_macros_makes_local_macros_usable() {
|
||||||
|
let map = def_map(
|
||||||
|
"
|
||||||
|
//- /main.rs crate:main deps:foo
|
||||||
|
foo::structs!(Foo, Bar);
|
||||||
|
mod bar;
|
||||||
|
//- /bar.rs
|
||||||
|
use crate::*;
|
||||||
|
//- /lib.rs crate:foo
|
||||||
|
#[macro_export(local_inner_macros)]
|
||||||
|
macro_rules! structs {
|
||||||
|
($($i:ident),*) => {
|
||||||
|
inner!($($i),*);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! inner {
|
||||||
|
($($i:ident),*) => {
|
||||||
|
$(struct $i { field: u32 } )*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
assert_snapshot!(map, @r###"
|
||||||
|
⋮crate
|
||||||
|
⋮Bar: t v
|
||||||
|
⋮Foo: t v
|
||||||
|
⋮bar: t
|
||||||
|
⋮
|
||||||
|
⋮crate::bar
|
||||||
|
⋮Bar: t v
|
||||||
|
⋮Foo: t v
|
||||||
|
⋮bar: t
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
|
fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
|
||||||
let map = def_map(
|
let map = def_map(
|
||||||
|
|
|
@ -116,6 +116,21 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path>
|
||||||
}
|
}
|
||||||
segments.reverse();
|
segments.reverse();
|
||||||
generic_args.reverse();
|
generic_args.reverse();
|
||||||
|
|
||||||
|
// handle local_inner_macros :
|
||||||
|
// Basically, even in rustc it is quite hacky:
|
||||||
|
// https://github.com/rust-lang/rust/blob/614f273e9388ddd7804d5cbc80b8865068a3744e/src/librustc_resolve/macros.rs#L456
|
||||||
|
// We follow what it did anyway :)
|
||||||
|
if segments.len() == 1 && kind == PathKind::Plain {
|
||||||
|
if let Some(macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
|
||||||
|
if macro_call.is_bang() {
|
||||||
|
if let Some(crate_id) = hygiene.local_inner_macros() {
|
||||||
|
kind = PathKind::DollarCrate(crate_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mod_path = ModPath { kind, segments };
|
let mod_path = ModPath { kind, segments };
|
||||||
return Some(Path { type_anchor, mod_path, generic_args });
|
return Some(Path { type_anchor, mod_path, generic_args });
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ macro_rules! register_builtin {
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(MacroDefId { krate: None, ast_id: None, kind: MacroDefKind::BuiltInDerive(kind) })
|
Some(MacroDefId { krate: None, ast_id: None, kind: MacroDefKind::BuiltInDerive(kind), local_inner: false })
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,11 +73,13 @@ pub fn find_builtin_macro(
|
||||||
krate: Some(krate),
|
krate: Some(krate),
|
||||||
ast_id: Some(ast_id),
|
ast_id: Some(ast_id),
|
||||||
kind: MacroDefKind::BuiltIn(kind),
|
kind: MacroDefKind::BuiltIn(kind),
|
||||||
|
local_inner: false,
|
||||||
}),
|
}),
|
||||||
Either::Right(kind) => Some(MacroDefId {
|
Either::Right(kind) => Some(MacroDefId {
|
||||||
krate: Some(krate),
|
krate: Some(krate),
|
||||||
ast_id: Some(ast_id),
|
ast_id: Some(ast_id),
|
||||||
kind: MacroDefKind::BuiltInEager(kind),
|
kind: MacroDefKind::BuiltInEager(kind),
|
||||||
|
local_inner: false,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -406,6 +408,7 @@ mod tests {
|
||||||
krate: Some(CrateId(0)),
|
krate: Some(CrateId(0)),
|
||||||
ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[0]))),
|
ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[0]))),
|
||||||
kind: MacroDefKind::BuiltIn(expander),
|
kind: MacroDefKind::BuiltIn(expander),
|
||||||
|
local_inner: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let loc = MacroCallLoc {
|
let loc = MacroCallLoc {
|
||||||
|
@ -425,6 +428,7 @@ mod tests {
|
||||||
krate: Some(CrateId(0)),
|
krate: Some(CrateId(0)),
|
||||||
ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[0]))),
|
ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[0]))),
|
||||||
kind: MacroDefKind::BuiltInEager(expander),
|
kind: MacroDefKind::BuiltInEager(expander),
|
||||||
|
local_inner: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let args = macro_calls[1].token_tree().unwrap();
|
let args = macro_calls[1].token_tree().unwrap();
|
||||||
|
|
|
@ -16,31 +16,34 @@ use crate::{
|
||||||
pub struct Hygiene {
|
pub struct Hygiene {
|
||||||
// This is what `$crate` expands to
|
// This is what `$crate` expands to
|
||||||
def_crate: Option<CrateId>,
|
def_crate: Option<CrateId>,
|
||||||
|
|
||||||
|
// Indiciate this is a local inner macro
|
||||||
|
local_inner: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hygiene {
|
impl Hygiene {
|
||||||
pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene {
|
pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene {
|
||||||
let def_crate = match file_id.0 {
|
let (def_crate, local_inner) = match file_id.0 {
|
||||||
HirFileIdRepr::FileId(_) => None,
|
HirFileIdRepr::FileId(_) => (None, false),
|
||||||
HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id {
|
HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id {
|
||||||
MacroCallId::LazyMacro(id) => {
|
MacroCallId::LazyMacro(id) => {
|
||||||
let loc = db.lookup_intern_macro(id);
|
let loc = db.lookup_intern_macro(id);
|
||||||
match loc.def.kind {
|
match loc.def.kind {
|
||||||
MacroDefKind::Declarative => loc.def.krate,
|
MacroDefKind::Declarative => (loc.def.krate, loc.def.local_inner),
|
||||||
MacroDefKind::BuiltIn(_) => None,
|
MacroDefKind::BuiltIn(_) => (None, false),
|
||||||
MacroDefKind::BuiltInDerive(_) => None,
|
MacroDefKind::BuiltInDerive(_) => (None, false),
|
||||||
MacroDefKind::BuiltInEager(_) => None,
|
MacroDefKind::BuiltInEager(_) => (None, false),
|
||||||
MacroDefKind::CustomDerive(_) => None,
|
MacroDefKind::CustomDerive(_) => (None, false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MacroCallId::EagerMacro(_id) => None,
|
MacroCallId::EagerMacro(_id) => (None, false),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
Hygiene { def_crate }
|
Hygiene { def_crate, local_inner }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_unhygienic() -> Hygiene {
|
pub fn new_unhygienic() -> Hygiene {
|
||||||
Hygiene { def_crate: None }
|
Hygiene { def_crate: None, local_inner: false }
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: this should just return name
|
// FIXME: this should just return name
|
||||||
|
@ -52,4 +55,12 @@ impl Hygiene {
|
||||||
}
|
}
|
||||||
Either::Left(name_ref.as_name())
|
Either::Left(name_ref.as_name())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn local_inner_macros(&self) -> Option<CrateId> {
|
||||||
|
if self.local_inner {
|
||||||
|
self.def_crate
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -204,6 +204,8 @@ pub struct MacroDefId {
|
||||||
pub krate: Option<CrateId>,
|
pub krate: Option<CrateId>,
|
||||||
pub ast_id: Option<AstId<ast::MacroCall>>,
|
pub ast_id: Option<AstId<ast::MacroCall>>,
|
||||||
pub kind: MacroDefKind,
|
pub kind: MacroDefKind,
|
||||||
|
|
||||||
|
pub local_inner: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MacroDefId {
|
impl MacroDefId {
|
||||||
|
|
|
@ -427,6 +427,32 @@ fn main() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_local_inner_macros() {
|
||||||
|
let (db, pos) = TestDB::with_position(
|
||||||
|
r#"
|
||||||
|
//- /main.rs crate:main deps:foo
|
||||||
|
fn test() {
|
||||||
|
let x = foo::foo!(1);
|
||||||
|
x<|>;
|
||||||
|
}
|
||||||
|
|
||||||
|
//- /lib.rs crate:foo
|
||||||
|
#[macro_export(local_inner_macros)]
|
||||||
|
macro_rules! foo {
|
||||||
|
(1) => { bar!() };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! bar {
|
||||||
|
() => { 42 }
|
||||||
|
}
|
||||||
|
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!("i32", type_at_pos(&db, pos));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_builtin_macros_line() {
|
fn infer_builtin_macros_line() {
|
||||||
assert_snapshot!(
|
assert_snapshot!(
|
||||||
|
|
|
@ -423,6 +423,10 @@ impl ast::MacroCall {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_bang(&self) -> bool {
|
||||||
|
self.is_macro_rules().is_none()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ast::LifetimeParam {
|
impl ast::LifetimeParam {
|
||||||
|
|
Loading…
Reference in a new issue