1fad3a1d3Sopenharmony_ci//! This test does the following for every file in the rust-lang/rust repo: 2fad3a1d3Sopenharmony_ci//! 3fad3a1d3Sopenharmony_ci//! 1. Parse the file using syn into a syn::File. 4fad3a1d3Sopenharmony_ci//! 2. Extract every syn::Expr from the file. 5fad3a1d3Sopenharmony_ci//! 3. Print each expr to a string of source code. 6fad3a1d3Sopenharmony_ci//! 4. Parse the source code using librustc_parse into a rustc_ast::Expr. 7fad3a1d3Sopenharmony_ci//! 5. For both the syn::Expr and rustc_ast::Expr, crawl the syntax tree to 8fad3a1d3Sopenharmony_ci//! insert parentheses surrounding every subexpression. 9fad3a1d3Sopenharmony_ci//! 6. Serialize the fully parenthesized syn::Expr to a string of source code. 10fad3a1d3Sopenharmony_ci//! 7. Parse the fully parenthesized source code using librustc_parse. 11fad3a1d3Sopenharmony_ci//! 8. Compare the rustc_ast::Expr resulting from parenthesizing using rustc 12fad3a1d3Sopenharmony_ci//! data structures vs syn data structures, ignoring spans. If they agree, 13fad3a1d3Sopenharmony_ci//! rustc's parser and syn's parser have identical handling of expression 14fad3a1d3Sopenharmony_ci//! precedence. 15fad3a1d3Sopenharmony_ci 16fad3a1d3Sopenharmony_ci#![cfg(not(syn_disable_nightly_tests))] 17fad3a1d3Sopenharmony_ci#![cfg(not(miri))] 18fad3a1d3Sopenharmony_ci#![recursion_limit = "1024"] 19fad3a1d3Sopenharmony_ci#![feature(rustc_private)] 20fad3a1d3Sopenharmony_ci#![allow( 21fad3a1d3Sopenharmony_ci clippy::blocks_in_conditions, 22fad3a1d3Sopenharmony_ci clippy::doc_markdown, 23fad3a1d3Sopenharmony_ci clippy::explicit_deref_methods, 24fad3a1d3Sopenharmony_ci clippy::let_underscore_untyped, 25fad3a1d3Sopenharmony_ci clippy::manual_assert, 26fad3a1d3Sopenharmony_ci clippy::manual_let_else, 27fad3a1d3Sopenharmony_ci clippy::match_like_matches_macro, 28fad3a1d3Sopenharmony_ci clippy::match_wildcard_for_single_variants, 29fad3a1d3Sopenharmony_ci clippy::too_many_lines, 30fad3a1d3Sopenharmony_ci clippy::uninlined_format_args 31fad3a1d3Sopenharmony_ci)] 32fad3a1d3Sopenharmony_ci 33fad3a1d3Sopenharmony_ciextern crate rustc_ast; 34fad3a1d3Sopenharmony_ciextern crate rustc_ast_pretty; 35fad3a1d3Sopenharmony_ciextern crate rustc_data_structures; 36fad3a1d3Sopenharmony_ciextern crate rustc_driver; 37fad3a1d3Sopenharmony_ciextern crate rustc_span; 38fad3a1d3Sopenharmony_ciextern crate smallvec; 39fad3a1d3Sopenharmony_ciextern crate thin_vec; 40fad3a1d3Sopenharmony_ci 41fad3a1d3Sopenharmony_ciuse crate::common::eq::SpanlessEq; 42fad3a1d3Sopenharmony_ciuse crate::common::parse; 43fad3a1d3Sopenharmony_ciuse quote::ToTokens; 44fad3a1d3Sopenharmony_ciuse rustc_ast::ast; 45fad3a1d3Sopenharmony_ciuse rustc_ast::ptr::P; 46fad3a1d3Sopenharmony_ciuse rustc_ast_pretty::pprust; 47fad3a1d3Sopenharmony_ciuse rustc_span::edition::Edition; 48fad3a1d3Sopenharmony_ciuse std::fs; 49fad3a1d3Sopenharmony_ciuse std::path::Path; 50fad3a1d3Sopenharmony_ciuse std::process; 51fad3a1d3Sopenharmony_ciuse std::sync::atomic::{AtomicUsize, Ordering}; 52fad3a1d3Sopenharmony_ci 53fad3a1d3Sopenharmony_ci#[macro_use] 54fad3a1d3Sopenharmony_cimod macros; 55fad3a1d3Sopenharmony_ci 56fad3a1d3Sopenharmony_ci#[allow(dead_code)] 57fad3a1d3Sopenharmony_cimod common; 58fad3a1d3Sopenharmony_ci 59fad3a1d3Sopenharmony_cimod repo; 60fad3a1d3Sopenharmony_ci 61fad3a1d3Sopenharmony_ci#[test] 62fad3a1d3Sopenharmony_cifn test_rustc_precedence() { 63fad3a1d3Sopenharmony_ci common::rayon_init(); 64fad3a1d3Sopenharmony_ci repo::clone_rust(); 65fad3a1d3Sopenharmony_ci let abort_after = common::abort_after(); 66fad3a1d3Sopenharmony_ci if abort_after == 0 { 67fad3a1d3Sopenharmony_ci panic!("Skipping all precedence tests"); 68fad3a1d3Sopenharmony_ci } 69fad3a1d3Sopenharmony_ci 70fad3a1d3Sopenharmony_ci let passed = AtomicUsize::new(0); 71fad3a1d3Sopenharmony_ci let failed = AtomicUsize::new(0); 72fad3a1d3Sopenharmony_ci 73fad3a1d3Sopenharmony_ci repo::for_each_rust_file(|path| { 74fad3a1d3Sopenharmony_ci let content = fs::read_to_string(path).unwrap(); 75fad3a1d3Sopenharmony_ci 76fad3a1d3Sopenharmony_ci let (l_passed, l_failed) = match syn::parse_file(&content) { 77fad3a1d3Sopenharmony_ci Ok(file) => { 78fad3a1d3Sopenharmony_ci let edition = repo::edition(path).parse().unwrap(); 79fad3a1d3Sopenharmony_ci let exprs = collect_exprs(file); 80fad3a1d3Sopenharmony_ci let (l_passed, l_failed) = test_expressions(path, edition, exprs); 81fad3a1d3Sopenharmony_ci errorf!( 82fad3a1d3Sopenharmony_ci "=== {}: {} passed | {} failed\n", 83fad3a1d3Sopenharmony_ci path.display(), 84fad3a1d3Sopenharmony_ci l_passed, 85fad3a1d3Sopenharmony_ci l_failed, 86fad3a1d3Sopenharmony_ci ); 87fad3a1d3Sopenharmony_ci (l_passed, l_failed) 88fad3a1d3Sopenharmony_ci } 89fad3a1d3Sopenharmony_ci Err(msg) => { 90fad3a1d3Sopenharmony_ci errorf!("\nFAIL {} - syn failed to parse: {}\n", path.display(), msg); 91fad3a1d3Sopenharmony_ci (0, 1) 92fad3a1d3Sopenharmony_ci } 93fad3a1d3Sopenharmony_ci }; 94fad3a1d3Sopenharmony_ci 95fad3a1d3Sopenharmony_ci passed.fetch_add(l_passed, Ordering::Relaxed); 96fad3a1d3Sopenharmony_ci let prev_failed = failed.fetch_add(l_failed, Ordering::Relaxed); 97fad3a1d3Sopenharmony_ci 98fad3a1d3Sopenharmony_ci if prev_failed + l_failed >= abort_after { 99fad3a1d3Sopenharmony_ci process::exit(1); 100fad3a1d3Sopenharmony_ci } 101fad3a1d3Sopenharmony_ci }); 102fad3a1d3Sopenharmony_ci 103fad3a1d3Sopenharmony_ci let passed = passed.load(Ordering::Relaxed); 104fad3a1d3Sopenharmony_ci let failed = failed.load(Ordering::Relaxed); 105fad3a1d3Sopenharmony_ci 106fad3a1d3Sopenharmony_ci errorf!("\n===== Precedence Test Results =====\n"); 107fad3a1d3Sopenharmony_ci errorf!("{} passed | {} failed\n", passed, failed); 108fad3a1d3Sopenharmony_ci 109fad3a1d3Sopenharmony_ci if failed > 0 { 110fad3a1d3Sopenharmony_ci panic!("{} failures", failed); 111fad3a1d3Sopenharmony_ci } 112fad3a1d3Sopenharmony_ci} 113fad3a1d3Sopenharmony_ci 114fad3a1d3Sopenharmony_cifn test_expressions(path: &Path, edition: Edition, exprs: Vec<syn::Expr>) -> (usize, usize) { 115fad3a1d3Sopenharmony_ci let mut passed = 0; 116fad3a1d3Sopenharmony_ci let mut failed = 0; 117fad3a1d3Sopenharmony_ci 118fad3a1d3Sopenharmony_ci rustc_span::create_session_if_not_set_then(edition, |_| { 119fad3a1d3Sopenharmony_ci for expr in exprs { 120fad3a1d3Sopenharmony_ci let source_code = expr.to_token_stream().to_string(); 121fad3a1d3Sopenharmony_ci let librustc_ast = if let Some(e) = librustc_parse_and_rewrite(&source_code) { 122fad3a1d3Sopenharmony_ci e 123fad3a1d3Sopenharmony_ci } else { 124fad3a1d3Sopenharmony_ci failed += 1; 125fad3a1d3Sopenharmony_ci errorf!( 126fad3a1d3Sopenharmony_ci "\nFAIL {} - librustc failed to parse original\n", 127fad3a1d3Sopenharmony_ci path.display(), 128fad3a1d3Sopenharmony_ci ); 129fad3a1d3Sopenharmony_ci continue; 130fad3a1d3Sopenharmony_ci }; 131fad3a1d3Sopenharmony_ci 132fad3a1d3Sopenharmony_ci let syn_parenthesized_code = 133fad3a1d3Sopenharmony_ci syn_parenthesize(expr.clone()).to_token_stream().to_string(); 134fad3a1d3Sopenharmony_ci let syn_ast = if let Some(e) = parse::librustc_expr(&syn_parenthesized_code) { 135fad3a1d3Sopenharmony_ci e 136fad3a1d3Sopenharmony_ci } else { 137fad3a1d3Sopenharmony_ci failed += 1; 138fad3a1d3Sopenharmony_ci errorf!( 139fad3a1d3Sopenharmony_ci "\nFAIL {} - librustc failed to parse parenthesized\n", 140fad3a1d3Sopenharmony_ci path.display(), 141fad3a1d3Sopenharmony_ci ); 142fad3a1d3Sopenharmony_ci continue; 143fad3a1d3Sopenharmony_ci }; 144fad3a1d3Sopenharmony_ci 145fad3a1d3Sopenharmony_ci if !SpanlessEq::eq(&syn_ast, &librustc_ast) { 146fad3a1d3Sopenharmony_ci failed += 1; 147fad3a1d3Sopenharmony_ci let syn_pretty = pprust::expr_to_string(&syn_ast); 148fad3a1d3Sopenharmony_ci let librustc_pretty = pprust::expr_to_string(&librustc_ast); 149fad3a1d3Sopenharmony_ci errorf!( 150fad3a1d3Sopenharmony_ci "\nFAIL {}\n{}\nsyn != rustc\n{}\n", 151fad3a1d3Sopenharmony_ci path.display(), 152fad3a1d3Sopenharmony_ci syn_pretty, 153fad3a1d3Sopenharmony_ci librustc_pretty, 154fad3a1d3Sopenharmony_ci ); 155fad3a1d3Sopenharmony_ci continue; 156fad3a1d3Sopenharmony_ci } 157fad3a1d3Sopenharmony_ci 158fad3a1d3Sopenharmony_ci let expr_invisible = make_parens_invisible(expr); 159fad3a1d3Sopenharmony_ci let Ok(reparsed_expr_invisible) = syn::parse2(expr_invisible.to_token_stream()) else { 160fad3a1d3Sopenharmony_ci failed += 1; 161fad3a1d3Sopenharmony_ci errorf!( 162fad3a1d3Sopenharmony_ci "\nFAIL {} - syn failed to parse invisible delimiters\n{}\n", 163fad3a1d3Sopenharmony_ci path.display(), 164fad3a1d3Sopenharmony_ci source_code, 165fad3a1d3Sopenharmony_ci ); 166fad3a1d3Sopenharmony_ci continue; 167fad3a1d3Sopenharmony_ci }; 168fad3a1d3Sopenharmony_ci if expr_invisible != reparsed_expr_invisible { 169fad3a1d3Sopenharmony_ci failed += 1; 170fad3a1d3Sopenharmony_ci errorf!( 171fad3a1d3Sopenharmony_ci "\nFAIL {} - mismatch after parsing invisible delimiters\n{}\n", 172fad3a1d3Sopenharmony_ci path.display(), 173fad3a1d3Sopenharmony_ci source_code, 174fad3a1d3Sopenharmony_ci ); 175fad3a1d3Sopenharmony_ci continue; 176fad3a1d3Sopenharmony_ci } 177fad3a1d3Sopenharmony_ci 178fad3a1d3Sopenharmony_ci passed += 1; 179fad3a1d3Sopenharmony_ci } 180fad3a1d3Sopenharmony_ci }); 181fad3a1d3Sopenharmony_ci 182fad3a1d3Sopenharmony_ci (passed, failed) 183fad3a1d3Sopenharmony_ci} 184fad3a1d3Sopenharmony_ci 185fad3a1d3Sopenharmony_cifn librustc_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> { 186fad3a1d3Sopenharmony_ci parse::librustc_expr(input).map(librustc_parenthesize) 187fad3a1d3Sopenharmony_ci} 188fad3a1d3Sopenharmony_ci 189fad3a1d3Sopenharmony_cifn librustc_parenthesize(mut librustc_expr: P<ast::Expr>) -> P<ast::Expr> { 190fad3a1d3Sopenharmony_ci use rustc_ast::ast::{ 191fad3a1d3Sopenharmony_ci AssocItem, AssocItemKind, Attribute, BinOpKind, Block, BorrowKind, BoundConstness, Expr, 192fad3a1d3Sopenharmony_ci ExprField, ExprKind, GenericArg, GenericBound, ItemKind, Local, LocalKind, Pat, Stmt, 193fad3a1d3Sopenharmony_ci StmtKind, StructExpr, StructRest, TraitBoundModifiers, Ty, 194fad3a1d3Sopenharmony_ci }; 195fad3a1d3Sopenharmony_ci use rustc_ast::mut_visit::{ 196fad3a1d3Sopenharmony_ci noop_flat_map_assoc_item, noop_visit_generic_arg, noop_visit_item_kind, noop_visit_local, 197fad3a1d3Sopenharmony_ci noop_visit_param_bound, MutVisitor, 198fad3a1d3Sopenharmony_ci }; 199fad3a1d3Sopenharmony_ci use rustc_data_structures::flat_map_in_place::FlatMapInPlace; 200fad3a1d3Sopenharmony_ci use rustc_span::DUMMY_SP; 201fad3a1d3Sopenharmony_ci use smallvec::SmallVec; 202fad3a1d3Sopenharmony_ci use std::mem; 203fad3a1d3Sopenharmony_ci use std::ops::DerefMut; 204fad3a1d3Sopenharmony_ci use thin_vec::ThinVec; 205fad3a1d3Sopenharmony_ci 206fad3a1d3Sopenharmony_ci struct FullyParenthesize; 207fad3a1d3Sopenharmony_ci 208fad3a1d3Sopenharmony_ci fn contains_let_chain(expr: &Expr) -> bool { 209fad3a1d3Sopenharmony_ci match &expr.kind { 210fad3a1d3Sopenharmony_ci ExprKind::Let(..) => true, 211fad3a1d3Sopenharmony_ci ExprKind::Binary(binop, left, right) => { 212fad3a1d3Sopenharmony_ci binop.node == BinOpKind::And 213fad3a1d3Sopenharmony_ci && (contains_let_chain(left) || contains_let_chain(right)) 214fad3a1d3Sopenharmony_ci } 215fad3a1d3Sopenharmony_ci _ => false, 216fad3a1d3Sopenharmony_ci } 217fad3a1d3Sopenharmony_ci } 218fad3a1d3Sopenharmony_ci 219fad3a1d3Sopenharmony_ci fn flat_map_field<T: MutVisitor>(mut f: ExprField, vis: &mut T) -> Vec<ExprField> { 220fad3a1d3Sopenharmony_ci if f.is_shorthand { 221fad3a1d3Sopenharmony_ci noop_visit_expr(&mut f.expr, vis); 222fad3a1d3Sopenharmony_ci } else { 223fad3a1d3Sopenharmony_ci vis.visit_expr(&mut f.expr); 224fad3a1d3Sopenharmony_ci } 225fad3a1d3Sopenharmony_ci vec![f] 226fad3a1d3Sopenharmony_ci } 227fad3a1d3Sopenharmony_ci 228fad3a1d3Sopenharmony_ci fn flat_map_stmt<T: MutVisitor>(stmt: Stmt, vis: &mut T) -> Vec<Stmt> { 229fad3a1d3Sopenharmony_ci let kind = match stmt.kind { 230fad3a1d3Sopenharmony_ci // Don't wrap toplevel expressions in statements. 231fad3a1d3Sopenharmony_ci StmtKind::Expr(mut e) => { 232fad3a1d3Sopenharmony_ci noop_visit_expr(&mut e, vis); 233fad3a1d3Sopenharmony_ci StmtKind::Expr(e) 234fad3a1d3Sopenharmony_ci } 235fad3a1d3Sopenharmony_ci StmtKind::Semi(mut e) => { 236fad3a1d3Sopenharmony_ci noop_visit_expr(&mut e, vis); 237fad3a1d3Sopenharmony_ci StmtKind::Semi(e) 238fad3a1d3Sopenharmony_ci } 239fad3a1d3Sopenharmony_ci s => s, 240fad3a1d3Sopenharmony_ci }; 241fad3a1d3Sopenharmony_ci 242fad3a1d3Sopenharmony_ci vec![Stmt { kind, ..stmt }] 243fad3a1d3Sopenharmony_ci } 244fad3a1d3Sopenharmony_ci 245fad3a1d3Sopenharmony_ci fn noop_visit_expr<T: MutVisitor>(e: &mut Expr, vis: &mut T) { 246fad3a1d3Sopenharmony_ci use rustc_ast::mut_visit::{noop_visit_expr, visit_attrs}; 247fad3a1d3Sopenharmony_ci match &mut e.kind { 248fad3a1d3Sopenharmony_ci ExprKind::AddrOf(BorrowKind::Raw, ..) => {} 249fad3a1d3Sopenharmony_ci ExprKind::Struct(expr) => { 250fad3a1d3Sopenharmony_ci let StructExpr { 251fad3a1d3Sopenharmony_ci qself, 252fad3a1d3Sopenharmony_ci path, 253fad3a1d3Sopenharmony_ci fields, 254fad3a1d3Sopenharmony_ci rest, 255fad3a1d3Sopenharmony_ci } = expr.deref_mut(); 256fad3a1d3Sopenharmony_ci vis.visit_qself(qself); 257fad3a1d3Sopenharmony_ci vis.visit_path(path); 258fad3a1d3Sopenharmony_ci fields.flat_map_in_place(|field| flat_map_field(field, vis)); 259fad3a1d3Sopenharmony_ci if let StructRest::Base(rest) = rest { 260fad3a1d3Sopenharmony_ci vis.visit_expr(rest); 261fad3a1d3Sopenharmony_ci } 262fad3a1d3Sopenharmony_ci vis.visit_id(&mut e.id); 263fad3a1d3Sopenharmony_ci vis.visit_span(&mut e.span); 264fad3a1d3Sopenharmony_ci visit_attrs(&mut e.attrs, vis); 265fad3a1d3Sopenharmony_ci } 266fad3a1d3Sopenharmony_ci _ => noop_visit_expr(e, vis), 267fad3a1d3Sopenharmony_ci } 268fad3a1d3Sopenharmony_ci } 269fad3a1d3Sopenharmony_ci 270fad3a1d3Sopenharmony_ci impl MutVisitor for FullyParenthesize { 271fad3a1d3Sopenharmony_ci fn visit_expr(&mut self, e: &mut P<Expr>) { 272fad3a1d3Sopenharmony_ci noop_visit_expr(e, self); 273fad3a1d3Sopenharmony_ci match e.kind { 274fad3a1d3Sopenharmony_ci ExprKind::Block(..) | ExprKind::If(..) | ExprKind::Let(..) => {} 275fad3a1d3Sopenharmony_ci ExprKind::Binary(..) if contains_let_chain(e) => {} 276fad3a1d3Sopenharmony_ci _ => { 277fad3a1d3Sopenharmony_ci let inner = mem::replace( 278fad3a1d3Sopenharmony_ci e, 279fad3a1d3Sopenharmony_ci P(Expr { 280fad3a1d3Sopenharmony_ci id: ast::DUMMY_NODE_ID, 281fad3a1d3Sopenharmony_ci kind: ExprKind::Err, 282fad3a1d3Sopenharmony_ci span: DUMMY_SP, 283fad3a1d3Sopenharmony_ci attrs: ThinVec::new(), 284fad3a1d3Sopenharmony_ci tokens: None, 285fad3a1d3Sopenharmony_ci }), 286fad3a1d3Sopenharmony_ci ); 287fad3a1d3Sopenharmony_ci e.kind = ExprKind::Paren(inner); 288fad3a1d3Sopenharmony_ci } 289fad3a1d3Sopenharmony_ci } 290fad3a1d3Sopenharmony_ci } 291fad3a1d3Sopenharmony_ci 292fad3a1d3Sopenharmony_ci fn visit_generic_arg(&mut self, arg: &mut GenericArg) { 293fad3a1d3Sopenharmony_ci match arg { 294fad3a1d3Sopenharmony_ci // Don't wrap unbraced const generic arg as that's invalid syntax. 295fad3a1d3Sopenharmony_ci GenericArg::Const(anon_const) => { 296fad3a1d3Sopenharmony_ci if let ExprKind::Block(..) = &mut anon_const.value.kind { 297fad3a1d3Sopenharmony_ci noop_visit_expr(&mut anon_const.value, self); 298fad3a1d3Sopenharmony_ci } 299fad3a1d3Sopenharmony_ci } 300fad3a1d3Sopenharmony_ci _ => noop_visit_generic_arg(arg, self), 301fad3a1d3Sopenharmony_ci } 302fad3a1d3Sopenharmony_ci } 303fad3a1d3Sopenharmony_ci 304fad3a1d3Sopenharmony_ci fn visit_param_bound(&mut self, bound: &mut GenericBound) { 305fad3a1d3Sopenharmony_ci match bound { 306fad3a1d3Sopenharmony_ci GenericBound::Trait( 307fad3a1d3Sopenharmony_ci _, 308fad3a1d3Sopenharmony_ci TraitBoundModifiers { 309fad3a1d3Sopenharmony_ci constness: BoundConstness::Maybe(_), 310fad3a1d3Sopenharmony_ci .. 311fad3a1d3Sopenharmony_ci }, 312fad3a1d3Sopenharmony_ci ) => {} 313fad3a1d3Sopenharmony_ci _ => noop_visit_param_bound(bound, self), 314fad3a1d3Sopenharmony_ci } 315fad3a1d3Sopenharmony_ci } 316fad3a1d3Sopenharmony_ci 317fad3a1d3Sopenharmony_ci fn visit_block(&mut self, block: &mut P<Block>) { 318fad3a1d3Sopenharmony_ci self.visit_id(&mut block.id); 319fad3a1d3Sopenharmony_ci block 320fad3a1d3Sopenharmony_ci .stmts 321fad3a1d3Sopenharmony_ci .flat_map_in_place(|stmt| flat_map_stmt(stmt, self)); 322fad3a1d3Sopenharmony_ci self.visit_span(&mut block.span); 323fad3a1d3Sopenharmony_ci } 324fad3a1d3Sopenharmony_ci 325fad3a1d3Sopenharmony_ci fn visit_local(&mut self, local: &mut P<Local>) { 326fad3a1d3Sopenharmony_ci match local.kind { 327fad3a1d3Sopenharmony_ci LocalKind::InitElse(..) => {} 328fad3a1d3Sopenharmony_ci _ => noop_visit_local(local, self), 329fad3a1d3Sopenharmony_ci } 330fad3a1d3Sopenharmony_ci } 331fad3a1d3Sopenharmony_ci 332fad3a1d3Sopenharmony_ci fn visit_item_kind(&mut self, item: &mut ItemKind) { 333fad3a1d3Sopenharmony_ci match item { 334fad3a1d3Sopenharmony_ci ItemKind::Const(const_item) 335fad3a1d3Sopenharmony_ci if !const_item.generics.params.is_empty() 336fad3a1d3Sopenharmony_ci || !const_item.generics.where_clause.predicates.is_empty() => {} 337fad3a1d3Sopenharmony_ci _ => noop_visit_item_kind(item, self), 338fad3a1d3Sopenharmony_ci } 339fad3a1d3Sopenharmony_ci } 340fad3a1d3Sopenharmony_ci 341fad3a1d3Sopenharmony_ci fn flat_map_trait_item(&mut self, item: P<AssocItem>) -> SmallVec<[P<AssocItem>; 1]> { 342fad3a1d3Sopenharmony_ci match &item.kind { 343fad3a1d3Sopenharmony_ci AssocItemKind::Const(const_item) 344fad3a1d3Sopenharmony_ci if !const_item.generics.params.is_empty() 345fad3a1d3Sopenharmony_ci || !const_item.generics.where_clause.predicates.is_empty() => 346fad3a1d3Sopenharmony_ci { 347fad3a1d3Sopenharmony_ci SmallVec::from([item]) 348fad3a1d3Sopenharmony_ci } 349fad3a1d3Sopenharmony_ci _ => noop_flat_map_assoc_item(item, self), 350fad3a1d3Sopenharmony_ci } 351fad3a1d3Sopenharmony_ci } 352fad3a1d3Sopenharmony_ci 353fad3a1d3Sopenharmony_ci fn flat_map_impl_item(&mut self, item: P<AssocItem>) -> SmallVec<[P<AssocItem>; 1]> { 354fad3a1d3Sopenharmony_ci match &item.kind { 355fad3a1d3Sopenharmony_ci AssocItemKind::Const(const_item) 356fad3a1d3Sopenharmony_ci if !const_item.generics.params.is_empty() 357fad3a1d3Sopenharmony_ci || !const_item.generics.where_clause.predicates.is_empty() => 358fad3a1d3Sopenharmony_ci { 359fad3a1d3Sopenharmony_ci SmallVec::from([item]) 360fad3a1d3Sopenharmony_ci } 361fad3a1d3Sopenharmony_ci _ => noop_flat_map_assoc_item(item, self), 362fad3a1d3Sopenharmony_ci } 363fad3a1d3Sopenharmony_ci } 364fad3a1d3Sopenharmony_ci 365fad3a1d3Sopenharmony_ci // We don't want to look at expressions that might appear in patterns or 366fad3a1d3Sopenharmony_ci // types yet. We'll look into comparing those in the future. For now 367fad3a1d3Sopenharmony_ci // focus on expressions appearing in other places. 368fad3a1d3Sopenharmony_ci fn visit_pat(&mut self, pat: &mut P<Pat>) { 369fad3a1d3Sopenharmony_ci let _ = pat; 370fad3a1d3Sopenharmony_ci } 371fad3a1d3Sopenharmony_ci 372fad3a1d3Sopenharmony_ci fn visit_ty(&mut self, ty: &mut P<Ty>) { 373fad3a1d3Sopenharmony_ci let _ = ty; 374fad3a1d3Sopenharmony_ci } 375fad3a1d3Sopenharmony_ci 376fad3a1d3Sopenharmony_ci fn visit_attribute(&mut self, attr: &mut Attribute) { 377fad3a1d3Sopenharmony_ci let _ = attr; 378fad3a1d3Sopenharmony_ci } 379fad3a1d3Sopenharmony_ci } 380fad3a1d3Sopenharmony_ci 381fad3a1d3Sopenharmony_ci let mut folder = FullyParenthesize; 382fad3a1d3Sopenharmony_ci folder.visit_expr(&mut librustc_expr); 383fad3a1d3Sopenharmony_ci librustc_expr 384fad3a1d3Sopenharmony_ci} 385fad3a1d3Sopenharmony_ci 386fad3a1d3Sopenharmony_cifn syn_parenthesize(syn_expr: syn::Expr) -> syn::Expr { 387fad3a1d3Sopenharmony_ci use syn::fold::{fold_expr, fold_generic_argument, Fold}; 388fad3a1d3Sopenharmony_ci use syn::{token, BinOp, Expr, ExprParen, GenericArgument, MetaNameValue, Pat, Stmt, Type}; 389fad3a1d3Sopenharmony_ci 390fad3a1d3Sopenharmony_ci struct FullyParenthesize; 391fad3a1d3Sopenharmony_ci 392fad3a1d3Sopenharmony_ci fn parenthesize(expr: Expr) -> Expr { 393fad3a1d3Sopenharmony_ci Expr::Paren(ExprParen { 394fad3a1d3Sopenharmony_ci attrs: Vec::new(), 395fad3a1d3Sopenharmony_ci expr: Box::new(expr), 396fad3a1d3Sopenharmony_ci paren_token: token::Paren::default(), 397fad3a1d3Sopenharmony_ci }) 398fad3a1d3Sopenharmony_ci } 399fad3a1d3Sopenharmony_ci 400fad3a1d3Sopenharmony_ci fn needs_paren(expr: &Expr) -> bool { 401fad3a1d3Sopenharmony_ci match expr { 402fad3a1d3Sopenharmony_ci Expr::Group(_) => unreachable!(), 403fad3a1d3Sopenharmony_ci Expr::If(_) | Expr::Unsafe(_) | Expr::Block(_) | Expr::Let(_) => false, 404fad3a1d3Sopenharmony_ci Expr::Binary(_) => !contains_let_chain(expr), 405fad3a1d3Sopenharmony_ci _ => true, 406fad3a1d3Sopenharmony_ci } 407fad3a1d3Sopenharmony_ci } 408fad3a1d3Sopenharmony_ci 409fad3a1d3Sopenharmony_ci fn contains_let_chain(expr: &Expr) -> bool { 410fad3a1d3Sopenharmony_ci match expr { 411fad3a1d3Sopenharmony_ci Expr::Let(_) => true, 412fad3a1d3Sopenharmony_ci Expr::Binary(expr) => { 413fad3a1d3Sopenharmony_ci matches!(expr.op, BinOp::And(_)) 414fad3a1d3Sopenharmony_ci && (contains_let_chain(&expr.left) || contains_let_chain(&expr.right)) 415fad3a1d3Sopenharmony_ci } 416fad3a1d3Sopenharmony_ci _ => false, 417fad3a1d3Sopenharmony_ci } 418fad3a1d3Sopenharmony_ci } 419fad3a1d3Sopenharmony_ci 420fad3a1d3Sopenharmony_ci impl Fold for FullyParenthesize { 421fad3a1d3Sopenharmony_ci fn fold_expr(&mut self, expr: Expr) -> Expr { 422fad3a1d3Sopenharmony_ci let needs_paren = needs_paren(&expr); 423fad3a1d3Sopenharmony_ci let folded = fold_expr(self, expr); 424fad3a1d3Sopenharmony_ci if needs_paren { 425fad3a1d3Sopenharmony_ci parenthesize(folded) 426fad3a1d3Sopenharmony_ci } else { 427fad3a1d3Sopenharmony_ci folded 428fad3a1d3Sopenharmony_ci } 429fad3a1d3Sopenharmony_ci } 430fad3a1d3Sopenharmony_ci 431fad3a1d3Sopenharmony_ci fn fold_generic_argument(&mut self, arg: GenericArgument) -> GenericArgument { 432fad3a1d3Sopenharmony_ci match arg { 433fad3a1d3Sopenharmony_ci GenericArgument::Const(arg) => GenericArgument::Const(match arg { 434fad3a1d3Sopenharmony_ci Expr::Block(_) => fold_expr(self, arg), 435fad3a1d3Sopenharmony_ci // Don't wrap unbraced const generic arg as that's invalid syntax. 436fad3a1d3Sopenharmony_ci _ => arg, 437fad3a1d3Sopenharmony_ci }), 438fad3a1d3Sopenharmony_ci _ => fold_generic_argument(self, arg), 439fad3a1d3Sopenharmony_ci } 440fad3a1d3Sopenharmony_ci } 441fad3a1d3Sopenharmony_ci 442fad3a1d3Sopenharmony_ci fn fold_stmt(&mut self, stmt: Stmt) -> Stmt { 443fad3a1d3Sopenharmony_ci match stmt { 444fad3a1d3Sopenharmony_ci // Don't wrap toplevel expressions in statements. 445fad3a1d3Sopenharmony_ci Stmt::Expr(Expr::Verbatim(_), Some(_)) => stmt, 446fad3a1d3Sopenharmony_ci Stmt::Expr(e, semi) => Stmt::Expr(fold_expr(self, e), semi), 447fad3a1d3Sopenharmony_ci s => s, 448fad3a1d3Sopenharmony_ci } 449fad3a1d3Sopenharmony_ci } 450fad3a1d3Sopenharmony_ci 451fad3a1d3Sopenharmony_ci fn fold_meta_name_value(&mut self, meta: MetaNameValue) -> MetaNameValue { 452fad3a1d3Sopenharmony_ci // Don't turn #[p = "..."] into #[p = ("...")]. 453fad3a1d3Sopenharmony_ci meta 454fad3a1d3Sopenharmony_ci } 455fad3a1d3Sopenharmony_ci 456fad3a1d3Sopenharmony_ci // We don't want to look at expressions that might appear in patterns or 457fad3a1d3Sopenharmony_ci // types yet. We'll look into comparing those in the future. For now 458fad3a1d3Sopenharmony_ci // focus on expressions appearing in other places. 459fad3a1d3Sopenharmony_ci fn fold_pat(&mut self, pat: Pat) -> Pat { 460fad3a1d3Sopenharmony_ci pat 461fad3a1d3Sopenharmony_ci } 462fad3a1d3Sopenharmony_ci 463fad3a1d3Sopenharmony_ci fn fold_type(&mut self, ty: Type) -> Type { 464fad3a1d3Sopenharmony_ci ty 465fad3a1d3Sopenharmony_ci } 466fad3a1d3Sopenharmony_ci } 467fad3a1d3Sopenharmony_ci 468fad3a1d3Sopenharmony_ci let mut folder = FullyParenthesize; 469fad3a1d3Sopenharmony_ci folder.fold_expr(syn_expr) 470fad3a1d3Sopenharmony_ci} 471fad3a1d3Sopenharmony_ci 472fad3a1d3Sopenharmony_cifn make_parens_invisible(expr: syn::Expr) -> syn::Expr { 473fad3a1d3Sopenharmony_ci use syn::fold::{fold_expr, fold_stmt, Fold}; 474fad3a1d3Sopenharmony_ci use syn::{token, Expr, ExprGroup, ExprParen, Stmt}; 475fad3a1d3Sopenharmony_ci 476fad3a1d3Sopenharmony_ci struct MakeParensInvisible; 477fad3a1d3Sopenharmony_ci 478fad3a1d3Sopenharmony_ci impl Fold for MakeParensInvisible { 479fad3a1d3Sopenharmony_ci fn fold_expr(&mut self, mut expr: Expr) -> Expr { 480fad3a1d3Sopenharmony_ci if let Expr::Paren(paren) = expr { 481fad3a1d3Sopenharmony_ci expr = Expr::Group(ExprGroup { 482fad3a1d3Sopenharmony_ci attrs: paren.attrs, 483fad3a1d3Sopenharmony_ci group_token: token::Group(paren.paren_token.span.join()), 484fad3a1d3Sopenharmony_ci expr: paren.expr, 485fad3a1d3Sopenharmony_ci }); 486fad3a1d3Sopenharmony_ci } 487fad3a1d3Sopenharmony_ci fold_expr(self, expr) 488fad3a1d3Sopenharmony_ci } 489fad3a1d3Sopenharmony_ci 490fad3a1d3Sopenharmony_ci fn fold_stmt(&mut self, stmt: Stmt) -> Stmt { 491fad3a1d3Sopenharmony_ci if let Stmt::Expr(expr @ (Expr::Binary(_) | Expr::Cast(_)), None) = stmt { 492fad3a1d3Sopenharmony_ci Stmt::Expr( 493fad3a1d3Sopenharmony_ci Expr::Paren(ExprParen { 494fad3a1d3Sopenharmony_ci attrs: Vec::new(), 495fad3a1d3Sopenharmony_ci paren_token: token::Paren::default(), 496fad3a1d3Sopenharmony_ci expr: Box::new(fold_expr(self, expr)), 497fad3a1d3Sopenharmony_ci }), 498fad3a1d3Sopenharmony_ci None, 499fad3a1d3Sopenharmony_ci ) 500fad3a1d3Sopenharmony_ci } else { 501fad3a1d3Sopenharmony_ci fold_stmt(self, stmt) 502fad3a1d3Sopenharmony_ci } 503fad3a1d3Sopenharmony_ci } 504fad3a1d3Sopenharmony_ci } 505fad3a1d3Sopenharmony_ci 506fad3a1d3Sopenharmony_ci let mut folder = MakeParensInvisible; 507fad3a1d3Sopenharmony_ci folder.fold_expr(expr) 508fad3a1d3Sopenharmony_ci} 509fad3a1d3Sopenharmony_ci 510fad3a1d3Sopenharmony_ci/// Walk through a crate collecting all expressions we can find in it. 511fad3a1d3Sopenharmony_cifn collect_exprs(file: syn::File) -> Vec<syn::Expr> { 512fad3a1d3Sopenharmony_ci use syn::fold::Fold; 513fad3a1d3Sopenharmony_ci use syn::punctuated::Punctuated; 514fad3a1d3Sopenharmony_ci use syn::{token, ConstParam, Expr, ExprTuple, Pat, Path}; 515fad3a1d3Sopenharmony_ci 516fad3a1d3Sopenharmony_ci struct CollectExprs(Vec<Expr>); 517fad3a1d3Sopenharmony_ci impl Fold for CollectExprs { 518fad3a1d3Sopenharmony_ci fn fold_expr(&mut self, expr: Expr) -> Expr { 519fad3a1d3Sopenharmony_ci match expr { 520fad3a1d3Sopenharmony_ci Expr::Verbatim(_) => {} 521fad3a1d3Sopenharmony_ci _ => self.0.push(expr), 522fad3a1d3Sopenharmony_ci } 523fad3a1d3Sopenharmony_ci 524fad3a1d3Sopenharmony_ci Expr::Tuple(ExprTuple { 525fad3a1d3Sopenharmony_ci attrs: vec![], 526fad3a1d3Sopenharmony_ci elems: Punctuated::new(), 527fad3a1d3Sopenharmony_ci paren_token: token::Paren::default(), 528fad3a1d3Sopenharmony_ci }) 529fad3a1d3Sopenharmony_ci } 530fad3a1d3Sopenharmony_ci 531fad3a1d3Sopenharmony_ci fn fold_pat(&mut self, pat: Pat) -> Pat { 532fad3a1d3Sopenharmony_ci pat 533fad3a1d3Sopenharmony_ci } 534fad3a1d3Sopenharmony_ci 535fad3a1d3Sopenharmony_ci fn fold_path(&mut self, path: Path) -> Path { 536fad3a1d3Sopenharmony_ci // Skip traversing into const generic path arguments 537fad3a1d3Sopenharmony_ci path 538fad3a1d3Sopenharmony_ci } 539fad3a1d3Sopenharmony_ci 540fad3a1d3Sopenharmony_ci fn fold_const_param(&mut self, const_param: ConstParam) -> ConstParam { 541fad3a1d3Sopenharmony_ci const_param 542fad3a1d3Sopenharmony_ci } 543fad3a1d3Sopenharmony_ci } 544fad3a1d3Sopenharmony_ci 545fad3a1d3Sopenharmony_ci let mut folder = CollectExprs(vec![]); 546fad3a1d3Sopenharmony_ci folder.fold_file(file); 547fad3a1d3Sopenharmony_ci folder.0 548fad3a1d3Sopenharmony_ci} 549