rust/tests/ui/macros/stringify.rs
Matthias Krüger 3a0562b93c
Rollup merge of #118726 - dtolnay:matchguardlet, r=compiler-errors
Do not parenthesize exterior struct lit inside match guards

Before this PR, the AST pretty-printer injects parentheses around expressions any time parens _could_ be needed depending on what else is in the code that surrounds that expression. But the pretty-printer did not pass around enough context to understand whether parentheses really _are_ needed on any particular expression. As a consequence, there are false positives where unneeded parentheses are being inserted.

Example:

```rust
#![feature(if_let_guard)]

macro_rules! pp {
    ($e:expr) => {
        stringify!($e)
    };
}

fn main() {
    println!("{}", pp!(match () { () if let _ = Struct {} => {} }));
}
```

**Before:**

```console
match () { () if let _ = (Struct {}) => {} }
```

**After:**

```console
match () { () if let _ = Struct {} => {} }
```

This PR introduces a bit of state that is passed across various expression printing methods to help understand accurately whether particular situations require parentheses injected by the pretty printer, and it fixes one such false positive involving match guards as shown above.

There are other parenthesization false positive cases not fixed by this PR. I intend to address these in follow-up PRs. For example here is one: the expression `{ let _ = match x {} + 1; }` is pretty-printed as `{ let _ = (match x {}) + 1; }` despite there being no reason for parentheses to appear there.
2023-12-11 20:46:49 +01:00

813 lines
25 KiB
Rust

// run-pass
// edition:2021
// compile-flags: --test
#![allow(incomplete_features)]
#![feature(async_closure)]
#![feature(auto_traits)]
#![feature(box_patterns)]
#![feature(const_trait_impl)]
#![feature(coroutines)]
#![feature(decl_macro)]
#![feature(explicit_tail_calls)]
#![feature(if_let_guard)]
#![feature(let_chains)]
#![feature(more_qualified_paths)]
#![feature(never_patterns)]
#![feature(raw_ref_op)]
#![feature(trait_alias)]
#![feature(try_blocks)]
#![feature(type_ascription)]
#![feature(yeet_expr)]
#![deny(unused_macros)]
// These macros force the use of AST pretty-printing by converting the input to
// a particular fragment specifier.
macro_rules! block { ($block:block) => { stringify!($block) }; }
macro_rules! expr { ($expr:expr) => { stringify!($expr) }; }
macro_rules! item { ($item:item) => { stringify!($item) }; }
macro_rules! meta { ($meta:meta) => { stringify!($meta) }; }
macro_rules! pat { ($pat:pat) => { stringify!($pat) }; }
macro_rules! path { ($path:path) => { stringify!($path) }; }
macro_rules! stmt { ($stmt:stmt) => { stringify!($stmt) }; }
macro_rules! ty { ($ty:ty) => { stringify!($ty) }; }
macro_rules! vis { ($vis:vis) => { stringify!($vis) }; }
// Use this when AST pretty-printing and TokenStream pretty-printing give
// the same result (which is preferable.)
macro_rules! c1 {
($frag:ident, [$($tt:tt)*], $s:literal) => {
assert_eq!($frag!($($tt)*), $s);
assert_eq!(stringify!($($tt)*), $s);
};
}
// Use this when AST pretty-printing and TokenStream pretty-printing give
// different results.
//
// `c1` and `c2` could be in a single macro, but having them separate makes it
// easy to find the cases where the two pretty-printing approaches give
// different results.
macro_rules! c2 {
($frag:ident, [$($tt:tt)*], $s1:literal, $s2:literal $(,)?) => {
assert_ne!($s1, $s2, "should use `c1!` instead");
assert_eq!($frag!($($tt)*), $s1);
assert_eq!(stringify!($($tt)*), $s2);
};
}
#[test]
fn test_block() {
c1!(block, [ {} ], "{}");
c1!(block, [ { true } ], "{ true }");
c1!(block, [ { return } ], "{ return }");
c1!(block, [ { return; } ], "{ return; }");
c1!(block,
[ {
let _;
true
} ],
"{ let _; true }"
);
}
#[test]
fn test_expr() {
// ExprKind::Array
c1!(expr, [ [] ], "[]");
c1!(expr, [ [true] ], "[true]");
c2!(expr, [ [true,] ], "[true]", "[true,]");
c1!(expr, [ [true, true] ], "[true, true]");
// ExprKind::ConstBlock
// FIXME: todo
// ExprKind::Call
c1!(expr, [ f() ], "f()");
c1!(expr, [ f::<u8>() ], "f::<u8>()");
c2!(expr, [ f :: < u8>( ) ], "f::<u8>()", "f :: < u8>()");
c1!(expr, [ f::<1>() ], "f::<1>()");
c1!(expr, [ f::<'a, u8, 1>() ], "f::<'a, u8, 1>()");
c1!(expr, [ f(true) ], "f(true)");
c2!(expr, [ f(true,) ], "f(true)", "f(true,)");
c1!(expr, [ ()() ], "()()");
// ExprKind::MethodCall
c1!(expr, [ x.f() ], "x.f()");
c1!(expr, [ x.f::<u8>() ], "x.f::<u8>()");
c1!(expr, [ x.collect::<Vec<_>>() ], "x.collect::<Vec<_>>()");
// ExprKind::Tup
c1!(expr, [ () ], "()");
c1!(expr, [ (true,) ], "(true,)");
c1!(expr, [ (true, false) ], "(true, false)");
c2!(expr, [ (true, false,) ], "(true, false)", "(true, false,)");
// ExprKind::Binary
c1!(expr, [ true || false ], "true || false");
c1!(expr, [ true || false && false ], "true || false && false");
c1!(expr, [ a < 1 && 2 < b && c > 3 && 4 > d ], "a < 1 && 2 < b && c > 3 && 4 > d");
c2!(expr, [ a & b & !c ], "a & b & !c", "a & b &!c"); // FIXME
c1!(expr, [ a + b * c - d + -1 * -2 - -3], "a + b * c - d + -1 * -2 - -3");
c2!(expr, [ x = !y ], "x = !y", "x =!y"); // FIXME
// ExprKind::Unary
c1!(expr, [ *expr ], "*expr");
c1!(expr, [ !expr ], "!expr");
c1!(expr, [ -expr ], "-expr");
// ExprKind::Lit
c1!(expr, [ 'x' ], "'x'");
c1!(expr, [ 1_000_i8 ], "1_000_i8");
c1!(expr, [ 1.00000000000000001 ], "1.00000000000000001");
// ExprKind::Cast
c1!(expr, [ expr as T ], "expr as T");
c1!(expr, [ expr as T<u8> ], "expr as T<u8>");
// ExprKind::Type: there is no syntax for type ascription.
// ExprKind::Let
c1!(expr, [ if let Some(a) = b { c } else { d } ], "if let Some(a) = b { c } else { d }");
c1!(expr, [ if let _ = true && false {} ], "if let _ = true && false {}");
c1!(expr, [ if let _ = (true && false) {} ], "if let _ = (true && false) {}");
macro_rules! c2_if_let {
($expr:expr, $expr_expected:expr, $tokens_expected:expr $(,)?) => {
c2!(expr, [ if let _ = $expr {} ], $expr_expected, $tokens_expected);
};
}
c2_if_let!(
true && false,
"if let _ = (true && false) {}",
"if let _ = true && false {}",
);
c2!(expr,
[ match () { _ if let _ = Struct {} => {} } ],
"match () { _ if let _ = Struct {} => {} }",
"match() { _ if let _ = Struct {} => {} }",
);
// ExprKind::If
c1!(expr, [ if true {} ], "if true {}");
c2!(expr, [ if !true {} ], "if !true {}", "if!true {}"); // FIXME
c1!(expr, [ if ::std::blah() { } else { } ], "if ::std::blah() {} else {}");
c1!(expr, [ if let true = true {} else {} ], "if let true = true {} else {}");
c1!(expr,
[ if true {
} else if false {
} ],
"if true {} else if false {}"
);
c1!(expr,
[ if true {
} else if false {
} else {
} ],
"if true {} else if false {} else {}"
);
c1!(expr,
[ if true {
return;
} else if false {
0
} else {
0
} ],
"if true { return; } else if false { 0 } else { 0 }"
);
// ExprKind::While
c1!(expr, [ while true {} ], "while true {}");
c1!(expr, [ 'a: while true {} ], "'a: while true {}");
c1!(expr, [ while let true = true {} ], "while let true = true {}");
// ExprKind::ForLoop
c1!(expr, [ for _ in x {} ], "for _ in x {}");
c1!(expr, [ 'a: for _ in x {} ], "'a: for _ in x {}");
// ExprKind::Loop
c1!(expr, [ loop {} ], "loop {}");
c1!(expr, [ 'a: loop {} ], "'a: loop {}");
// ExprKind::Match
c1!(expr, [ match self {} ], "match self {}");
c1!(expr,
[ match self {
Ok => 1,
} ],
"match self { Ok => 1, }"
);
c1!(expr,
[ match self {
Ok => 1,
Err => 0,
} ],
"match self { Ok => 1, Err => 0, }"
);
// ExprKind::Closure
c1!(expr, [ || {} ], "|| {}");
c1!(expr, [ |x| {} ], "|x| {}");
c1!(expr, [ |x: u8| {} ], "|x: u8| {}");
c1!(expr, [ || () ], "|| ()");
c1!(expr, [ move || self ], "move || self");
c1!(expr, [ async || self ], "async || self");
c1!(expr, [ async move || self ], "async move || self");
c1!(expr, [ static || self ], "static || self");
c1!(expr, [ static move || self ], "static move || self");
c1!(expr, [ static async || self ], "static async || self");
c1!(expr, [ static async move || self ], "static async move || self");
c1!(expr, [ || -> u8 { self } ], "|| -> u8 { self }");
c2!(expr, [ 1 + || {} ], "1 + (|| {})", "1 + || {}"); // AST??
// ExprKind::Block
c1!(expr, [ {} ], "{}");
c1!(expr, [ unsafe {} ], "unsafe {}");
c1!(expr, [ 'a: {} ], "'a: {}");
c1!(expr, [ #[attr] {} ], "#[attr] {}");
c2!(expr,
[
{
#![attr]
}
],
"{\n\
\x20 #![attr]\n\
}",
"{ #![attr] }"
);
// ExprKind::Async
c1!(expr, [ async {} ], "async {}");
c1!(expr, [ async move {} ], "async move {}");
// ExprKind::Await
c1!(expr, [ expr.await ], "expr.await");
// ExprKind::TryBlock
c1!(expr, [ try {} ], "try {}");
// ExprKind::Assign
c1!(expr, [ expr = true ], "expr = true");
// ExprKind::AssignOp
c1!(expr, [ expr += true ], "expr += true");
// ExprKind::Field
c1!(expr, [ expr.field ], "expr.field");
c1!(expr, [ expr.0 ], "expr.0");
// ExprKind::Index
c1!(expr, [ expr[true] ], "expr[true]");
// ExprKind::Range
c1!(expr, [ .. ], "..");
c1!(expr, [ ..hi ], "..hi");
c1!(expr, [ lo.. ], "lo..");
c1!(expr, [ lo..hi ], "lo..hi");
c2!(expr, [ lo .. hi ], "lo..hi", "lo .. hi");
c1!(expr, [ ..=hi ], "..=hi");
c1!(expr, [ lo..=hi ], "lo..=hi");
c1!(expr, [ -2..=-1 ], "-2..=-1");
// ExprKind::Underscore
// FIXME: todo
// ExprKind::Path
c1!(expr, [ thing ], "thing");
c1!(expr, [ m::thing ], "m::thing");
c1!(expr, [ self::thing ], "self::thing");
c1!(expr, [ crate::thing ], "crate::thing");
c1!(expr, [ Self::thing ], "Self::thing");
c1!(expr, [ <Self as T>::thing ], "<Self as T>::thing");
c1!(expr, [ Self::<'static> ], "Self::<'static>");
// ExprKind::AddrOf
c1!(expr, [ &expr ], "&expr");
c1!(expr, [ &mut expr ], "&mut expr");
c1!(expr, [ &raw const expr ], "&raw const expr");
c1!(expr, [ &raw mut expr ], "&raw mut expr");
// ExprKind::Break
c1!(expr, [ break ], "break");
c1!(expr, [ break 'a ], "break 'a");
c1!(expr, [ break true ], "break true");
c1!(expr, [ break 'a true ], "break 'a true");
// ExprKind::Continue
c1!(expr, [ continue ], "continue");
c1!(expr, [ continue 'a ], "continue 'a");
// ExprKind::Ret
c1!(expr, [ return ], "return");
c1!(expr, [ return true ], "return true");
// ExprKind::InlineAsm: untestable because this test works pre-expansion.
// ExprKind::OffsetOf: untestable because this test works pre-expansion.
// ExprKind::MacCall
c1!(expr, [ mac!(...) ], "mac!(...)");
c1!(expr, [ mac![...] ], "mac![...]");
c1!(expr, [ mac! { ... } ], "mac! { ... }");
// ExprKind::Struct
c1!(expr, [ Struct {} ], "Struct {}");
c1!(expr, [ <Struct as Trait>::Type {} ], "<Struct as Trait>::Type {}");
c1!(expr, [ Struct { .. } ], "Struct { .. }");
c1!(expr, [ Struct { ..base } ], "Struct { ..base }");
c1!(expr, [ Struct { x } ], "Struct { x }");
c1!(expr, [ Struct { x, .. } ], "Struct { x, .. }");
c1!(expr, [ Struct { x, ..base } ], "Struct { x, ..base }");
c1!(expr, [ Struct { x: true } ], "Struct { x: true }");
c1!(expr, [ Struct { x: true, .. } ], "Struct { x: true, .. }");
c1!(expr, [ Struct { x: true, ..base } ], "Struct { x: true, ..base }");
// ExprKind::Repeat
c1!(expr, [ [(); 0] ], "[(); 0]");
// ExprKind::Paren
c1!(expr, [ (expr) ], "(expr)");
// ExprKind::Try
c1!(expr, [ expr? ], "expr?");
// ExprKind::Yield
c1!(expr, [ yield ], "yield");
c1!(expr, [ yield true ], "yield true");
// ExprKind::Yeet
c1!(expr, [ do yeet ], "do yeet");
c1!(expr, [ do yeet 0 ], "do yeet 0");
// ExprKind::Become
// FIXME: todo
// ExprKind::IncludedBytes
// FIXME: todo
// ExprKind::FormatArgs: untestable because this test works pre-expansion.
// ExprKind::Err: untestable.
}
#[test]
fn test_item() {
// ItemKind::ExternCrate
c1!(item, [ extern crate std; ], "extern crate std;");
c1!(item, [ pub extern crate self as std; ], "pub extern crate self as std;");
// ItemKind::Use
c2!(item,
[ pub use crate::{a, b::c}; ],
"pub use crate::{a, b::c};",
"pub use crate::{ a, b::c };" // FIXME
);
c1!(item, [ pub use A::*; ], "pub use A::*;");
// ItemKind::Static
c1!(item, [ pub static S: () = {}; ], "pub static S: () = {};");
c1!(item, [ static mut S: () = {}; ], "static mut S: () = {};");
c1!(item, [ static S: (); ], "static S: ();");
c1!(item, [ static mut S: (); ], "static mut S: ();");
// ItemKind::Const
c1!(item, [ pub const S: () = {}; ], "pub const S: () = {};");
c1!(item, [ const S: (); ], "const S: ();");
// ItemKind::Fn
c1!(item,
[ pub default const async unsafe extern "C" fn f() {} ],
"pub default const async unsafe extern \"C\" fn f() {}"
);
c1!(item, [ fn g<T>(t: Vec<Vec<Vec<T>>>) {} ], "fn g<T>(t: Vec<Vec<Vec<T>>>) {}");
c1!(item,
[ fn h<'a>(t: &'a Vec<Cell<dyn D>>) {} ],
"fn h<'a>(t: &'a Vec<Cell<dyn D>>) {}"
);
// ItemKind::Mod
c1!(item, [ pub mod m; ], "pub mod m;");
c1!(item, [ mod m {} ], "mod m {}");
c1!(item, [ unsafe mod m; ], "unsafe mod m;");
c1!(item, [ unsafe mod m {} ], "unsafe mod m {}");
// ItemKind::ForeignMod
c1!(item, [ extern "C" {} ], "extern \"C\" {}");
c2!(item,
[ pub extern "C" {} ],
"extern \"C\" {}", // ??
"pub extern \"C\" {}"
);
c1!(item, [ unsafe extern "C++" {} ], "unsafe extern \"C++\" {}");
// ItemKind::GlobalAsm: untestable because this test works pre-expansion.
// ItemKind::TyAlias
c2!(item,
[
pub default type Type<'a>: Bound
where
Self: 'a,
= T;
],
"pub default type Type<'a>: Bound where Self: 'a = T;",
"pub default type Type<'a>: Bound where Self: 'a, = T;"
);
// ItemKind::Enum
c1!(item, [ pub enum Void {} ], "pub enum Void {}");
c1!(item,
[
enum Empty {
Unit,
Tuple(),
Struct {},
}
],
"enum Empty { Unit, Tuple(), Struct {}, }"
);
c2!(item,
[
enum Enum<T>
where
T: 'a,
{
Unit,
Tuple(T),
Struct { t: T },
}
],
"enum Enum<T> where T: 'a {\n\
\x20 Unit,\n\
\x20 Tuple(T),\n\
\x20 Struct {\n\
\x20 t: T,\n\
\x20 },\n\
}",
"enum Enum<T> where T: 'a, { Unit, Tuple(T), Struct { t: T }, }"
);
// ItemKind::Struct
c1!(item, [ pub struct Unit; ], "pub struct Unit;");
c1!(item, [ struct Tuple(); ], "struct Tuple();");
c1!(item, [ struct Tuple(T); ], "struct Tuple(T);");
c1!(item, [ struct Struct {} ], "struct Struct {}");
c2!(item,
[
struct Struct<T>
where
T: 'a,
{
t: T,
}
],
"struct Struct<T> where T: 'a {\n\
\x20 t: T,\n\
}",
"struct Struct<T> where T: 'a, { t: T, }"
);
// ItemKind::Union
c1!(item, [ pub union Union {} ], "pub union Union {}");
c2!(item,
[
union Union<T> where T: 'a {
t: T,
}
],
"union Union<T> where T: 'a {\n\
\x20 t: T,\n\
}",
"union Union<T> where T: 'a { t: T, }"
);
// ItemKind::Trait
c1!(item, [ pub unsafe auto trait Send {} ], "pub unsafe auto trait Send {}");
c2!(item,
[
trait Trait<'a>: Sized
where
Self: 'a,
{
}
],
"trait Trait<'a>: Sized where Self: 'a {}",
"trait Trait<'a>: Sized where Self: 'a, {}"
);
// ItemKind::TraitAlias
c1!(item,
[ pub trait Trait<T> = Sized where T: 'a; ],
"pub trait Trait<T> = Sized where T: 'a;"
);
// ItemKind::Impl
c1!(item, [ pub impl Struct {} ], "pub impl Struct {}");
c1!(item, [ impl<T> Struct<T> {} ], "impl<T> Struct<T> {}");
c1!(item, [ pub impl Trait for Struct {} ], "pub impl Trait for Struct {}");
c1!(item, [ impl<T> const Trait for T {} ], "impl<T> const Trait for T {}");
c1!(item, [ impl ~const Struct {} ], "impl ~const Struct {}");
// ItemKind::MacCall
c1!(item, [ mac!(...); ], "mac!(...);");
c1!(item, [ mac![...]; ], "mac![...];");
c1!(item, [ mac! { ... } ], "mac! { ... }");
// ItemKind::MacroDef
c1!(item,
[
macro_rules! stringify {
() => {};
}
],
"macro_rules! stringify { () => {}; }"
);
c2!(item,
[ pub macro stringify() {} ],
"pub macro stringify { () => {} }", // ??
"pub macro stringify() {}"
);
}
#[test]
fn test_meta() {
c1!(meta, [ k ], "k");
c1!(meta, [ k = "v" ], "k = \"v\"");
c1!(meta, [ list(k1, k2 = "v") ], "list(k1, k2 = \"v\")");
c1!(meta, [ serde::k ], "serde::k");
}
#[test]
fn test_pat() {
// PatKind::Wild
c1!(pat, [ _ ], "_");
// PatKind::Ident
c1!(pat, [ _x ], "_x");
c1!(pat, [ ref _x ], "ref _x");
c1!(pat, [ mut _x ], "mut _x");
c1!(pat, [ ref mut _x ], "ref mut _x");
c1!(pat, [ ref mut _x @ _ ], "ref mut _x @ _");
// PatKind::Struct
c1!(pat, [ Struct {} ], "Struct {}");
c1!(pat, [ Struct::<u8> {} ], "Struct::<u8> {}");
c2!(pat, [ Struct ::< u8 > {} ], "Struct::<u8> {}", "Struct ::< u8 > {}");
c1!(pat, [ Struct::<'static> {} ], "Struct::<'static> {}");
c1!(pat, [ Struct { x } ], "Struct { x }");
c1!(pat, [ Struct { x: _x } ], "Struct { x: _x }");
c1!(pat, [ Struct { .. } ], "Struct { .. }");
c1!(pat, [ Struct { x, .. } ], "Struct { x, .. }");
c1!(pat, [ Struct { x: _x, .. } ], "Struct { x: _x, .. }");
c1!(pat, [ <Struct as Trait>::Type {} ], "<Struct as Trait>::Type {}");
// PatKind::TupleStruct
c1!(pat, [ Tuple() ], "Tuple()");
c1!(pat, [ Tuple::<u8>() ], "Tuple::<u8>()");
c1!(pat, [ Tuple::<'static>() ], "Tuple::<'static>()");
c1!(pat, [ Tuple(x) ], "Tuple(x)");
c1!(pat, [ Tuple(..) ], "Tuple(..)");
c1!(pat, [ Tuple(x, ..) ], "Tuple(x, ..)");
c1!(pat, [ <Struct as Trait>::Type() ], "<Struct as Trait>::Type()");
// PatKind::Or
c1!(pat, [ true | false ], "true | false");
c2!(pat, [ | true ], "true", "| true");
c2!(pat, [ |true| false ], "true | false", "|true| false");
// PatKind::Path
c1!(pat, [ crate::Path ], "crate::Path");
c1!(pat, [ Path::<u8> ], "Path::<u8>");
c1!(pat, [ Path::<'static> ], "Path::<'static>");
c1!(pat, [ <Struct as Trait>::Type ], "<Struct as Trait>::Type");
// PatKind::Tuple
c1!(pat, [ () ], "()");
c1!(pat, [ (true,) ], "(true,)");
c1!(pat, [ (true, false) ], "(true, false)");
// PatKind::Box
c1!(pat, [ box pat ], "box pat");
// PatKind::Ref
c1!(pat, [ &pat ], "&pat");
c1!(pat, [ &mut pat ], "&mut pat");
// PatKind::Lit
c1!(pat, [ 1_000_i8 ], "1_000_i8");
// PatKind::Range
c1!(pat, [ ..1 ], "..1");
c1!(pat, [ 0.. ], "0..");
c1!(pat, [ 0..1 ], "0..1");
c1!(pat, [ 0..=1 ], "0..=1");
c1!(pat, [ -2..=-1 ], "-2..=-1");
// PatKind::Slice
c1!(pat, [ [] ], "[]");
c1!(pat, [ [true] ], "[true]");
c2!(pat, [ [true,] ], "[true]", "[true,]");
c1!(pat, [ [true, false] ], "[true, false]");
// PatKind::Rest
c1!(pat, [ .. ], "..");
// PatKind::Never
c1!(pat, [ Some(!) ], "Some(!)");
c1!(pat, [ None | Some(!) ], "None | Some(!)");
// PatKind::Paren
c1!(pat, [ (pat) ], "(pat)");
// PatKind::MacCall
c1!(pat, [ mac!(...) ], "mac!(...)");
c1!(pat, [ mac![...] ], "mac![...]");
c1!(pat, [ mac! { ... } ], "mac! { ... }");
}
#[test]
fn test_path() {
c1!(path, [ thing ], "thing");
c1!(path, [ m::thing ], "m::thing");
c1!(path, [ self::thing ], "self::thing");
c1!(path, [ crate::thing ], "crate::thing");
c1!(path, [ Self::thing ], "Self::thing");
c1!(path, [ Self<'static> ], "Self<'static>");
c2!(path, [ Self::<'static> ], "Self<'static>", "Self::<'static>");
c1!(path, [ Self() ], "Self()");
c1!(path, [ Self() -> () ], "Self() -> ()");
}
#[test]
fn test_stmt() {
// StmtKind::Local
c2!(stmt, [ let _ ], "let _;", "let _");
c2!(stmt, [ let x = true ], "let x = true;", "let x = true");
c2!(stmt, [ let x: bool = true ], "let x: bool = true;", "let x: bool = true");
c2!(stmt, [ let (a, b) = (1, 2) ], "let (a, b) = (1, 2);", "let(a, b) = (1, 2)"); // FIXME
c2!(stmt,
[ let (a, b): (u32, u32) = (1, 2) ],
"let (a, b): (u32, u32) = (1, 2);",
"let(a, b): (u32, u32) = (1, 2)" // FIXME
);
// StmtKind::Item
c1!(stmt, [ struct S; ], "struct S;");
c1!(stmt, [ struct S {} ], "struct S {}");
// StmtKind::Expr
c1!(stmt, [ loop {} ], "loop {}");
// StmtKind::Semi
c2!(stmt, [ 1 + 1 ], "1 + 1;", "1 + 1");
// StmtKind::Empty
c1!(stmt, [ ; ], ";");
// StmtKind::MacCall
c1!(stmt, [ mac!(...) ], "mac!(...)");
c1!(stmt, [ mac![...] ], "mac![...]");
c1!(stmt, [ mac! { ... } ], "mac! { ... }");
}
#[test]
fn test_ty() {
// TyKind::Slice
c1!(ty, [ [T] ], "[T]");
// TyKind::Array
c1!(ty, [ [T; 0] ], "[T; 0]");
// TyKind::Ptr
c1!(ty, [ *const T ], "*const T");
c1!(ty, [ *mut T ], "*mut T");
// TyKind::Ref
c1!(ty, [ &T ], "&T");
c1!(ty, [ &mut T ], "&mut T");
c1!(ty, [ &'a T ], "&'a T");
c1!(ty, [ &'a mut [T] ], "&'a mut [T]");
c1!(ty, [ &A<B<C<D<E>>>> ], "&A<B<C<D<E>>>>");
c2!(ty, [ &A<B<C<D<E> > > > ], "&A<B<C<D<E>>>>", "&A<B<C<D<E> > > >");
// TyKind::BareFn
c1!(ty, [ fn() ], "fn()");
c1!(ty, [ fn() -> () ], "fn() -> ()");
c1!(ty, [ fn(u8) ], "fn(u8)");
c1!(ty, [ fn(x: u8) ], "fn(x: u8)");
c2!(ty, [ for<> fn() ], "fn()", "for<> fn()");
c1!(ty, [ for<'a> fn() ], "for<'a> fn()");
// TyKind::Never
c1!(ty, [ ! ], "!");
// TyKind::Tup
c1!(ty, [ () ], "()");
c1!(ty, [ (T,) ], "(T,)");
c1!(ty, [ (T, U) ], "(T, U)");
// TyKind::AnonStruct: untestable in isolation.
// TyKind::AnonUnion: untestable in isolation.
// TyKind::Path
c1!(ty, [ T ], "T");
c1!(ty, [ Ref<'a> ], "Ref<'a>");
c1!(ty, [ PhantomData<T> ], "PhantomData<T>");
c2!(ty, [ PhantomData::<T> ], "PhantomData<T>", "PhantomData::<T>");
c2!(ty, [ Fn() -> ! ], "Fn() -> !", "Fn() ->!");
c2!(ty, [ Fn(u8) -> ! ], "Fn(u8) -> !", "Fn(u8) ->!"); // FIXME
c1!(ty, [ <Struct as Trait>::Type ], "<Struct as Trait>::Type");
// TyKind::TraitObject
c1!(ty, [ dyn Send ], "dyn Send");
c1!(ty, [ dyn Send + 'a ], "dyn Send + 'a");
c1!(ty, [ dyn 'a + Send ], "dyn 'a + Send");
c1!(ty, [ dyn ?Sized ], "dyn ?Sized");
c1!(ty, [ dyn ~const Clone ], "dyn ~const Clone");
c1!(ty, [ dyn for<'a> Send ], "dyn for<'a> Send");
// TyKind::ImplTrait
c1!(ty, [ impl Send ], "impl Send");
c1!(ty, [ impl Send + 'a ], "impl Send + 'a");
c1!(ty, [ impl 'a + Send ], "impl 'a + Send");
c1!(ty, [ impl ?Sized ], "impl ?Sized");
c1!(ty, [ impl ~const Clone ], "impl ~const Clone");
c1!(ty, [ impl for<'a> Send ], "impl for<'a> Send");
// TyKind::Paren
c1!(ty, [ (T) ], "(T)");
// TyKind::Typeof: unused for now.
// TyKind::Infer
c1!(ty, [ _ ], "_");
// TyKind::ImplicitSelf: there is no syntax for this.
// TyKind::MacCall
c1!(ty, [ mac!(...) ], "mac!(...)");
c1!(ty, [ mac![...] ], "mac![...]");
c1!(ty, [ mac! { ... } ], "mac! { ... }");
// TyKind::Err: untestable.
// TyKind::CVarArgs
// FIXME: todo
}
#[test]
fn test_vis() {
// VisibilityKind::Public
c2!(vis, [ pub ], "pub ", "pub");
// VisibilityKind::Restricted
c2!(vis, [ pub(crate) ], "pub(crate) ", "pub(crate)");
c2!(vis, [ pub(self) ], "pub(self) ", "pub(self)");
c2!(vis, [ pub(super) ], "pub(super) ", "pub(super)");
c2!(vis, [ pub(in crate) ], "pub(in crate) ", "pub(in crate)");
c2!(vis, [ pub(in self) ], "pub(in self) ", "pub(in self)");
c2!(vis, [ pub(in super) ], "pub(in super) ", "pub(in super)");
c2!(vis, [ pub(in path::to) ], "pub(in path::to) ", "pub(in path::to)");
c2!(vis, [ pub(in ::path::to) ], "pub(in ::path::to) ", "pub(in ::path::to)");
c2!(vis, [ pub(in self::path::to) ], "pub(in self::path::to) ", "pub(in self::path::to)");
c2!(vis,
[ pub(in super::path::to) ],
"pub(in super::path::to) ",
"pub(in super::path::to)"
);
// VisibilityKind::Inherited
// This one is different because directly calling `vis!` does not work.
macro_rules! inherited_vis { ($vis:vis struct) => { vis!($vis) }; }
assert_eq!(inherited_vis!(struct), "");
assert_eq!(stringify!(), "");
}
macro_rules! p {
([$($tt:tt)*], $s:literal) => {
assert_eq!(stringify!($($tt)*), $s);
};
}
#[test]
fn test_punct() {
// For all these cases, we should preserve spaces between the tokens.
// Otherwise, any old proc macro that parses pretty-printed code might glue
// together tokens that shouldn't be glued.
p!([ = = < < <= <= == == != != >= >= > > ], "= = < < <= <= == == != != >= >= > >");
p!([ && && & & || || | | ! ! ], "&& && & & || || | |!!"); // FIXME
p!([ ~ ~ @ @ # # ], "~ ~ @ @ # #");
p!([ . . .. .. ... ... ..= ..=], ".... .. ... ... ..= ..="); // FIXME
p!([ , , ; ; : : :: :: ], ",, ; ; : : :: ::"); // FIXME
p!([ -> -> <- <- => =>], "-> -> <- <- => =>");
p!([ $ $ ? ? ' ' ], "$$? ? ' '"); // FIXME
p!([ + + += += - - -= -= * * *= *= / / /= /= ], "+ + += += - - -= -= * * *= *= / / /= /=");
p!([ % % %= %= ^ ^ ^= ^= << << <<= <<= >> >> >>= >>= ],
"% % %= %= ^ ^ ^= ^= << << <<= <<= >> >> >>= >>=");
p!([ +! ?= |> >>@ --> <-- $$ =====> ], "+! ?= |> >>@ --> <-- $$=====>");
p!([ ,; ;, ** @@ $+$ >< <> ?? +== ], ",; ;, ** @@ $+$>< <> ?? +=="); // FIXME: `$ >` -> `$>`
p!([ :#!@|$=&*,+;*~? ], ":#!@|$=&*,+;*~?");
}