blob: bf89ea711a0a1113f5006e5562259b46a06a4341 [file] [log] [blame]
use expect_test::expect;
use test_fixture::WithFixture;
use itertools::Itertools;
use crate::nameres::tests::check;
use super::*;
#[test]
fn macro_rules_are_globally_visible() {
check(
r#"
//- /lib.rs
macro_rules! structs {
($($i:ident),*) => {
$(struct $i { field: u32 } )*
}
}
structs!(Foo);
mod nested;
//- /nested.rs
structs!(Bar, Baz);
"#,
expect![[r#"
crate
Foo: t
nested: t
crate::nested
Bar: t
Baz: t
"#]],
);
}
#[test]
fn macro_rules_can_define_modules() {
check(
r#"
//- /lib.rs
macro_rules! m {
($name:ident) => { mod $name; }
}
m!(n1);
mod m { m!(n3) }
//- /n1.rs
m!(n2)
//- /n1/n2.rs
struct X;
//- /m/n3.rs
struct Y;
"#,
expect![[r#"
crate
m: t
n1: t
crate::m
n3: t
crate::m::n3
Y: t v
crate::n1
n2: t
crate::n1::n2
X: t v
"#]],
);
}
#[test]
fn macro_rules_from_other_crates_are_visible() {
check(
r#"
//- /main.rs crate:main deps:foo
foo::structs!(Foo, Bar)
mod bar;
//- /bar.rs
use crate::*;
//- /lib.rs crate:foo
#[macro_export]
macro_rules! structs {
($($i:ident),*) => {
$(struct $i { field: u32 } )*
}
}
"#,
expect![[r#"
crate
Bar: t
Foo: t
bar: t
crate::bar
Bar: t
Foo: t
bar: t
"#]],
);
}
#[test]
fn macro_rules_export_with_local_inner_macros_are_visible() {
check(
r#"
//- /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),*) => {
$(struct $i { field: u32 } )*
}
}
"#,
expect![[r#"
crate
Bar: t
Foo: t
bar: t
crate::bar
Bar: t
Foo: t
bar: t
"#]],
);
}
#[test]
fn local_inner_macros_makes_local_macros_usable() {
check(
r#"
//- /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 } )*
}
}
"#,
expect![[r#"
crate
Bar: t
Foo: t
bar: t
crate::bar
Bar: t
Foo: t
bar: t
"#]],
);
}
#[test]
fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
check(
r#"
//- /main.rs crate:main deps:foo
macro_rules! baz {
() => {
use foo::bar;
}
}
foo!();
bar!();
baz!();
//- /lib.rs crate:foo
#[macro_export]
macro_rules! foo {
() => {
struct Foo { field: u32 }
}
}
#[macro_export]
macro_rules! bar {
() => {
use foo::foo;
}
}
"#,
expect![[r#"
crate
Foo: t
bar: mi
foo: mi
"#]],
);
}
#[test]
fn macro_rules_from_other_crates_are_visible_with_macro_use() {
cov_mark::check!(macro_rules_from_other_crates_are_visible_with_macro_use);
check(
r#"
//- /main.rs crate:main deps:foo
structs!(Foo);
structs_priv!(Bar);
structs_not_exported!(MacroNotResolved1);
crate::structs!(MacroNotResolved2);
mod bar;
#[macro_use]
extern crate foo;
//- /bar.rs
structs!(Baz);
crate::structs!(MacroNotResolved3);
//- /lib.rs crate:foo
#[macro_export]
macro_rules! structs {
($i:ident) => { struct $i; }
}
macro_rules! structs_not_exported {
($i:ident) => { struct $i; }
}
mod priv_mod {
#[macro_export]
macro_rules! structs_priv {
($i:ident) => { struct $i; }
}
}
"#,
expect![[r#"
crate
Bar: t v
Foo: t v
bar: t
foo: te
crate::bar
Baz: t v
"#]],
);
}
#[test]
fn macro_use_filter() {
check(
r#"
//- /main.rs crate:main deps:empty,multiple,all
#[macro_use()]
extern crate empty;
foo_not_imported!();
#[macro_use(bar1)]
#[macro_use()]
#[macro_use(bar2, bar3)]
extern crate multiple;
bar1!();
bar2!();
bar3!();
bar_not_imported!();
#[macro_use(baz1)]
#[macro_use]
#[macro_use(baz2)]
extern crate all;
baz1!();
baz2!();
baz3!();
//- /empty.rs crate:empty
#[macro_export]
macro_rules! foo_not_imported { () => { struct NotOkFoo; } }
//- /multiple.rs crate:multiple
#[macro_export]
macro_rules! bar1 { () => { struct OkBar1; } }
#[macro_export]
macro_rules! bar2 { () => { struct OkBar2; } }
#[macro_export]
macro_rules! bar3 { () => { struct OkBar3; } }
#[macro_export]
macro_rules! bar_not_imported { () => { struct NotOkBar; } }
//- /all.rs crate:all
#[macro_export]
macro_rules! baz1 { () => { struct OkBaz1; } }
#[macro_export]
macro_rules! baz2 { () => { struct OkBaz2; } }
#[macro_export]
macro_rules! baz3 { () => { struct OkBaz3; } }
"#,
expect![[r#"
crate
OkBar1: t v
OkBar2: t v
OkBar3: t v
OkBaz1: t v
OkBaz2: t v
OkBaz3: t v
all: te
empty: te
multiple: te
"#]],
);
}
#[test]
fn prelude_is_macro_use() {
cov_mark::check!(prelude_is_macro_use);
check(
r#"
//- /main.rs edition:2018 crate:main deps:std
structs!(Foo);
structs_priv!(Bar);
structs_outside!(Out);
crate::structs!(MacroNotResolved2);
mod bar;
//- /bar.rs
structs!(Baz);
crate::structs!(MacroNotResolved3);
//- /lib.rs crate:std
pub mod prelude {
pub mod rust_2018 {
#[macro_export]
macro_rules! structs {
($i:ident) => { struct $i; }
}
mod priv_mod {
#[macro_export]
macro_rules! structs_priv {
($i:ident) => { struct $i; }
}
}
}
}
#[macro_export]
macro_rules! structs_outside {
($i:ident) => { struct $i; }
}
"#,
expect![[r#"
crate
Bar: t v
Foo: t v
Out: t v
bar: t
crate::bar
Baz: t v
"#]],
);
}
#[test]
fn prelude_cycle() {
check(
r#"
#[prelude_import]
use self::prelude::*;
declare_mod!();
mod prelude {
macro_rules! declare_mod {
() => (mod foo {})
}
}
"#,
expect![[r#"
crate
prelude: t
crate::prelude
"#]],
);
}
#[test]
fn legacy_macro_use_before_def() {
check(
r#"
m!();
macro_rules! m {
() => {
struct S;
}
}
"#,
expect![[r#"
crate
S: t v
"#]],
);
// FIXME: should not expand. legacy macro scoping is not implemented.
}
#[test]
fn plain_macros_are_legacy_textual_scoped() {
check(
r#"
//- /main.rs
mod m1;
bar!(NotFoundNotMacroUse);
mod m2 { foo!(NotFoundBeforeInside2); }
macro_rules! foo {
($x:ident) => { struct $x; }
}
foo!(Ok);
mod m3;
foo!(OkShadowStop);
bar!(NotFoundMacroUseStop);
#[macro_use]
mod m5 {
#[macro_use]
mod m6 {
macro_rules! foo {
($x:ident) => { fn $x() {} }
}
}
}
foo!(ok_double_macro_use_shadow);
baz!(NotFoundBefore);
#[macro_use]
mod m7 {
macro_rules! baz {
($x:ident) => { struct $x; }
}
}
baz!(OkAfter);
//- /m1.rs
foo!(NotFoundBeforeInside1);
macro_rules! bar {
($x:ident) => { struct $x; }
}
//- /m3/mod.rs
foo!(OkAfterInside);
macro_rules! foo {
($x:ident) => { fn $x() {} }
}
foo!(ok_shadow);
#[macro_use]
mod m4;
bar!(OkMacroUse);
mod m5;
baz!(OkMacroUseInner);
//- /m3/m4.rs
foo!(ok_shadow_deep);
macro_rules! bar {
($x:ident) => { struct $x; }
}
//- /m3/m5.rs
#![macro_use]
macro_rules! baz {
($x:ident) => { struct $x; }
}
"#,
expect![[r#"
crate
NotFoundBefore: t v
Ok: t v
OkAfter: t v
OkShadowStop: t v
m1: t
m2: t
m3: t
m5: t
m7: t
ok_double_macro_use_shadow: v
crate::m1
crate::m2
crate::m3
OkAfterInside: t v
OkMacroUse: t v
OkMacroUseInner: t v
m4: t
m5: t
ok_shadow: v
crate::m3::m4
ok_shadow_deep: v
crate::m3::m5
crate::m5
m6: t
crate::m5::m6
crate::m7
"#]],
);
// FIXME: should not see `NotFoundBefore`
}
#[test]
fn type_value_macro_live_in_different_scopes() {
check(
r#"
#[macro_export]
macro_rules! foo {
($x:ident) => { type $x = (); }
}
foo!(foo);
use foo as bar;
use self::foo as baz;
fn baz() {}
"#,
expect![[r#"
crate
bar: ti mi
baz: ti v mi
foo: t m
"#]],
);
}
#[test]
fn macro_use_can_be_aliased() {
check(
r#"
//- /main.rs crate:main deps:foo
#[macro_use]
extern crate foo;
foo!(Direct);
bar!(Alias);
//- /lib.rs crate:foo
use crate::foo as bar;
mod m {
#[macro_export]
macro_rules! foo {
($x:ident) => { struct $x; }
}
}
"#,
expect![[r#"
crate
Alias: t v
Direct: t v
foo: te
"#]],
);
}
#[test]
fn path_qualified_macros() {
check(
r#"
macro_rules! foo {
($x:ident) => { struct $x; }
}
crate::foo!(NotResolved);
crate::bar!(OkCrate);
bar!(OkPlain);
alias1!(NotHere);
m::alias1!(OkAliasPlain);
m::alias2!(OkAliasSuper);
m::alias3!(OkAliasCrate);
not_found!(NotFound);
mod m {
#[macro_export]
macro_rules! bar {
($x:ident) => { struct $x; }
}
pub use bar as alias1;
pub use super::bar as alias2;
pub use crate::bar as alias3;
pub use self::bar as not_found;
}
"#,
expect![[r#"
crate
OkAliasCrate: t v
OkAliasPlain: t v
OkAliasSuper: t v
OkCrate: t v
OkPlain: t v
bar: m
m: t
crate::m
alias1: mi
alias2: mi
alias3: mi
not_found: _
"#]],
);
}
#[test]
fn macro_dollar_crate_is_correct_in_item() {
cov_mark::check!(macro_dollar_crate_self);
check(
r#"
//- /main.rs crate:main deps:foo
#[macro_use]
extern crate foo;
#[macro_use]
mod m {
macro_rules! current {
() => {
use $crate::Foo as FooSelf;
}
}
}
struct Foo;
current!();
not_current1!();
foo::not_current2!();
//- /lib.rs crate:foo
mod m {
#[macro_export]
macro_rules! not_current1 {
() => {
use $crate::Bar;
}
}
}
#[macro_export]
macro_rules! not_current2 {
() => {
use $crate::Baz;
}
}
pub struct Bar;
pub struct Baz;
"#,
expect![[r#"
crate
Bar: ti vi
Baz: ti vi
Foo: t v
FooSelf: ti vi
foo: te
m: t
crate::m
"#]],
);
}
#[test]
fn macro_dollar_crate_is_correct_in_indirect_deps() {
cov_mark::check!(macro_dollar_crate_other);
// From std
check(
r#"
//- /main.rs edition:2018 crate:main deps:std
foo!();
//- /std.rs crate:std deps:core
pub use core::foo;
pub mod prelude {
pub mod rust_2018 {}
}
#[macro_use]
mod std_macros;
//- /core.rs crate:core
#[macro_export]
macro_rules! foo {
() => {
use $crate::bar;
}
}
pub struct bar;
"#,
expect![[r#"
crate
bar: ti vi
"#]],
);
}
#[test]
fn macro_dollar_crate_is_correct_in_derive_meta() {
let map = compute_crate_def_map(
r#"
//- minicore: derive, clone
//- /main.rs crate:main deps:lib
lib::foo!();
//- /lib.rs crate:lib
#[macro_export]
macro_rules! foo {
() => {
#[derive($crate::Clone)]
struct S;
}
}
pub use core::clone::Clone;
"#,
);
assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1);
}
#[test]
fn expand_derive() {
let map = compute_crate_def_map(
r#"
//- /main.rs crate:main deps:core
use core::Copy;
#[core::derive(Copy, core::Clone)]
struct Foo;
//- /core.rs crate:core
#[rustc_builtin_macro]
pub macro derive($item:item) {}
#[rustc_builtin_macro]
pub macro Copy {}
#[rustc_builtin_macro]
pub macro Clone {}
"#,
);
assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 2);
}
#[test]
fn resolve_builtin_derive() {
check(
r#"
//- /main.rs crate:main deps:core
use core::*;
//- /core.rs crate:core
#[rustc_builtin_macro]
pub macro Clone {}
pub trait Clone {}
"#,
expect![[r#"
crate
Clone: t m
"#]],
);
}
#[test]
fn builtin_derive_with_unresolved_attributes_fall_back() {
// Tests that we still resolve derives after ignoring an unresolved attribute.
cov_mark::check!(unresolved_attribute_fallback);
let map = compute_crate_def_map(
r#"
//- /main.rs crate:main deps:core
use core::{Clone, derive};
#[derive(Clone)]
#[unresolved]
struct Foo;
//- /core.rs crate:core
#[rustc_builtin_macro]
pub macro derive($item:item) {}
#[rustc_builtin_macro]
pub macro Clone {}
"#,
);
assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1);
}
#[test]
fn unresolved_attributes_fall_back_track_per_file_moditems() {
// Tests that we track per-file ModItems when ignoring an unresolved attribute.
// Just tracking the `ModItem` leads to `Foo` getting ignored.
check(
r#"
//- /main.rs crate:main
mod submod;
#[unresolved]
struct Foo;
//- /submod.rs
#[unresolved]
struct Bar;
"#,
expect![[r#"
crate
Foo: t v
submod: t
crate::submod
Bar: t v
"#]],
);
}
#[test]
fn unresolved_attrs_extern_block_hang() {
// Regression test for https://github.com/rust-lang/rust-analyzer/issues/8905
check(
r#"
#[unresolved]
extern "C" {
#[unresolved]
fn f();
}
"#,
expect![[r#"
crate
f: v
"#]],
);
}
#[test]
fn macros_in_extern_block() {
check(
r#"
macro_rules! m {
() => { static S: u8; };
}
extern {
m!();
}
"#,
expect![[r#"
crate
S: v
"#]],
);
}
#[test]
fn resolves_derive_helper() {
cov_mark::check!(resolved_derive_helper);
check(
r#"
//- /main.rs crate:main deps:proc
#[rustc_builtin_macro]
pub macro derive($item:item) {}
#[derive(proc::Derive)]
#[helper]
#[unresolved]
struct S;
//- /proc.rs crate:proc
#![crate_type="proc-macro"]
#[proc_macro_derive(Derive, attributes(helper))]
fn derive() {}
"#,
expect![[r#"
crate
S: t v
derive: m
"#]],
);
}
#[test]
fn resolves_derive_helper_rustc_builtin_macro() {
cov_mark::check!(resolved_derive_helper);
// This is NOT the correct usage of `default` helper attribute, but we don't resolve helper
// attributes on non mod items in hir nameres.
check(
r#"
//- minicore: derive, default
#[derive(Default)]
#[default]
enum E {
A,
B,
}
"#,
expect![[r#"
crate
E: t
"#]],
);
}
#[test]
fn unresolved_attr_with_cfg_attr_hang() {
// Another regression test for https://github.com/rust-lang/rust-analyzer/issues/8905
check(
r#"
#[cfg_attr(not(off), unresolved, unresolved)]
struct S;
"#,
expect![[r#"
crate
S: t v
"#]],
);
}
#[test]
fn macro_expansion_overflow() {
cov_mark::check!(macro_expansion_overflow);
check(
r#"
macro_rules! a {
($e:expr; $($t:tt)*) => {
b!(static = (); $($t)*);
};
() => {};
}
macro_rules! b {
(static = $e:expr; $($t:tt)*) => {
a!($e; $($t)*);
};
() => {};
}
b! { static = #[] ();}
"#,
expect![[r#"
crate
"#]],
);
}
#[test]
fn macros_defining_macros() {
check(
r#"
macro_rules! item {
($item:item) => { $item }
}
item! {
macro_rules! indirect_macro { () => { struct S {} } }
}
indirect_macro!();
"#,
expect![[r#"
crate
S: t
"#]],
);
}
#[test]
fn resolves_proc_macros() {
check(
r#"
#![crate_type="proc-macro"]
struct TokenStream;
#[proc_macro]
pub fn function_like_macro(args: TokenStream) -> TokenStream {
args
}
#[proc_macro_attribute]
pub fn attribute_macro(_args: TokenStream, item: TokenStream) -> TokenStream {
item
}
#[proc_macro_derive(DummyTrait)]
pub fn derive_macro(_item: TokenStream) -> TokenStream {
TokenStream
}
#[proc_macro_derive(AnotherTrait, attributes(helper_attr))]
pub fn derive_macro_2(_item: TokenStream) -> TokenStream {
TokenStream
}
"#,
expect![[r#"
crate
AnotherTrait: m
DummyTrait: m
TokenStream: t v
attribute_macro: v m
derive_macro: v
derive_macro_2: v
function_like_macro: v m
"#]],
);
}
#[test]
fn proc_macro_censoring() {
// Make sure that only proc macros are publicly exported from proc-macro crates.
check(
r#"
//- /main.rs crate:main deps:macros
pub use macros::*;
//- /macros.rs crate:macros
#![crate_type="proc-macro"]
pub struct TokenStream;
#[proc_macro]
pub fn function_like_macro(args: TokenStream) -> TokenStream {
args
}
#[proc_macro_attribute]
pub fn attribute_macro(_args: TokenStream, item: TokenStream) -> TokenStream {
item
}
#[proc_macro_derive(DummyTrait)]
pub fn derive_macro(_item: TokenStream) -> TokenStream {
TokenStream
}
#[macro_export]
macro_rules! mbe {
() => {};
}
"#,
expect![[r#"
crate
DummyTrait: m
attribute_macro: m
function_like_macro: m
"#]],
);
}
#[test]
fn collects_derive_helpers() {
let db = TestDB::with_files(
r#"
#![crate_type="proc-macro"]
struct TokenStream;
#[proc_macro_derive(AnotherTrait, attributes(helper_attr))]
pub fn derive_macro_2(_item: TokenStream) -> TokenStream {
TokenStream
}
"#,
);
let krate = db.crate_graph().iter().next().unwrap();
let def_map = db.crate_def_map(krate);
assert_eq!(def_map.data.exported_derives.len(), 1);
match def_map.data.exported_derives.values().next() {
Some(helpers) => match &**helpers {
[attr] => assert_eq!(attr.display(&db).to_string(), "helper_attr"),
_ => unreachable!(),
},
_ => unreachable!(),
}
}
#[test]
fn resolve_macro_def() {
check(
r#"
pub macro structs($($i:ident),*) {
$(struct $i { field: u32 } )*
}
structs!(Foo);
"#,
expect![[r#"
crate
Foo: t
structs: m
"#]],
);
}
#[test]
fn macro_in_prelude() {
check(
r#"
//- /lib.rs edition:2018 crate:lib deps:std
global_asm!();
//- /std.rs crate:std
pub mod prelude {
pub mod rust_2018 {
pub macro global_asm() {
pub struct S;
}
}
}
"#,
expect![[r#"
crate
S: t v
"#]],
)
}
#[test]
fn issue9358_bad_macro_stack_overflow() {
cov_mark::check!(issue9358_bad_macro_stack_overflow);
check(
r#"
macro_rules! m {
($cond:expr) => { m!($cond, stringify!($cond)) };
($cond:expr, $($arg:tt)*) => { $cond };
}
m!(
"#,
expect![[r#"
crate
"#]],
)
}
#[test]
fn eager_macro_correctly_resolves_contents() {
// Eager macros resolve any contained macros when expanded. This should work correctly with the
// usual name resolution rules, so both of these `include!`s should include the right file.
check(
r#"
//- /lib.rs
#[rustc_builtin_macro]
macro_rules! include { () => {} }
include!(inner_a!());
include!(crate::inner_b!());
#[macro_export]
macro_rules! inner_a {
() => { "inc_a.rs" };
}
#[macro_export]
macro_rules! inner_b {
() => { "inc_b.rs" };
}
//- /inc_a.rs
struct A;
//- /inc_b.rs
struct B;
"#,
expect![[r#"
crate
A: t v
B: t v
inner_a: m
inner_b: m
"#]],
);
}
#[test]
fn eager_macro_correctly_resolves_dollar_crate() {
// MBE -> eager -> $crate::mbe
check(
r#"
//- /lib.rs
#[rustc_builtin_macro]
macro_rules! include { () => {} }
#[macro_export]
macro_rules! inner {
() => { "inc.rs" };
}
macro_rules! m {
() => { include!($crate::inner!()); };
}
m!();
//- /inc.rs
struct A;
"#,
expect![[r#"
crate
A: t v
inner: m
"#]],
);
// eager -> MBE -> $crate::mbe
check(
r#"
//- /lib.rs
#[rustc_builtin_macro]
macro_rules! include { () => {} }
#[macro_export]
macro_rules! inner {
() => { "inc.rs" };
}
macro_rules! n {
() => {
$crate::inner!()
};
}
include!(n!());
//- /inc.rs
struct A;
"#,
expect![[r#"
crate
A: t v
inner: m
"#]],
);
}
#[test]
fn nested_include() {
check(
r#"
//- minicore: include
//- /lib.rs
include!("out_dir/includes.rs");
//- /out_dir/includes.rs
pub mod company_name {
pub mod network {
pub mod v1 {
include!("company_name.network.v1.rs");
}
}
}
//- /out_dir/company_name.network.v1.rs
pub struct IpAddress {
pub ip_type: &'static str,
}
/// Nested message and enum types in `IpAddress`.
pub mod ip_address {
pub enum IpType {
IpV4(u32),
}
}
"#,
expect![[r#"
crate
company_name: t
crate::company_name
network: t
crate::company_name::network
v1: t
crate::company_name::network::v1
IpAddress: t
ip_address: t
crate::company_name::network::v1::ip_address
IpType: t
"#]],
);
}
#[test]
fn macro_use_imports_all_macro_types() {
let db = TestDB::with_files(
r#"
//- /main.rs crate:main deps:lib
#[macro_use]
extern crate lib;
//- /lib.rs crate:lib deps:proc
pub use proc::*;
#[macro_export]
macro_rules! legacy { () => () }
pub macro macro20 {}
//- /proc.rs crate:proc
#![crate_type="proc-macro"]
struct TokenStream;
#[proc_macro_attribute]
fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a }
"#,
);
let krate = db.crate_graph().iter().next().unwrap();
let def_map = db.crate_def_map(krate);
let root_module = &def_map[DefMap::ROOT].scope;
assert!(
root_module.legacy_macros().count() == 0,
"`#[macro_use]` shouldn't bring macros into textual macro scope",
);
let actual = def_map
.macro_use_prelude
.keys()
.map(|name| name.display(&db).to_string())
.sorted()
.join("\n");
expect![[r#"
legacy
macro20
proc_attr"#]]
.assert_eq(&actual);
}
#[test]
fn non_prelude_macros_take_precedence_over_macro_use_prelude() {
check(
r#"
//- /lib.rs edition:2021 crate:lib deps:dep,core
#[macro_use]
extern crate dep;
macro foo() { struct Ok; }
macro bar() { fn ok() {} }
foo!();
bar!();
//- /dep.rs crate:dep
#[macro_export]
macro_rules! foo {
() => { struct NotOk; }
}
//- /core.rs crate:core
pub mod prelude {
pub mod rust_2021 {
#[macro_export]
macro_rules! bar {
() => { fn not_ok() {} }
}
}
}
"#,
expect![[r#"
crate
Ok: t v
bar: m
dep: te
foo: m
ok: v
"#]],
);
}
#[test]
fn macro_use_prelude_is_eagerly_expanded() {
// See FIXME in `ModCollector::collect_macro_call()`.
check(
r#"
//- /main.rs crate:main deps:lib
#[macro_use]
extern crate lib;
mk_foo!();
mod a {
foo!();
}
//- /lib.rs crate:lib
#[macro_export]
macro_rules! mk_foo {
() => {
macro_rules! foo {
() => { struct Ok; }
}
}
}
"#,
expect![[r#"
crate
a: t
lib: te
crate::a
Ok: t v
"#]],
);
}
#[test]
fn macro_sub_namespace() {
let map = compute_crate_def_map(
r#"
//- minicore: derive, clone
macro_rules! Clone { () => {} }
macro_rules! derive { () => {} }
#[derive(Clone)]
struct S;
"#,
);
assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1);
}
#[test]
fn macro_sub_namespace2() {
check(
r#"
//- /main.rs edition:2021 crate:main deps:proc,core
use proc::{foo, bar};
foo!();
bar!();
//- /proc.rs crate:proc
#![crate_type="proc-macro"]
#[proc_macro_derive(foo)]
pub fn foo() {}
#[proc_macro_attribute]
pub fn bar() {}
//- /core.rs crate:core
pub mod prelude {
pub mod rust_2021 {
pub macro foo() {
struct Ok;
}
pub macro bar() {
fn ok() {}
}
}
}
"#,
expect![[r#"
crate
Ok: t v
bar: mi
foo: mi
ok: v
"#]],
);
}