// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. // ignore-cross-compile #![feature(rustc_private)] extern crate syntax; use syntax::ast::*; use syntax::codemap::{Spanned, DUMMY_SP}; use syntax::codemap::FilePathMapping; use syntax::fold::{self, Folder}; use syntax::parse::{self, ParseSess}; use syntax::print::pprust; use syntax::ptr::P; use syntax::util::ThinVec; fn parse_expr(ps: &ParseSess, src: &str) -> P { let mut p = parse::new_parser_from_source_str(ps, "".to_owned(), src.to_owned()); p.parse_expr().unwrap() } // Helper functions for building exprs fn expr(kind: ExprKind) -> P { P(Expr { id: DUMMY_NODE_ID, node: kind, span: DUMMY_SP, attrs: ThinVec::new(), }) } fn make_x() -> P { let seg = PathSegment { identifier: Ident::from_str("x"), span: DUMMY_SP, parameters: None, }; let path = Path { span: DUMMY_SP, segments: vec![seg], }; expr(ExprKind::Path(None, path)) } /// Iterate over exprs of depth up to `depth`. The goal is to explore all "interesting" /// combinations of expression nesting. For example, we explore combinations using `if`, but not /// `while` or `match`, since those should print and parse in much the same way as `if`. fn iter_exprs(depth: usize, f: &mut FnMut(P)) { if depth == 0 { f(make_x()); return; } let mut g = |e| f(expr(e)); for kind in 0 .. 17 { match kind { 0 => iter_exprs(depth - 1, &mut |e| g(ExprKind::Box(e))), 1 => { // Note that for binary expressions, we explore each side separately. The // parenthesization decisions for the LHS and RHS should be independent, and this // way produces `O(n)` results instead of `O(n^2)`. iter_exprs(depth - 1, &mut |e| g(ExprKind::InPlace(e, make_x()))); iter_exprs(depth - 1, &mut |e| g(ExprKind::InPlace(make_x(), e))); }, 2 => iter_exprs(depth - 1, &mut |e| g(ExprKind::Call(e, vec![]))), 3 => { let seg = PathSegment { identifier: Ident::from_str("x"), span: DUMMY_SP, parameters: None, }; iter_exprs(depth - 1, &mut |e| g(ExprKind::MethodCall( seg.clone(), vec![e, make_x()]))); iter_exprs(depth - 1, &mut |e| g(ExprKind::MethodCall( seg.clone(), vec![make_x(), e]))); }, 4 => { let op = Spanned { span: DUMMY_SP, node: BinOpKind::Add }; iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, e, make_x()))); iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, make_x(), e))); }, 5 => { let op = Spanned { span: DUMMY_SP, node: BinOpKind::Mul }; iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, e, make_x()))); iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, make_x(), e))); }, 6 => { let op = Spanned { span: DUMMY_SP, node: BinOpKind::Shl }; iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, e, make_x()))); iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, make_x(), e))); }, 7 => { iter_exprs(depth - 1, &mut |e| g(ExprKind::Unary(UnOp::Deref, e))); }, 8 => { let block = P(Block { stmts: Vec::new(), id: DUMMY_NODE_ID, rules: BlockCheckMode::Default, span: DUMMY_SP, }); iter_exprs(depth - 1, &mut |e| g(ExprKind::If(e, block.clone(), None))); }, 9 => { let decl = P(FnDecl { inputs: vec![], output: FunctionRetTy::Default(DUMMY_SP), variadic: false, }); iter_exprs(depth - 1, &mut |e| g( ExprKind::Closure(CaptureBy::Value, decl.clone(), e, DUMMY_SP))); }, 10 => { iter_exprs(depth - 1, &mut |e| g(ExprKind::Assign(e, make_x()))); iter_exprs(depth - 1, &mut |e| g(ExprKind::Assign(make_x(), e))); }, 11 => { let ident = Spanned { span: DUMMY_SP, node: Ident::from_str("f") }; iter_exprs(depth - 1, &mut |e| g(ExprKind::Field(e, ident))); }, 12 => { iter_exprs(depth - 1, &mut |e| g(ExprKind::Range( Some(e), Some(make_x()), RangeLimits::HalfOpen))); iter_exprs(depth - 1, &mut |e| g(ExprKind::Range( Some(make_x()), Some(e), RangeLimits::HalfOpen))); }, 13 => { iter_exprs(depth - 1, &mut |e| g(ExprKind::AddrOf(Mutability::Immutable, e))); }, 14 => { g(ExprKind::Ret(None)); iter_exprs(depth - 1, &mut |e| g(ExprKind::Ret(Some(e)))); }, 15 => { let seg = PathSegment { identifier: Ident::from_str("S"), span: DUMMY_SP, parameters: None, }; let path = Path { span: DUMMY_SP, segments: vec![seg], }; g(ExprKind::Struct(path, vec![], Some(make_x()))); }, 16 => { iter_exprs(depth - 1, &mut |e| g(ExprKind::Try(e))); }, _ => panic!("bad counter value in iter_exprs"), } } } // Folders for manipulating the placement of `Paren` nodes. See below for why this is needed. /// Folder that removes all `ExprKind::Paren` nodes. struct RemoveParens; impl Folder for RemoveParens { fn fold_expr(&mut self, e: P) -> P { let e = match e.node { ExprKind::Paren(ref inner) => inner.clone(), _ => e.clone(), }; e.map(|e| fold::noop_fold_expr(e, self)) } } /// Folder that inserts `ExprKind::Paren` nodes around every `Expr`. struct AddParens; impl Folder for AddParens { fn fold_expr(&mut self, e: P) -> P { let e = e.map(|e| fold::noop_fold_expr(e, self)); P(Expr { id: DUMMY_NODE_ID, node: ExprKind::Paren(e), span: DUMMY_SP, attrs: ThinVec::new(), }) } } fn main() { let ps = ParseSess::new(FilePathMapping::empty()); iter_exprs(2, &mut |e| { // If the pretty printer is correct, then `parse(print(e))` should be identical to `e`, // modulo placement of `Paren` nodes. let printed = pprust::expr_to_string(&e); println!("printed: {}", printed); let parsed = parse_expr(&ps, &printed); // We want to know if `parsed` is structurally identical to `e`, ignoring trivial // differences like placement of `Paren`s or the exact ranges of node spans. // Unfortunately, there is no easy way to make this comparison. Instead, we add `Paren`s // everywhere we can, then pretty-print. This should give an unambiguous representation of // each `Expr`, and it bypasses nearly all of the parenthesization logic, so we aren't // relying on the correctness of the very thing we're testing. let e1 = AddParens.fold_expr(RemoveParens.fold_expr(e)); let text1 = pprust::expr_to_string(&e1); let e2 = AddParens.fold_expr(RemoveParens.fold_expr(parsed)); let text2 = pprust::expr_to_string(&e2); assert!(text1 == text2, "exprs are not equal:\n e = {:?}\n parsed = {:?}", text1, text2); }); }