diff options
author | Robin Krahl <robin.krahl@ireas.org> | 2020-01-07 11:18:04 +0000 |
---|---|---|
committer | Daniel Mueller <deso@posteo.net> | 2020-01-08 09:20:25 -0800 |
commit | 5e20a29b4fdc8a2d442d1093681b396dcb4b816b (patch) | |
tree | 55ab083fa8999d2ccbb5e921c1ffe52560dca152 /syn/tests/test_precedence.rs | |
parent | 203e691f46d591a2cc8acdfd850fa9f5b0fb8a98 (diff) | |
download | nitrocli-5e20a29b4fdc8a2d442d1093681b396dcb4b816b.tar.gz nitrocli-5e20a29b4fdc8a2d442d1093681b396dcb4b816b.tar.bz2 |
Add structopt dependency in version 0.3.7
This patch series replaces argparse with structopt in the argument
handling code. As a first step, we need structopt as a dependency.
Import subrepo structopt/:structopt at efbdda4753592e27bc430fb01f7b9650b2f3174d
Import subrepo bitflags/:bitflags at 30668016aca6bd3b02c766e8347e0b4080d4c296
Import subrepo clap/:clap at 784524f7eb193e35f81082cc69454c8c21b948f7
Import subrepo heck/:heck at 093d56fbf001e1506e56dbfa38631d99b1066df1
Import subrepo proc-macro-error/:proc-macro-error at 6c4cfe79a622c5de8ae68557993542be46eacae2
Import subrepo proc-macro2/:proc-macro2 at d5d48eddca4566e5438e8a2cbed4a74e049544de
Import subrepo quote/:quote at 727436c6c137b20f0f34dde5d8fda2679b9747ad
Import subrepo rustversion/:rustversion at 0c5663313516263059ce9059ef81fc7a1cf655ca
Import subrepo syn-mid/:syn-mid at 5d3d85414a9e6674e1857ec22a87b96e04a6851a
Import subrepo syn/:syn at e87c27e87f6f4ef8919d0372bdb056d53ef0d8f3
Import subrepo textwrap/:textwrap at abcd618beae3f74841032aa5b53c1086b0a57ca2
Import subrepo unicode-segmentation/:unicode-segmentation at 637c9874c4fe0c205ff27787faf150a40295c6c3
Import subrepo unicode-width/:unicode-width at 3033826f8bf05e82724140a981d5941e48fce393
Import subrepo unicode-xid/:unicode-xid at 4baae9fffb156ba229665b972a9cd5991787ceb7
Diffstat (limited to 'syn/tests/test_precedence.rs')
-rw-r--r-- | syn/tests/test_precedence.rs | 394 |
1 files changed, 394 insertions, 0 deletions
diff --git a/syn/tests/test_precedence.rs b/syn/tests/test_precedence.rs new file mode 100644 index 0000000..3529c06 --- /dev/null +++ b/syn/tests/test_precedence.rs @@ -0,0 +1,394 @@ +#![cfg(not(syn_disable_nightly_tests))] +#![recursion_limit = "1024"] +#![feature(rustc_private)] + +//! The tests in this module do the following: +//! +//! 1. Parse a given expression in both `syn` and `libsyntax`. +//! 2. Fold over the expression adding brackets around each subexpression (with +//! some complications - see the `syn_brackets` and `libsyntax_brackets` +//! methods). +//! 3. Serialize the `syn` expression back into a string, and re-parse it with +//! `libsyntax`. +//! 4. Respan all of the expressions, replacing the spans with the default +//! spans. +//! 5. Compare the expressions with one another, if they are not equal fail. + +extern crate rustc_data_structures; +extern crate rustc_span; +extern crate syntax; + +mod features; + +use quote::quote; +use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use regex::Regex; +use rustc_span::edition::Edition; +use syntax::ast; +use syntax::ptr::P; +use walkdir::{DirEntry, WalkDir}; + +use std::fs::File; +use std::io::Read; +use std::process; +use std::sync::atomic::{AtomicUsize, Ordering}; + +use common::eq::SpanlessEq; +use common::parse; + +#[macro_use] +mod macros; + +#[allow(dead_code)] +mod common; + +mod repo; + +/// Test some pre-set expressions chosen by us. +#[test] +fn test_simple_precedence() { + const EXPRS: &[&str] = &[ + "1 + 2 * 3 + 4", + "1 + 2 * ( 3 + 4 )", + "{ for i in r { } *some_ptr += 1; }", + "{ loop { break 5; } }", + "{ if true { () }.mthd() }", + "{ for i in unsafe { 20 } { } }", + ]; + + let mut failed = 0; + + for input in EXPRS { + let expr = if let Some(expr) = parse::syn_expr(input) { + expr + } else { + failed += 1; + continue; + }; + + let pf = match test_expressions(vec![expr]) { + (1, 0) => "passed", + (0, 1) => { + failed += 1; + "failed" + } + _ => unreachable!(), + }; + errorf!("=== {}: {}\n", input, pf); + } + + if failed > 0 { + panic!("Failed {} tests", failed); + } +} + +/// Test expressions from rustc, like in `test_round_trip`. +#[test] +fn test_rustc_precedence() { + repo::clone_rust(); + let abort_after = common::abort_after(); + if abort_after == 0 { + panic!("Skipping all precedence tests"); + } + + let passed = AtomicUsize::new(0); + let failed = AtomicUsize::new(0); + + // 2018 edition is hard + let edition_regex = Regex::new(r"\b(async|try)[!(]").unwrap(); + + WalkDir::new("tests/rust") + .sort_by(|a, b| a.file_name().cmp(b.file_name())) + .into_iter() + .filter_entry(repo::base_dir_filter) + .collect::<Result<Vec<DirEntry>, walkdir::Error>>() + .unwrap() + .into_par_iter() + .for_each(|entry| { + let path = entry.path(); + if path.is_dir() { + return; + } + + // Our version of `libsyntax` can't parse this tests + if path + .to_str() + .unwrap() + .ends_with("optional_comma_in_match_arm.rs") + { + return; + } + + let mut file = File::open(path).unwrap(); + let mut content = String::new(); + file.read_to_string(&mut content).unwrap(); + let content = edition_regex.replace_all(&content, "_$0"); + + let (l_passed, l_failed) = match syn::parse_file(&content) { + Ok(file) => { + let exprs = collect_exprs(file); + test_expressions(exprs) + } + Err(msg) => { + errorf!("syn failed to parse\n{:?}\n", msg); + (0, 1) + } + }; + + errorf!( + "=== {}: {} passed | {} failed\n", + path.display(), + l_passed, + l_failed + ); + + passed.fetch_add(l_passed, Ordering::SeqCst); + let prev_failed = failed.fetch_add(l_failed, Ordering::SeqCst); + + if prev_failed + l_failed >= abort_after { + process::exit(1); + } + }); + + let passed = passed.load(Ordering::SeqCst); + let failed = failed.load(Ordering::SeqCst); + + errorf!("\n===== Precedence Test Results =====\n"); + errorf!("{} passed | {} failed\n", passed, failed); + + if failed > 0 { + panic!("{} failures", failed); + } +} + +fn test_expressions(exprs: Vec<syn::Expr>) -> (usize, usize) { + let mut passed = 0; + let mut failed = 0; + + syntax::with_globals(Edition::Edition2018, || { + for expr in exprs { + let raw = quote!(#expr).to_string(); + + let libsyntax_ast = if let Some(e) = libsyntax_parse_and_rewrite(&raw) { + e + } else { + failed += 1; + errorf!("\nFAIL - libsyntax failed to parse raw\n"); + continue; + }; + + let syn_expr = syn_brackets(expr); + let syn_ast = if let Some(e) = parse::libsyntax_expr("e!(#syn_expr).to_string()) { + e + } else { + failed += 1; + errorf!("\nFAIL - libsyntax failed to parse bracketed\n"); + continue; + }; + + if SpanlessEq::eq(&syn_ast, &libsyntax_ast) { + passed += 1; + } else { + failed += 1; + errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, libsyntax_ast); + } + } + }); + + (passed, failed) +} + +fn libsyntax_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> { + parse::libsyntax_expr(input).and_then(libsyntax_brackets) +} + +/// Wrap every expression which is not already wrapped in parens with parens, to +/// reveal the precidence of the parsed expressions, and produce a stringified +/// form of the resulting expression. +/// +/// This method operates on libsyntax objects. +fn libsyntax_brackets(mut libsyntax_expr: P<ast::Expr>) -> Option<P<ast::Expr>> { + use rustc_data_structures::thin_vec::ThinVec; + use rustc_span::DUMMY_SP; + use std::mem; + use syntax::ast::{Block, Expr, ExprKind, Field, Mac, Pat, Stmt, StmtKind, Ty}; + use syntax::mut_visit::MutVisitor; + use syntax::util::map_in_place::MapInPlace; + + struct BracketsVisitor { + failed: bool, + }; + + fn flat_map_field<T: MutVisitor>(mut f: Field, vis: &mut T) -> Vec<Field> { + if f.is_shorthand { + noop_visit_expr(&mut f.expr, vis); + } else { + vis.visit_expr(&mut f.expr); + } + vec![f] + } + + fn flat_map_stmt<T: MutVisitor>(stmt: Stmt, vis: &mut T) -> Vec<Stmt> { + let kind = match stmt.kind { + // Don't wrap toplevel expressions in statements. + StmtKind::Expr(mut e) => { + noop_visit_expr(&mut e, vis); + StmtKind::Expr(e) + } + StmtKind::Semi(mut e) => { + noop_visit_expr(&mut e, vis); + StmtKind::Semi(e) + } + s => s, + }; + + vec![Stmt { kind, ..stmt }] + } + + fn noop_visit_expr<T: MutVisitor>(e: &mut Expr, vis: &mut T) { + use syntax::mut_visit::{noop_visit_expr, visit_opt, visit_thin_attrs}; + match &mut e.kind { + ExprKind::Struct(path, fields, expr) => { + vis.visit_path(path); + fields.flat_map_in_place(|field| flat_map_field(field, vis)); + visit_opt(expr, |expr| vis.visit_expr(expr)); + vis.visit_id(&mut e.id); + vis.visit_span(&mut e.span); + visit_thin_attrs(&mut e.attrs, vis); + } + _ => noop_visit_expr(e, vis), + } + } + + impl MutVisitor for BracketsVisitor { + fn visit_expr(&mut self, e: &mut P<Expr>) { + noop_visit_expr(e, self); + match e.kind { + ExprKind::If(..) | ExprKind::Block(..) | ExprKind::Let(..) => {} + _ => { + let inner = mem::replace( + e, + P(Expr { + id: ast::DUMMY_NODE_ID, + kind: ExprKind::Err, + span: DUMMY_SP, + attrs: ThinVec::new(), + }), + ); + e.kind = ExprKind::Paren(inner); + } + } + } + + fn visit_block(&mut self, block: &mut P<Block>) { + self.visit_id(&mut block.id); + block + .stmts + .flat_map_in_place(|stmt| flat_map_stmt(stmt, self)); + self.visit_span(&mut block.span); + } + + // We don't want to look at expressions that might appear in patterns or + // types yet. We'll look into comparing those in the future. For now + // focus on expressions appearing in other places. + fn visit_pat(&mut self, pat: &mut P<Pat>) { + let _ = pat; + } + + fn visit_ty(&mut self, ty: &mut P<Ty>) { + let _ = ty; + } + + fn visit_mac(&mut self, mac: &mut Mac) { + // By default when folding over macros, libsyntax panics. This is + // because it's usually not what you want, you want to run after + // macro expansion. We do want to do that (syn doesn't do macro + // expansion), so we implement visit_mac to just return the macro + // unchanged. + let _ = mac; + } + } + + let mut folder = BracketsVisitor { failed: false }; + folder.visit_expr(&mut libsyntax_expr); + if folder.failed { + None + } else { + Some(libsyntax_expr) + } +} + +/// Wrap every expression which is not already wrapped in parens with parens, to +/// reveal the precedence of the parsed expressions, and produce a stringified +/// form of the resulting expression. +fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr { + use syn::fold::*; + use syn::*; + + struct ParenthesizeEveryExpr; + impl Fold for ParenthesizeEveryExpr { + fn fold_expr(&mut self, expr: Expr) -> Expr { + match expr { + Expr::Group(_) => unreachable!(), + Expr::If(..) | Expr::Unsafe(..) | Expr::Block(..) | Expr::Let(..) => { + fold_expr(self, expr) + } + _ => Expr::Paren(ExprParen { + attrs: Vec::new(), + expr: Box::new(fold_expr(self, expr)), + paren_token: token::Paren::default(), + }), + } + } + + fn fold_stmt(&mut self, stmt: Stmt) -> Stmt { + match stmt { + // Don't wrap toplevel expressions in statements. + Stmt::Expr(e) => Stmt::Expr(fold_expr(self, e)), + Stmt::Semi(e, semi) => Stmt::Semi(fold_expr(self, e), semi), + s => s, + } + } + + // We don't want to look at expressions that might appear in patterns or + // types yet. We'll look into comparing those in the future. For now + // focus on expressions appearing in other places. + fn fold_pat(&mut self, pat: Pat) -> Pat { + pat + } + + fn fold_type(&mut self, ty: Type) -> Type { + ty + } + } + + let mut folder = ParenthesizeEveryExpr; + folder.fold_expr(syn_expr) +} + +/// Walk through a crate collecting all expressions we can find in it. +fn collect_exprs(file: syn::File) -> Vec<syn::Expr> { + use syn::fold::*; + use syn::punctuated::Punctuated; + use syn::*; + + struct CollectExprs(Vec<Expr>); + impl Fold for CollectExprs { + fn fold_expr(&mut self, expr: Expr) -> Expr { + match expr { + Expr::Verbatim(tokens) if tokens.is_empty() => {} + _ => self.0.push(expr), + } + + Expr::Tuple(ExprTuple { + attrs: vec![], + elems: Punctuated::new(), + paren_token: token::Paren::default(), + }) + } + } + + let mut folder = CollectExprs(vec![]); + folder.fold_file(file); + folder.0 +} |