summaryrefslogtreecommitdiff
path: root/vendor/prettyplease/tests
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-07-02 18:36:06 -0600
committermo khan <mo@mokhan.ca>2025-07-02 18:36:06 -0600
commit8cdfa445d6629ffef4cb84967ff7017654045bc2 (patch)
tree22f0b0907c024c78d26a731e2e1f5219407d8102 /vendor/prettyplease/tests
parent4351c74c7c5f97156bc94d3a8549b9940ac80e3f (diff)
chore: add vendor directory
Diffstat (limited to 'vendor/prettyplease/tests')
-rw-r--r--vendor/prettyplease/tests/test.rs51
-rw-r--r--vendor/prettyplease/tests/test_precedence.rs900
2 files changed, 951 insertions, 0 deletions
diff --git a/vendor/prettyplease/tests/test.rs b/vendor/prettyplease/tests/test.rs
new file mode 100644
index 00000000..aa6b849f
--- /dev/null
+++ b/vendor/prettyplease/tests/test.rs
@@ -0,0 +1,51 @@
+use indoc::indoc;
+use proc_macro2::{Delimiter, Group, TokenStream};
+use quote::quote;
+
+#[track_caller]
+fn test(tokens: TokenStream, expected: &str) {
+ let syntax_tree: syn::File = syn::parse2(tokens).unwrap();
+ let pretty = prettyplease::unparse(&syntax_tree);
+ assert_eq!(pretty, expected);
+}
+
+#[test]
+fn test_parenthesize_cond() {
+ let s = Group::new(Delimiter::None, quote!(Struct {}));
+ test(
+ quote! {
+ fn main() {
+ if #s == #s {}
+ }
+ },
+ indoc! {"
+ fn main() {
+ if (Struct {}) == (Struct {}) {}
+ }
+ "},
+ );
+}
+
+#[test]
+fn test_parenthesize_match_guard() {
+ let expr_struct = Group::new(Delimiter::None, quote!(Struct {}));
+ let expr_binary = Group::new(Delimiter::None, quote!(true && false));
+ test(
+ quote! {
+ fn main() {
+ match () {
+ () if let _ = #expr_struct => {}
+ () if let _ = #expr_binary => {}
+ }
+ }
+ },
+ indoc! {"
+ fn main() {
+ match () {
+ () if let _ = Struct {} => {}
+ () if let _ = (true && false) => {}
+ }
+ }
+ "},
+ );
+}
diff --git a/vendor/prettyplease/tests/test_precedence.rs b/vendor/prettyplease/tests/test_precedence.rs
new file mode 100644
index 00000000..f1eec232
--- /dev/null
+++ b/vendor/prettyplease/tests/test_precedence.rs
@@ -0,0 +1,900 @@
+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
+ }
+}