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