Files
another-boids-in-rust/vendor/prettyplease/tests/test_precedence.rs

901 lines
31 KiB
Rust

use proc_macro2::{Ident, Span, TokenStream};
use quote::ToTokens as _;
use std::mem;
use std::process::ExitCode;
use syn::punctuated::Punctuated;
use syn::visit_mut::{self, VisitMut};
use syn::{
token, AngleBracketedGenericArguments, Arm, BinOp, Block, Expr, ExprArray, ExprAssign,
ExprAsync, ExprAwait, ExprBinary, ExprBlock, ExprBreak, ExprCall, ExprCast, ExprClosure,
ExprConst, ExprContinue, ExprField, ExprForLoop, ExprIf, ExprIndex, ExprLet, ExprLit, ExprLoop,
ExprMacro, ExprMatch, ExprMethodCall, ExprPath, ExprRange, ExprRawAddr, ExprReference,
ExprReturn, ExprStruct, ExprTry, ExprTryBlock, ExprUnary, ExprUnsafe, ExprWhile, ExprYield,
File, GenericArgument, Generics, Item, ItemConst, Label, Lifetime, LifetimeParam, Lit, LitInt,
Macro, MacroDelimiter, Member, Pat, PatWild, Path, PathArguments, PathSegment,
PointerMutability, QSelf, RangeLimits, ReturnType, Stmt, StmtMacro, Token, Type, TypeInfer,
TypeParam, TypePath, UnOp, Visibility,
};
struct FlattenParens;
impl VisitMut for FlattenParens {
fn visit_expr_mut(&mut self, e: &mut Expr) {
while let Expr::Paren(paren) = e {
*e = mem::replace(&mut *paren.expr, Expr::PLACEHOLDER);
}
visit_mut::visit_expr_mut(self, e);
}
}
struct AsIfPrinted;
impl VisitMut for AsIfPrinted {
fn visit_generics_mut(&mut self, generics: &mut Generics) {
if generics.params.is_empty() {
generics.lt_token = None;
generics.gt_token = None;
}
if let Some(where_clause) = &generics.where_clause {
if where_clause.predicates.is_empty() {
generics.where_clause = None;
}
}
visit_mut::visit_generics_mut(self, generics);
}
fn visit_lifetime_param_mut(&mut self, param: &mut LifetimeParam) {
if param.bounds.is_empty() {
param.colon_token = None;
}
visit_mut::visit_lifetime_param_mut(self, param);
}
fn visit_stmt_mut(&mut self, stmt: &mut Stmt) {
if let Stmt::Expr(expr, semi) = stmt {
if let Expr::Macro(e) = expr {
if match e.mac.delimiter {
MacroDelimiter::Brace(_) => true,
MacroDelimiter::Paren(_) | MacroDelimiter::Bracket(_) => semi.is_some(),
} {
let expr = match mem::replace(expr, Expr::PLACEHOLDER) {
Expr::Macro(expr) => expr,
_ => unreachable!(),
};
*stmt = Stmt::Macro(StmtMacro {
attrs: expr.attrs,
mac: expr.mac,
semi_token: *semi,
});
}
}
}
visit_mut::visit_stmt_mut(self, stmt);
}
fn visit_type_param_mut(&mut self, param: &mut TypeParam) {
if param.bounds.is_empty() {
param.colon_token = None;
}
visit_mut::visit_type_param_mut(self, param);
}
}
#[test]
fn test_permutations() -> ExitCode {
fn iter(depth: usize, f: &mut dyn FnMut(Expr)) {
let span = Span::call_site();
// Expr::Path
f(Expr::Path(ExprPath {
// `x`
attrs: Vec::new(),
qself: None,
path: Path::from(Ident::new("x", span)),
}));
if false {
f(Expr::Path(ExprPath {
// `x::<T>`
attrs: Vec::new(),
qself: None,
path: Path {
leading_colon: None,
segments: Punctuated::from_iter([PathSegment {
ident: Ident::new("x", span),
arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
colon2_token: Some(Token![::](span)),
lt_token: Token![<](span),
args: Punctuated::from_iter([GenericArgument::Type(Type::Path(
TypePath {
qself: None,
path: Path::from(Ident::new("T", span)),
},
))]),
gt_token: Token![>](span),
}),
}]),
},
}));
f(Expr::Path(ExprPath {
// `<T as Trait>::CONST`
attrs: Vec::new(),
qself: Some(QSelf {
lt_token: Token![<](span),
ty: Box::new(Type::Path(TypePath {
qself: None,
path: Path::from(Ident::new("T", span)),
})),
position: 1,
as_token: Some(Token![as](span)),
gt_token: Token![>](span),
}),
path: Path {
leading_colon: None,
segments: Punctuated::from_iter([
PathSegment::from(Ident::new("Trait", span)),
PathSegment::from(Ident::new("CONST", span)),
]),
},
}));
}
let depth = match depth.checked_sub(1) {
Some(depth) => depth,
None => return,
};
// Expr::Assign
iter(depth, &mut |expr| {
iter(0, &mut |simple| {
f(Expr::Assign(ExprAssign {
// `x = $expr`
attrs: Vec::new(),
left: Box::new(simple.clone()),
eq_token: Token![=](span),
right: Box::new(expr.clone()),
}));
f(Expr::Assign(ExprAssign {
// `$expr = x`
attrs: Vec::new(),
left: Box::new(expr.clone()),
eq_token: Token![=](span),
right: Box::new(simple),
}));
});
});
// Expr::Binary
iter(depth, &mut |expr| {
iter(0, &mut |simple| {
for op in [
BinOp::Add(Token![+](span)),
//BinOp::Sub(Token![-](span)),
//BinOp::Mul(Token![*](span)),
//BinOp::Div(Token![/](span)),
//BinOp::Rem(Token![%](span)),
//BinOp::And(Token![&&](span)),
//BinOp::Or(Token![||](span)),
//BinOp::BitXor(Token![^](span)),
//BinOp::BitAnd(Token![&](span)),
//BinOp::BitOr(Token![|](span)),
//BinOp::Shl(Token![<<](span)),
//BinOp::Shr(Token![>>](span)),
//BinOp::Eq(Token![==](span)),
BinOp::Lt(Token![<](span)),
//BinOp::Le(Token![<=](span)),
//BinOp::Ne(Token![!=](span)),
//BinOp::Ge(Token![>=](span)),
//BinOp::Gt(Token![>](span)),
BinOp::ShlAssign(Token![<<=](span)),
] {
f(Expr::Binary(ExprBinary {
// `x + $expr`
attrs: Vec::new(),
left: Box::new(simple.clone()),
op,
right: Box::new(expr.clone()),
}));
f(Expr::Binary(ExprBinary {
// `$expr + x`
attrs: Vec::new(),
left: Box::new(expr.clone()),
op,
right: Box::new(simple.clone()),
}));
}
});
});
// Expr::Block
f(Expr::Block(ExprBlock {
// `{}`
attrs: Vec::new(),
label: None,
block: Block {
brace_token: token::Brace(span),
stmts: Vec::new(),
},
}));
// Expr::Break
f(Expr::Break(ExprBreak {
// `break`
attrs: Vec::new(),
break_token: Token![break](span),
label: None,
expr: None,
}));
iter(depth, &mut |expr| {
f(Expr::Break(ExprBreak {
// `break $expr`
attrs: Vec::new(),
break_token: Token![break](span),
label: None,
expr: Some(Box::new(expr)),
}));
});
// Expr::Call
iter(depth, &mut |expr| {
f(Expr::Call(ExprCall {
// `$expr()`
attrs: Vec::new(),
func: Box::new(expr),
paren_token: token::Paren(span),
args: Punctuated::new(),
}));
});
// Expr::Cast
iter(depth, &mut |expr| {
f(Expr::Cast(ExprCast {
// `$expr as T`
attrs: Vec::new(),
expr: Box::new(expr),
as_token: Token![as](span),
ty: Box::new(Type::Path(TypePath {
qself: None,
path: Path::from(Ident::new("T", span)),
})),
}));
});
// Expr::Closure
iter(depth, &mut |expr| {
f(Expr::Closure(ExprClosure {
// `|| $expr`
attrs: Vec::new(),
lifetimes: None,
constness: None,
movability: None,
asyncness: None,
capture: None,
or1_token: Token![|](span),
inputs: Punctuated::new(),
or2_token: Token![|](span),
output: ReturnType::Default,
body: Box::new(expr),
}));
});
// Expr::Field
iter(depth, &mut |expr| {
f(Expr::Field(ExprField {
// `$expr.field`
attrs: Vec::new(),
base: Box::new(expr),
dot_token: Token![.](span),
member: Member::Named(Ident::new("field", span)),
}));
});
// Expr::If
iter(depth, &mut |expr| {
f(Expr::If(ExprIf {
// `if $expr {}`
attrs: Vec::new(),
if_token: Token![if](span),
cond: Box::new(expr),
then_branch: Block {
brace_token: token::Brace(span),
stmts: Vec::new(),
},
else_branch: None,
}));
});
// Expr::Let
iter(depth, &mut |expr| {
f(Expr::Let(ExprLet {
attrs: Vec::new(),
let_token: Token![let](span),
pat: Box::new(Pat::Wild(PatWild {
attrs: Vec::new(),
underscore_token: Token![_](span),
})),
eq_token: Token![=](span),
expr: Box::new(expr),
}));
});
// Expr::Range
f(Expr::Range(ExprRange {
// `..`
attrs: Vec::new(),
start: None,
limits: RangeLimits::HalfOpen(Token![..](span)),
end: None,
}));
iter(depth, &mut |expr| {
f(Expr::Range(ExprRange {
// `..$expr`
attrs: Vec::new(),
start: None,
limits: RangeLimits::HalfOpen(Token![..](span)),
end: Some(Box::new(expr.clone())),
}));
f(Expr::Range(ExprRange {
// `$expr..`
attrs: Vec::new(),
start: Some(Box::new(expr)),
limits: RangeLimits::HalfOpen(Token![..](span)),
end: None,
}));
});
// Expr::Reference
iter(depth, &mut |expr| {
f(Expr::Reference(ExprReference {
// `&$expr`
attrs: Vec::new(),
and_token: Token![&](span),
mutability: None,
expr: Box::new(expr),
}));
});
// Expr::Return
f(Expr::Return(ExprReturn {
// `return`
attrs: Vec::new(),
return_token: Token![return](span),
expr: None,
}));
iter(depth, &mut |expr| {
f(Expr::Return(ExprReturn {
// `return $expr`
attrs: Vec::new(),
return_token: Token![return](span),
expr: Some(Box::new(expr)),
}));
});
// Expr::Try
iter(depth, &mut |expr| {
f(Expr::Try(ExprTry {
// `$expr?`
attrs: Vec::new(),
expr: Box::new(expr),
question_token: Token![?](span),
}));
});
// Expr::Unary
iter(depth, &mut |expr| {
for op in [
UnOp::Deref(Token![*](span)),
//UnOp::Not(Token![!](span)),
//UnOp::Neg(Token![-](span)),
] {
f(Expr::Unary(ExprUnary {
// `*$expr`
attrs: Vec::new(),
op,
expr: Box::new(expr.clone()),
}));
}
});
if false {
// Expr::Array
f(Expr::Array(ExprArray {
// `[]`
attrs: Vec::new(),
bracket_token: token::Bracket(span),
elems: Punctuated::new(),
}));
// Expr::Async
f(Expr::Async(ExprAsync {
// `async {}`
attrs: Vec::new(),
async_token: Token![async](span),
capture: None,
block: Block {
brace_token: token::Brace(span),
stmts: Vec::new(),
},
}));
// Expr::Await
iter(depth, &mut |expr| {
f(Expr::Await(ExprAwait {
// `$expr.await`
attrs: Vec::new(),
base: Box::new(expr),
dot_token: Token![.](span),
await_token: Token![await](span),
}));
});
// Expr::Block
f(Expr::Block(ExprBlock {
// `'a: {}`
attrs: Vec::new(),
label: Some(Label {
name: Lifetime::new("'a", span),
colon_token: Token![:](span),
}),
block: Block {
brace_token: token::Brace(span),
stmts: Vec::new(),
},
}));
iter(depth, &mut |expr| {
f(Expr::Block(ExprBlock {
// `{ $expr }`
attrs: Vec::new(),
label: None,
block: Block {
brace_token: token::Brace(span),
stmts: Vec::from([Stmt::Expr(expr.clone(), None)]),
},
}));
f(Expr::Block(ExprBlock {
// `{ $expr; }`
attrs: Vec::new(),
label: None,
block: Block {
brace_token: token::Brace(span),
stmts: Vec::from([Stmt::Expr(expr, Some(Token![;](span)))]),
},
}));
});
// Expr::Break
f(Expr::Break(ExprBreak {
// `break 'a`
attrs: Vec::new(),
break_token: Token![break](span),
label: Some(Lifetime::new("'a", span)),
expr: None,
}));
iter(depth, &mut |expr| {
f(Expr::Break(ExprBreak {
// `break 'a $expr`
attrs: Vec::new(),
break_token: Token![break](span),
label: Some(Lifetime::new("'a", span)),
expr: Some(Box::new(expr)),
}));
});
// Expr::Closure
f(Expr::Closure(ExprClosure {
// `|| -> T {}`
attrs: Vec::new(),
lifetimes: None,
constness: None,
movability: None,
asyncness: None,
capture: None,
or1_token: Token![|](span),
inputs: Punctuated::new(),
or2_token: Token![|](span),
output: ReturnType::Type(
Token![->](span),
Box::new(Type::Path(TypePath {
qself: None,
path: Path::from(Ident::new("T", span)),
})),
),
body: Box::new(Expr::Block(ExprBlock {
attrs: Vec::new(),
label: None,
block: Block {
brace_token: token::Brace(span),
stmts: Vec::new(),
},
})),
}));
// Expr::Const
f(Expr::Const(ExprConst {
// `const {}`
attrs: Vec::new(),
const_token: Token![const](span),
block: Block {
brace_token: token::Brace(span),
stmts: Vec::new(),
},
}));
// Expr::Continue
f(Expr::Continue(ExprContinue {
// `continue`
attrs: Vec::new(),
continue_token: Token![continue](span),
label: None,
}));
f(Expr::Continue(ExprContinue {
// `continue 'a`
attrs: Vec::new(),
continue_token: Token![continue](span),
label: Some(Lifetime::new("'a", span)),
}));
// Expr::ForLoop
iter(depth, &mut |expr| {
f(Expr::ForLoop(ExprForLoop {
// `for _ in $expr {}`
attrs: Vec::new(),
label: None,
for_token: Token![for](span),
pat: Box::new(Pat::Wild(PatWild {
attrs: Vec::new(),
underscore_token: Token![_](span),
})),
in_token: Token![in](span),
expr: Box::new(expr.clone()),
body: Block {
brace_token: token::Brace(span),
stmts: Vec::new(),
},
}));
f(Expr::ForLoop(ExprForLoop {
// `'a: for _ in $expr {}`
attrs: Vec::new(),
label: Some(Label {
name: Lifetime::new("'a", span),
colon_token: Token![:](span),
}),
for_token: Token![for](span),
pat: Box::new(Pat::Wild(PatWild {
attrs: Vec::new(),
underscore_token: Token![_](span),
})),
in_token: Token![in](span),
expr: Box::new(expr),
body: Block {
brace_token: token::Brace(span),
stmts: Vec::new(),
},
}));
});
// Expr::Index
iter(depth, &mut |expr| {
f(Expr::Index(ExprIndex {
// `$expr[0]`
attrs: Vec::new(),
expr: Box::new(expr),
bracket_token: token::Bracket(span),
index: Box::new(Expr::Lit(ExprLit {
attrs: Vec::new(),
lit: Lit::Int(LitInt::new("0", span)),
})),
}));
});
// Expr::Loop
f(Expr::Loop(ExprLoop {
// `loop {}`
attrs: Vec::new(),
label: None,
loop_token: Token![loop](span),
body: Block {
brace_token: token::Brace(span),
stmts: Vec::new(),
},
}));
f(Expr::Loop(ExprLoop {
// `'a: loop {}`
attrs: Vec::new(),
label: Some(Label {
name: Lifetime::new("'a", span),
colon_token: Token![:](span),
}),
loop_token: Token![loop](span),
body: Block {
brace_token: token::Brace(span),
stmts: Vec::new(),
},
}));
// Expr::Macro
f(Expr::Macro(ExprMacro {
// `m!()`
attrs: Vec::new(),
mac: Macro {
path: Path::from(Ident::new("m", span)),
bang_token: Token![!](span),
delimiter: MacroDelimiter::Paren(token::Paren(span)),
tokens: TokenStream::new(),
},
}));
f(Expr::Macro(ExprMacro {
// `m! {}`
attrs: Vec::new(),
mac: Macro {
path: Path::from(Ident::new("m", span)),
bang_token: Token![!](span),
delimiter: MacroDelimiter::Brace(token::Brace(span)),
tokens: TokenStream::new(),
},
}));
// Expr::Match
iter(depth, &mut |expr| {
f(Expr::Match(ExprMatch {
// `match $expr {}`
attrs: Vec::new(),
match_token: Token![match](span),
expr: Box::new(expr.clone()),
brace_token: token::Brace(span),
arms: Vec::new(),
}));
f(Expr::Match(ExprMatch {
// `match x { _ => $expr }`
attrs: Vec::new(),
match_token: Token![match](span),
expr: Box::new(Expr::Path(ExprPath {
attrs: Vec::new(),
qself: None,
path: Path::from(Ident::new("x", span)),
})),
brace_token: token::Brace(span),
arms: Vec::from([Arm {
attrs: Vec::new(),
pat: Pat::Wild(PatWild {
attrs: Vec::new(),
underscore_token: Token![_](span),
}),
guard: None,
fat_arrow_token: Token![=>](span),
body: Box::new(expr.clone()),
comma: None,
}]),
}));
f(Expr::Match(ExprMatch {
// `match x { _ if $expr => {} }`
attrs: Vec::new(),
match_token: Token![match](span),
expr: Box::new(Expr::Path(ExprPath {
attrs: Vec::new(),
qself: None,
path: Path::from(Ident::new("x", span)),
})),
brace_token: token::Brace(span),
arms: Vec::from([Arm {
attrs: Vec::new(),
pat: Pat::Wild(PatWild {
attrs: Vec::new(),
underscore_token: Token![_](span),
}),
guard: Some((Token![if](span), Box::new(expr))),
fat_arrow_token: Token![=>](span),
body: Box::new(Expr::Block(ExprBlock {
attrs: Vec::new(),
label: None,
block: Block {
brace_token: token::Brace(span),
stmts: Vec::new(),
},
})),
comma: None,
}]),
}));
});
// Expr::MethodCall
iter(depth, &mut |expr| {
f(Expr::MethodCall(ExprMethodCall {
// `$expr.method()`
attrs: Vec::new(),
receiver: Box::new(expr.clone()),
dot_token: Token![.](span),
method: Ident::new("method", span),
turbofish: None,
paren_token: token::Paren(span),
args: Punctuated::new(),
}));
f(Expr::MethodCall(ExprMethodCall {
// `$expr.method::<T>()`
attrs: Vec::new(),
receiver: Box::new(expr),
dot_token: Token![.](span),
method: Ident::new("method", span),
turbofish: Some(AngleBracketedGenericArguments {
colon2_token: Some(Token![::](span)),
lt_token: Token![<](span),
args: Punctuated::from_iter([GenericArgument::Type(Type::Path(
TypePath {
qself: None,
path: Path::from(Ident::new("T", span)),
},
))]),
gt_token: Token![>](span),
}),
paren_token: token::Paren(span),
args: Punctuated::new(),
}));
});
// Expr::RawAddr
iter(depth, &mut |expr| {
f(Expr::RawAddr(ExprRawAddr {
// `&raw const $expr`
attrs: Vec::new(),
and_token: Token![&](span),
raw: Token![raw](span),
mutability: PointerMutability::Const(Token![const](span)),
expr: Box::new(expr),
}));
});
// Expr::Struct
f(Expr::Struct(ExprStruct {
// `Struct {}`
attrs: Vec::new(),
qself: None,
path: Path::from(Ident::new("Struct", span)),
brace_token: token::Brace(span),
fields: Punctuated::new(),
dot2_token: None,
rest: None,
}));
// Expr::TryBlock
f(Expr::TryBlock(ExprTryBlock {
// `try {}`
attrs: Vec::new(),
try_token: Token![try](span),
block: Block {
brace_token: token::Brace(span),
stmts: Vec::new(),
},
}));
// Expr::Unsafe
f(Expr::Unsafe(ExprUnsafe {
// `unsafe {}`
attrs: Vec::new(),
unsafe_token: Token![unsafe](span),
block: Block {
brace_token: token::Brace(span),
stmts: Vec::new(),
},
}));
// Expr::While
iter(depth, &mut |expr| {
f(Expr::While(ExprWhile {
// `while $expr {}`
attrs: Vec::new(),
label: None,
while_token: Token![while](span),
cond: Box::new(expr.clone()),
body: Block {
brace_token: token::Brace(span),
stmts: Vec::new(),
},
}));
f(Expr::While(ExprWhile {
// `'a: while $expr {}`
attrs: Vec::new(),
label: Some(Label {
name: Lifetime::new("'a", span),
colon_token: Token![:](span),
}),
while_token: Token![while](span),
cond: Box::new(expr),
body: Block {
brace_token: token::Brace(span),
stmts: Vec::new(),
},
}));
});
// Expr::Yield
f(Expr::Yield(ExprYield {
// `yield`
attrs: Vec::new(),
yield_token: Token![yield](span),
expr: None,
}));
iter(depth, &mut |expr| {
f(Expr::Yield(ExprYield {
// `yield $expr`
attrs: Vec::new(),
yield_token: Token![yield](span),
expr: Some(Box::new(expr)),
}));
});
}
}
let mut failures = 0;
macro_rules! fail {
($($message:tt)*) => {{
eprintln!($($message)*);
failures += 1;
return;
}};
}
let mut assert = |mut original: Expr| {
let span = Span::call_site();
// `const _: () = $expr;`
let pretty = prettyplease::unparse(&File {
shebang: None,
attrs: Vec::new(),
items: Vec::from([Item::Const(ItemConst {
attrs: Vec::new(),
vis: Visibility::Inherited,
const_token: Token![const](span),
ident: Ident::from(Token![_](span)),
generics: Generics::default(),
colon_token: Token![:](span),
ty: Box::new(Type::Infer(TypeInfer {
underscore_token: Token![_](span),
})),
eq_token: Token![=](span),
expr: Box::new(original.clone()),
semi_token: Token![;](span),
})]),
});
let mut parsed = match syn::parse_file(&pretty) {
Ok(parsed) => parsed,
_ => fail!("failed to parse: {pretty}{original:#?}"),
};
let item = match parsed.items.as_mut_slice() {
[Item::Const(item)] => item,
_ => unreachable!(),
};
let mut parsed = mem::replace(&mut *item.expr, Expr::PLACEHOLDER);
AsIfPrinted.visit_expr_mut(&mut original);
FlattenParens.visit_expr_mut(&mut parsed);
if original != parsed {
fail!(
"before: {}\n{:#?}\nafter: {}\n{:#?}",
original.to_token_stream(),
original,
parsed.to_token_stream(),
parsed,
);
}
if pretty.contains("(||") {
// https://github.com/dtolnay/prettyplease/issues/99
return;
}
let no_paren = pretty.replace(['(', ')'], "");
if pretty != no_paren {
if let Ok(mut parsed2) = syn::parse_file(&no_paren) {
let item = match parsed2.items.as_mut_slice() {
[Item::Const(item)] => item,
_ => unreachable!(),
};
if original == *item.expr {
fail!("redundant parens: {}", pretty);
}
}
}
};
iter(if cfg!(debug_assertions) { 3 } else { 4 }, &mut assert);
if failures > 0 {
eprintln!("FAILURES: {failures}");
ExitCode::FAILURE
} else {
ExitCode::SUCCESS
}
}