Merge pull request #173 from mystor/macroinput
Remove the MacroInput type alias
diff --git a/src/expr.rs b/src/expr.rs
index 2b73998..79a4b56 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -1098,6 +1098,26 @@
syn!(ExprRepeat) => { ExprKind::Repeat }
));
+ named!(expr_nosemi -> Expr, map!(alt!(
+ syn!(ExprIf) => { ExprKind::If }
+ |
+ syn!(ExprIfLet) => { ExprKind::IfLet }
+ |
+ syn!(ExprWhile) => { ExprKind::While }
+ |
+ syn!(ExprWhileLet) => { ExprKind::WhileLet }
+ |
+ syn!(ExprForLoop) => { ExprKind::ForLoop }
+ |
+ syn!(ExprLoop) => { ExprKind::Loop }
+ |
+ syn!(ExprMatch) => { ExprKind::Match }
+ |
+ syn!(ExprCatch) => { ExprKind::Catch }
+ |
+ syn!(ExprBlock) => { ExprKind::Block }
+ ), Expr::from));
+
impl Synom for ExprGroup {
named!(parse -> Self, do_parse!(
e: grouped!(syn!(Expr)) >>
@@ -1665,6 +1685,8 @@
|
stmt_item
|
+ stmt_blockexpr
+ |
stmt_expr
));
}
@@ -1716,34 +1738,31 @@
named!(stmt_item -> Stmt, map!(syn!(Item), |i| Stmt::Item(Box::new(i))));
- fn requires_semi(e: &Expr) -> bool {
- match e.node {
- ExprKind::If(_) |
- ExprKind::IfLet(_) |
- ExprKind::While(_) |
- ExprKind::WhileLet(_) |
- ExprKind::ForLoop(_) |
- ExprKind::Loop(_) |
- ExprKind::Match(_) |
- ExprKind::Block(_) => false,
-
- _ => true,
- }
- }
+ named!(stmt_blockexpr -> Stmt, do_parse!(
+ attrs: many0!(call!(Attribute::parse_outer)) >>
+ mut e: expr_nosemi >>
+ // If the next token is a `.` or a `?` it is special-cased to parse as
+ // an expression instead of a blockexpression.
+ not!(syn!(Dot)) >>
+ not!(syn!(Question)) >>
+ semi: option!(syn!(Semi)) >>
+ ({
+ e.attrs = attrs;
+ if let Some(semi) = semi {
+ Stmt::Semi(Box::new(e), semi)
+ } else {
+ Stmt::Expr(Box::new(e))
+ }
+ })
+ ));
named!(stmt_expr -> Stmt, do_parse!(
attrs: many0!(call!(Attribute::parse_outer)) >>
mut e: syn!(Expr) >>
- semi: option!(syn!(Semi)) >>
+ semi: syn!(Semi) >>
({
e.attrs = attrs;
- if let Some(s) = semi {
- Stmt::Semi(Box::new(e), s)
- } else if requires_semi(&e) {
- return parse_error();
- } else {
- Stmt::Expr(Box::new(e))
- }
+ Stmt::Semi(Box::new(e), semi)
})
));
diff --git a/src/fold.rs b/src/fold.rs
index 59dd73e..3dae69c 100644
--- a/src/fold.rs
+++ b/src/fold.rs
@@ -599,10 +599,10 @@
use proc_macro2::{TokenKind, TokenTree as TokenTree2};
match tt.0.kind {
TokenKind::Word(sym) => {
- let sym = folder.fold_ident(Ident::new(sym, Span(tt.0.span)));
+ // XXX: Do we want to fold over idents
TokenTree(TokenTree2 {
- span: sym.span.0,
- kind: TokenKind::Word(sym.sym),
+ span: tt.0.span,
+ kind: TokenKind::Word(sym),
})
}
TokenKind::Op(..) => tt,
@@ -837,6 +837,7 @@
}
MethodCall(e) => {
MethodCall(ExprMethodCall {
+ expr: e.expr.lift(|e| folder.fold_expr(e)),
method: folder.fold_ident(e.method),
typarams: e.typarams.lift(|t| folder.fold_ty(t)),
args: e.args.lift(|e| folder.fold_expr(e)),
diff --git a/tests/common/mod.rs b/tests/common/mod.rs
new file mode 100644
index 0000000..fcb8c61
--- /dev/null
+++ b/tests/common/mod.rs
@@ -0,0 +1,85 @@
+extern crate walkdir;
+
+use walkdir::DirEntry;
+use std::env;
+use std::u32;
+
+macro_rules! errorf {
+ ($($tt:tt)*) => {
+ {
+ use ::std::io::Write;
+ write!(::std::io::stderr(), $($tt)*).unwrap();
+ }
+ };
+}
+
+pub mod parse;
+pub mod respan;
+
+pub fn check_min_stack() {
+ let min_stack_value = env::var("RUST_MIN_STACK")
+ .expect("RUST_MIN_STACK env var should be set since some tests require it.");
+ let min_stack_value: usize = min_stack_value.parse()
+ .expect("RUST_MIN_STACK env var should be set since some tests require it.");
+ assert!(min_stack_value >= 16000000);
+}
+
+/// Read the `ABORT_AFTER_FAILURE` environment variable, and parse it.
+pub fn abort_after() -> u32 {
+ if let Ok(s) = env::var("ABORT_AFTER_FAILURE") {
+ if let Ok(n) = s.parse::<u32>() {
+ return n;
+ }
+ }
+ u32::MAX
+}
+
+pub fn base_dir_filter(entry: &DirEntry) -> bool {
+ let path = entry.path();
+ if path.is_dir() {
+ return true; // otherwise walkdir does not visit the files
+ }
+ if path.extension().map(|e| e != "rs").unwrap_or(true) {
+ return false;
+ }
+ let path_string = path.to_string_lossy();
+ let path_string = if cfg!(windows) {
+ path_string.replace('\\', "/").into()
+ } else {
+ path_string
+ };
+ // TODO assert that parsing fails on the parse-fail cases
+ if path_string.starts_with("tests/rust/src/test/parse-fail") ||
+ path_string.starts_with("tests/rust/src/test/compile-fail") {
+ return false;
+ }
+
+ if path_string.starts_with("tests/rust/src/test/ui") {
+ let stderr_path = path.with_extension("stderr");
+ if stderr_path.exists() {
+ // Expected to fail in some way
+ return false;
+ }
+ }
+
+ match path_string.as_ref() {
+ // TODO better support for attributes
+ "tests/rust/src/librustc_data_structures/blake2b.rs" |
+ // TODO better support for attributes
+ "tests/rust/src/test/incremental/hashes/enum_defs.rs" |
+ // TODO better support for attributes
+ "tests/rust/src/test/pretty/stmt_expr_attributes.rs" |
+ // not actually a test case
+ "tests/rust/src/test/run-pass/auxiliary/macro-include-items-expr.rs" |
+ // TODO better support for attributes
+ "tests/rust/src/test/run-pass/cfg_stmt_expr.rs" |
+ // TODO weird glob import
+ "tests/rust/src/test/run-pass/import-glob-crate.rs" |
+ // TODO better support for attributes
+ "tests/rust/src/test/run-pass/inner-attrs-on-impl.rs" |
+ // TODO better support for attributes
+ "tests/rust/src/test/run-pass/item-attributes.rs" => false,
+ _ => true,
+ }
+}
+
diff --git a/tests/common/parse.rs b/tests/common/parse.rs
new file mode 100644
index 0000000..15c2faf
--- /dev/null
+++ b/tests/common/parse.rs
@@ -0,0 +1,47 @@
+extern crate syn;
+extern crate syntex_syntax;
+
+use self::syntex_syntax::ast;
+use self::syntex_syntax::ptr::P;
+use self::syntex_syntax::parse::{self, ParseSess};
+use self::syntex_syntax::codemap::FilePathMapping;
+
+use std::panic;
+
+pub fn syntex_expr(input: &str) -> Option<P<ast::Expr>> {
+ match panic::catch_unwind(|| {
+ let sess = ParseSess::new(FilePathMapping::empty());
+ sess.span_diagnostic.set_continue_after_error(false);
+ let e = parse::parse_expr_from_source_str(
+ "test_precedence".to_string(),
+ input.to_string(),
+ &sess,
+ );
+ Some(match e {
+ Ok(expr) => expr,
+ Err(mut diagnostic) => {
+ diagnostic.emit();;
+ return None
+ }
+ })
+ }) {
+ Ok(Some(e)) => Some(e),
+ Ok(None) => {
+ None
+ }
+ Err(_) => {
+ errorf!("syntex paniced\n");
+ None
+ }
+ }
+}
+
+pub fn syn_expr(input: &str) -> Option<syn::Expr> {
+ match input.parse::<syn::Expr>() {
+ Ok(e) => Some(e),
+ Err(msg) => {
+ errorf!("syn failed to parse\n{:?}\n", msg);
+ None
+ }
+ }
+}
diff --git a/tests/common/respan.rs b/tests/common/respan.rs
new file mode 100644
index 0000000..c9d12f5
--- /dev/null
+++ b/tests/common/respan.rs
@@ -0,0 +1,245 @@
+use std::rc::Rc;
+use syntex_syntax::ast::{Attribute, Expr, ExprKind, Field, FnDecl, FunctionRetTy, ImplItem,
+ ImplItemKind, ItemKind, Mac, MetaItem, MetaItemKind, MethodSig,
+ NestedMetaItem, NestedMetaItemKind, TraitItem, TraitItemKind, TyParam,
+ Visibility};
+use syntex_syntax::codemap::{self, Spanned};
+use syntex_syntax::fold::{self, Folder};
+use syntex_syntax::parse::token::{Lit, Token};
+use syntex_syntax::ptr::P;
+use syntex_syntax::symbol::Symbol;
+use syntex_syntax::tokenstream::{Delimited, TokenTree};
+use syntex_syntax::util::move_map::MoveMap;
+use syntex_syntax::util::small_vector::SmallVector;
+
+use syntex_pos::{Span, DUMMY_SP};
+use syntex_syntax::ast;
+
+struct Respanner;
+
+impl Respanner {
+ fn fold_spanned<T>(&mut self, spanned: Spanned<T>) -> Spanned<T> {
+ codemap::respan(self.new_span(spanned.span), spanned.node)
+ }
+
+ fn fold_lit(&mut self, l: Lit) -> Lit {
+ // Give up on comparing literals inside of macros because there are
+ // so many equivalent representations of the same literal; they are
+ // tested elsewhere
+ match l {
+ Lit::Byte(_) => Lit::Byte(Symbol::intern("")),
+ Lit::Char(_) => Lit::Char(Symbol::intern("")),
+ Lit::Integer(_) => Lit::Integer(Symbol::intern("")),
+ Lit::Float(_) => Lit::Float(Symbol::intern("")),
+ Lit::Str_(_) => Lit::Str_(Symbol::intern("")),
+ Lit::ByteStr(_) => Lit::ByteStr(Symbol::intern("")),
+ _ => l,
+ }
+ }
+}
+
+impl Folder for Respanner {
+ fn new_span(&mut self, _: Span) -> Span {
+ DUMMY_SP
+ }
+
+ fn fold_item_kind(&mut self, i: ItemKind) -> ItemKind {
+ match i {
+ ItemKind::Fn(decl, unsafety, constness, abi, generics, body) => {
+ let generics = self.fold_generics(generics);
+ let decl = self.fold_fn_decl(decl);
+ let body = self.fold_block(body);
+ // default fold_item_kind does not fold this span
+ let constness = self.fold_spanned(constness);
+ ItemKind::Fn(decl, unsafety, constness, abi, generics, body)
+ }
+ _ => fold::noop_fold_item_kind(i, self),
+ }
+ }
+
+ fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
+ e.map(|e| {
+ let folded = fold::noop_fold_expr(e, self);
+ Expr {
+ node: match folded.node {
+ ExprKind::Lit(l) => {
+ // default fold_expr does not fold lits
+ ExprKind::Lit(l.map(|l| self.fold_spanned(l)))
+ }
+ ExprKind::Binary(op, lhs, rhs) => {
+ // default fold_expr does not fold the op span
+ ExprKind::Binary(self.fold_spanned(op),
+ self.fold_expr(lhs),
+ self.fold_expr(rhs))
+ }
+ ExprKind::AssignOp(op, lhs, rhs) => {
+ // default fold_expr does not fold the op span
+ ExprKind::AssignOp(self.fold_spanned(op),
+ self.fold_expr(lhs),
+ self.fold_expr(rhs))
+ }
+ other => other,
+ },
+ ..folded
+ }
+ })
+ }
+
+ fn fold_ty_param(&mut self, tp: TyParam) -> TyParam {
+ TyParam {
+ // default fold_ty_param does not fold the span
+ span: self.new_span(tp.span),
+ ..fold::noop_fold_ty_param(tp, self)
+ }
+ }
+
+ fn fold_fn_decl(&mut self, decl: P<FnDecl>) -> P<FnDecl> {
+ decl.map(|FnDecl { inputs, output, variadic }| {
+ FnDecl {
+ inputs: inputs.move_map(|x| self.fold_arg(x)),
+ output: match output {
+ FunctionRetTy::Ty(ty) => FunctionRetTy::Ty(self.fold_ty(ty)),
+ // default fold_fn_decl does not fold this span
+ FunctionRetTy::Default(span) => FunctionRetTy::Default(self.new_span(span)),
+ },
+ variadic: variadic,
+ }
+ })
+ }
+
+ fn fold_field(&mut self, field: Field) -> Field {
+ Field {
+ ident: codemap::respan(// default fold_field does not fold this span
+ self.new_span(field.ident.span),
+ self.fold_ident(field.ident.node)),
+ expr: self.fold_expr(field.expr),
+ span: self.new_span(field.span),
+ is_shorthand: field.is_shorthand,
+ attrs: ast::ThinVec::new(),
+ }
+ }
+
+ fn fold_trait_item(&mut self, i: TraitItem) -> SmallVector<TraitItem> {
+ let noop = fold::noop_fold_trait_item(i, self).expect_one("");
+ SmallVector::one(TraitItem {
+ node: match noop.node {
+ TraitItemKind::Method(sig, body) => {
+ TraitItemKind::Method(MethodSig {
+ constness: self.fold_spanned(sig.constness),
+ ..sig
+ },
+ body)
+ }
+ node => node,
+ },
+ ..noop
+ })
+ }
+
+ fn fold_impl_item(&mut self, i: ImplItem) -> SmallVector<ImplItem> {
+ let noop = fold::noop_fold_impl_item(i, self).expect_one("");
+ SmallVector::one(ImplItem {
+ node: match noop.node {
+ ImplItemKind::Method(sig, body) => {
+ ImplItemKind::Method(MethodSig {
+ constness: self.fold_spanned(sig.constness),
+ ..sig
+ },
+ body)
+ }
+ node => node,
+ },
+ ..noop
+ })
+ }
+
+ fn fold_attribute(&mut self, mut at: Attribute) -> Option<Attribute> {
+ at.id.0 = 0;
+ fold::noop_fold_attribute(at, self)
+ }
+
+ fn fold_meta_item(&mut self, meta_item: MetaItem) -> MetaItem {
+ let MetaItem { name, node, span } = meta_item;
+ MetaItem {
+ name: name,
+ node: match node {
+ MetaItemKind::Word => MetaItemKind::Word,
+ MetaItemKind::List(nested) => {
+ MetaItemKind::List(nested.move_map(|e| self.fold_meta_list_item(e)))
+ }
+ // default fold_meta_item does not fold the value span
+ MetaItemKind::NameValue(lit) => MetaItemKind::NameValue(self.fold_spanned(lit)),
+ },
+ span: self.new_span(span),
+ }
+ }
+
+ fn fold_meta_list_item(&mut self, list_item: NestedMetaItem) -> NestedMetaItem {
+ Spanned {
+ node: match list_item.node {
+ NestedMetaItemKind::MetaItem(mi) => {
+ NestedMetaItemKind::MetaItem(self.fold_meta_item(mi))
+ }
+ // default fold_meta_list_item does not fold the span
+ NestedMetaItemKind::Literal(lit) => {
+ NestedMetaItemKind::Literal(self.fold_spanned(lit))
+ }
+ },
+ span: self.new_span(list_item.span),
+ }
+ }
+
+ fn fold_mac(&mut self, mac: Mac) -> Mac {
+ fold::noop_fold_mac(mac, self)
+ }
+
+ fn fold_tt(&mut self, tt: TokenTree) -> TokenTree {
+ match tt {
+ TokenTree::Token(span, ref tok) => {
+ TokenTree::Token(self.new_span(span), self.fold_token(tok.clone()))
+ }
+ TokenTree::Delimited(span, ref delimed) => {
+ TokenTree::Delimited(self.new_span(span),
+ Delimited {
+ delim: delimed.delim,
+ tts: self.fold_tts(delimed.tts.clone().into()).into(),
+ })
+ }
+ }
+ }
+
+ fn fold_token(&mut self, t: Token) -> Token {
+ match t {
+ // default fold_token does not fold literals
+ Token::Literal(lit, repr) => Token::Literal(self.fold_lit(lit), repr),
+ Token::Ident(id) => Token::Ident(self.fold_ident(id)),
+ Token::Lifetime(id) => Token::Lifetime(self.fold_ident(id)),
+ Token::Interpolated(nt) => {
+ let nt = match Rc::try_unwrap(nt) {
+ Ok(nt) => nt,
+ Err(nt) => (*nt).clone(),
+ };
+ Token::Interpolated(Rc::new(self.fold_interpolated(nt)))
+ }
+ Token::SubstNt(ident) => Token::SubstNt(self.fold_ident(ident)),
+ _ => t,
+ }
+ }
+
+ fn fold_vis(&mut self, vis: Visibility) -> Visibility {
+ match vis {
+ Visibility::Crate(span) => Visibility::Crate(self.new_span(span)),
+ _ => fold::noop_fold_vis(vis, self),
+ }
+ }
+}
+
+#[allow(dead_code)]
+pub fn respan_crate(krate: ast::Crate) -> ast::Crate {
+ Respanner.fold_crate(krate)
+}
+
+#[allow(dead_code)]
+pub fn respan_expr(expr: P<ast::Expr>) -> P<ast::Expr> {
+ Respanner.fold_expr(expr)
+}
diff --git a/tests/test_precedence.rs b/tests/test_precedence.rs
new file mode 100644
index 0000000..a97f622
--- /dev/null
+++ b/tests/test_precedence.rs
@@ -0,0 +1,378 @@
+#![cfg(all(feature = "full", feature = "fold"))]
+
+//! The tests in this module do the following:
+//!
+//! 1. Parse a given expression in both `syn` and `syntex`.
+//! 2. Fold over the expression adding brackets around each subexpression (with
+//! some complications - see the `syn_brackets` and `syntex_brackets`
+//! methods).
+//! 3. Serialize the `syn` expression back into a string, and re-parse it with
+//! `syntex`.
+//! 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.
+
+#[macro_use]
+extern crate quote;
+extern crate syn;
+extern crate synom;
+extern crate syntex_pos;
+extern crate syntex_syntax;
+extern crate walkdir;
+extern crate proc_macro2;
+
+use syntex_syntax::ast;
+use syntex_syntax::ptr::P;
+
+use common::{respan, parse};
+
+#[allow(dead_code)]
+#[macro_use]
+mod common;
+
+/// 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() }",
+ ];
+
+ 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() {
+ use walkdir::{WalkDir, WalkDirIterator};
+ use std::fs::File;
+ use std::io::Read;
+
+ common::check_min_stack();
+ let abort_after = common::abort_after();
+ if abort_after == 0 {
+ panic!("Skipping all precedence tests");
+ }
+
+ let mut passed = 0;
+ let mut failed = 0;
+
+ let walk = WalkDir::new("tests/rust").sort_by(|a, b| a.cmp(b));
+ for entry in walk.into_iter().filter_entry(common::base_dir_filter) {
+ let entry = entry.unwrap();
+
+ let path = entry.path();
+ if path.is_dir() {
+ continue;
+ }
+
+ let mut file = File::open(path).unwrap();
+ let mut content = String::new();
+ file.read_to_string(&mut content).unwrap();
+
+ let (l_passed, l_failed) = match content.parse::<syn::Crate>() {
+ Ok(krate) => {
+ let exprs = collect_exprs(krate);
+ test_expressions(exprs)
+ }
+ Err(msg) => {
+ errorf!("syn failed to parse\n{:?}\n", msg);
+ failed += 1;
+ (0, 1)
+ }
+ };
+
+ passed += l_passed;
+ failed += l_failed;
+
+ errorf!("=== {}: {} passed | {} failed\n", path.display(), l_passed, l_failed);
+
+ if failed >= abort_after {
+ errorf!("Aborting Immediately due to ABORT_AFTER_FAILURE\n");
+ break;
+ }
+ }
+
+ 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>) -> (u32, u32) {
+ let mut passed = 0;
+ let mut failed = 0;
+
+ for expr in exprs {
+ let raw = quote!(#expr).to_string();
+
+ let syntex_ast = if let Some(e) = syntex_parse_and_rewrite(&raw) {
+ e
+ } else {
+ failed += 1;
+ errorf!("\nFAIL - syntex failed to parse raw\n");
+ continue;
+ };
+
+ let syn_expr = syn_brackets(expr);
+ let syn_ast = if let Some(e) = parse::syntex_expr("e!(#syn_expr).to_string()) {
+ e
+ } else {
+ failed += 1;
+ errorf!("\nFAIL - syntex failed to parse bracketed\n");
+ continue;
+ };
+
+ let syn_ast = respan::respan_expr(syn_ast);
+ let syntex_ast = respan::respan_expr(syntex_ast);
+
+ if syn_ast == syntex_ast {
+ passed += 1;
+ } else {
+ failed += 1;
+ errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, syntex_ast);
+ }
+ }
+
+ (passed, failed)
+}
+
+fn syntex_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
+ parse::syntex_expr(input).and_then(|e| syntex_brackets(e))
+}
+
+/// 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 syntex objects.
+fn syntex_brackets(syntex_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
+ use syntex_syntax::ast::{Expr, ExprKind, Mac, Stmt, StmtKind, Pat, Ty, Field};
+ use syntex_syntax::fold::{self, Folder};
+ use syntex_syntax::util::ThinVec;
+ use syntex_syntax::util::small_vector::SmallVector;
+ use syntex_syntax::ext::quote::rt::DUMMY_SP;
+ use syntex_syntax::codemap;
+
+ fn expr(node: ExprKind) -> P<Expr> {
+ P(Expr {
+ id: ast::DUMMY_NODE_ID,
+ node: node,
+ span: DUMMY_SP,
+ attrs: ThinVec::new(),
+ })
+ }
+
+ struct BracketsFolder {
+ failed: bool,
+ };
+ impl Folder for BracketsFolder {
+ fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
+ e.map(|e| {
+ Expr {
+ node: match e.node {
+ ExprKind::Paren(inner) => {
+ ExprKind::Paren(inner.map(|e| {
+ fold::noop_fold_expr(e, self)
+ }))
+ }
+ ExprKind::If(..) |
+ ExprKind::Block(..) |
+ ExprKind::IfLet(..) => {
+ return fold::noop_fold_expr(e, self);
+ }
+ node => {
+ ExprKind::Paren(expr(node).map(|e| {
+ fold::noop_fold_expr(e, self)
+ }))
+ }
+ },
+ ..e
+ }
+ })
+ }
+
+ fn fold_field(&mut self, f: Field) -> Field {
+ Field {
+ ident: codemap::respan(f.ident.span, self.fold_ident(f.ident.node)),
+ expr: if f.is_shorthand {
+ f.expr.map(|e| fold::noop_fold_expr(e, self))
+ } else {
+ self.fold_expr(f.expr)
+ },
+ span: self.new_span(f.span),
+ is_shorthand: f.is_shorthand,
+ attrs: fold::fold_thin_attrs(f.attrs, self),
+ }
+ }
+
+ // 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: P<Pat>) -> P<Pat> {
+ pat
+ }
+
+ fn fold_ty(&mut self, ty: P<Ty>) -> P<Ty> {
+ ty
+ }
+
+ fn fold_stmt(&mut self, stmt: Stmt) -> SmallVector<Stmt> {
+ let node = match stmt.node {
+ // Don't wrap toplevel expressions in statements.
+ StmtKind::Expr(e) => {
+ StmtKind::Expr(e.map(|e| fold::noop_fold_expr(e, self)))
+ }
+ StmtKind::Semi(e) => {
+ StmtKind::Semi(e.map(|e| fold::noop_fold_expr(e, self)))
+ }
+ s => s,
+ };
+
+ SmallVector::one(Stmt {
+ node: node,
+ ..stmt
+ })
+ }
+
+ fn fold_mac(&mut self, mac: Mac) -> Mac {
+ // By default when folding over macros, syntex 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 fold_mac to just return the macro
+ // unchanged.
+ mac
+ }
+ }
+
+ let mut folder = BracketsFolder {
+ failed: false,
+ };
+ let e = folder.fold_expr(syntex_expr);
+ if folder.failed {
+ None
+ } else {
+ Some(e)
+ }
+}
+
+/// 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.
+fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
+ use syn::*;
+ use syn::fold::*;
+
+ fn paren(folder: &mut BracketsFolder, node: ExprKind) -> ExprKind {
+ ExprKind::Paren(ExprParen {
+ expr: Box::new(noop_fold_expr(folder, Expr {
+ node: node,
+ attrs: vec![],
+ })),
+ paren_token: tokens::Paren::default(),
+ })
+ }
+
+ struct BracketsFolder;
+ impl Folder for BracketsFolder {
+ fn fold_expr(&mut self, expr: Expr) -> Expr {
+ let kind = match expr.node {
+ ExprKind::Group(_) => unreachable!(),
+ ExprKind::Paren(p) => paren(self, p.expr.node),
+ ExprKind::If(..) |
+ ExprKind::Block(..) |
+ ExprKind::IfLet(..) => {
+ return noop_fold_expr(self, expr);
+ }
+ node => paren(self, node),
+ };
+
+ Expr {
+ node: kind,
+ ..expr
+ }
+ }
+
+ fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
+ match stmt {
+ // Don't wrap toplevel expressions in statements.
+ Stmt::Expr(e) => {
+ Stmt::Expr(Box::new(noop_fold_expr(self, *e)))
+ }
+ Stmt::Semi(e, semi) => {
+ Stmt::Semi(Box::new(noop_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_ty(&mut self, ty: Ty) -> Ty {
+ ty
+ }
+ }
+
+ let mut folder = BracketsFolder;
+ folder.fold_expr(syn_expr)
+}
+
+/// Walk through a crate collecting all expressions we can find in it.
+fn collect_exprs(krate: syn::Crate) -> Vec<syn::Expr> {
+ use synom::delimited::Delimited;
+ use syn::*;
+ use syn::fold::*;
+
+ struct CollectExprsFolder(Vec<Expr>);
+ impl Folder for CollectExprsFolder {
+ fn fold_expr(&mut self, expr: Expr) -> Expr {
+ self.0.push(expr);
+
+ Expr {
+ node: ExprKind::Tup(ExprTup {
+ args: Delimited::new(),
+ paren_token: tokens::Paren::default(),
+ lone_comma: None
+ }),
+ attrs: vec![],
+ }
+ }
+ }
+
+ let mut folder = CollectExprsFolder(vec![]);
+ folder.fold_crate(krate);
+ folder.0
+}
diff --git a/tests/test_round_trip.rs b/tests/test_round_trip.rs
index bb07fc7..c07d5f9 100644
--- a/tests/test_round_trip.rs
+++ b/tests/test_round_trip.rs
@@ -8,91 +8,24 @@
extern crate syntex_syntax;
extern crate walkdir;
-use syntex_pos::Span;
use syntex_syntax::ast;
use syntex_syntax::parse::{self, ParseSess, PResult};
use syntex_syntax::codemap::FilePathMapping;
-use walkdir::{DirEntry, WalkDir, WalkDirIterator};
+use walkdir::{WalkDir, WalkDirIterator};
use std::fs::File;
-use std::io::{self, Read, Write};
+use std::io::Read;
use std::panic;
use std::time::Instant;
-macro_rules! errorf {
- ($($tt:tt)*) => {
- write!(io::stderr(), $($tt)*).unwrap();
- };
-}
-
-fn filter(entry: &DirEntry) -> bool {
- let path = entry.path();
- if path.is_dir() {
- return true; // otherwise walkdir does not visit the files
- }
- if path.extension().map(|e| e != "rs").unwrap_or(true) {
- return false;
- }
- let path_string = path.to_string_lossy();
- let path_string = if cfg!(windows) {
- path_string.replace('\\', "/").into()
- } else {
- path_string
- };
- // TODO assert that parsing fails on the parse-fail cases
- if path_string.starts_with("tests/rust/src/test/parse-fail") ||
- path_string.starts_with("tests/rust/src/test/compile-fail") {
- return false;
- }
-
- if path_string.starts_with("tests/rust/src/test/ui") {
- let stderr_path = path.with_extension("stderr");
- if stderr_path.exists() {
- // Expected to fail in some way
- return false;
- }
- }
-
- match path_string.as_ref() {
- // TODO better support for attributes
- "tests/rust/src/librustc_data_structures/blake2b.rs" |
- // TODO better support for attributes
- "tests/rust/src/test/incremental/hashes/enum_defs.rs" |
- // TODO better support for attributes
- "tests/rust/src/test/pretty/stmt_expr_attributes.rs" |
- // not actually a test case
- "tests/rust/src/test/run-pass/auxiliary/macro-include-items-expr.rs" |
- // TODO better support for attributes
- "tests/rust/src/test/run-pass/cfg_stmt_expr.rs" |
- // TODO weird glob import
- "tests/rust/src/test/run-pass/import-glob-crate.rs" |
- // TODO better support for attributes
- "tests/rust/src/test/run-pass/inner-attrs-on-impl.rs" |
- // TODO better support for attributes
- "tests/rust/src/test/run-pass/item-attributes.rs" => false,
- _ => true,
- }
-}
-
-/// Abort immediately after this many failures.
-fn abort_after() -> u32 {
- if let Ok(s) = std::env::var("ABORT_AFTER_FAILURE") {
- if let Ok(n) = s.parse::<u32>() {
- return n;
- }
- }
- std::u32::MAX
-}
+#[allow(dead_code)]
+#[macro_use]
+mod common;
#[test]
fn test_round_trip() {
- {
- let min_stack_value = std::env::var("RUST_MIN_STACK").expect("RUST_MIN_STACK env var should be set since some tests require it.");
- let min_stack_value: usize = min_stack_value.parse().expect("RUST_MIN_STACK env var should be set since some tests require it.");
- assert!(min_stack_value >= 16000000);
- }
-
- let abort_after = abort_after();
+ common::check_min_stack();
+ let abort_after = common::abort_after();
if abort_after == 0 {
panic!("Skipping all round_trip tests");
}
@@ -100,7 +33,7 @@
let mut failed = 0;
let walk = WalkDir::new("tests/rust").sort_by(|a, b| a.cmp(b));
- for entry in walk.into_iter().filter_entry(filter) {
+ for entry in walk.into_iter().filter_entry(common::base_dir_filter) {
let entry = entry.unwrap();
let path = entry.path();
@@ -180,242 +113,6 @@
fn syntex_parse(content: String, sess: &ParseSess) -> PResult<ast::Crate> {
let name = "test_round_trip".to_string();
- parse::parse_crate_from_source_str(name, content, sess).map(respan_crate)
-}
-
-fn respan_crate(krate: ast::Crate) -> ast::Crate {
- use std::rc::Rc;
- use syntex_syntax::ast::{Attribute, Expr, ExprKind, Field, FnDecl, FunctionRetTy, ImplItem,
- ImplItemKind, ItemKind, Mac, MetaItem, MetaItemKind, MethodSig,
- NestedMetaItem, NestedMetaItemKind, TraitItem, TraitItemKind, TyParam,
- Visibility};
- use syntex_syntax::codemap::{self, Spanned};
- use syntex_syntax::fold::{self, Folder};
- use syntex_syntax::parse::token::{Lit, Token};
- use syntex_syntax::ptr::P;
- use syntex_syntax::symbol::Symbol;
- use syntex_syntax::tokenstream::{Delimited, TokenTree};
- use syntex_syntax::util::move_map::MoveMap;
- use syntex_syntax::util::small_vector::SmallVector;
-
- struct Respanner;
-
- impl Respanner {
- fn fold_spanned<T>(&mut self, spanned: Spanned<T>) -> Spanned<T> {
- codemap::respan(self.new_span(spanned.span), spanned.node)
- }
-
- fn fold_lit(&mut self, l: Lit) -> Lit {
- // Give up on comparing literals inside of macros because there are
- // so many equivalent representations of the same literal; they are
- // tested elsewhere
- match l {
- Lit::Byte(_) => Lit::Byte(Symbol::intern("")),
- Lit::Char(_) => Lit::Char(Symbol::intern("")),
- Lit::Integer(_) => Lit::Integer(Symbol::intern("")),
- Lit::Float(_) => Lit::Float(Symbol::intern("")),
- Lit::Str_(_) => Lit::Str_(Symbol::intern("")),
- Lit::ByteStr(_) => Lit::ByteStr(Symbol::intern("")),
- _ => l,
- }
- }
- }
-
- impl Folder for Respanner {
- fn new_span(&mut self, _: Span) -> Span {
- syntex_pos::DUMMY_SP
- }
-
- fn fold_item_kind(&mut self, i: ItemKind) -> ItemKind {
- match i {
- ItemKind::Fn(decl, unsafety, constness, abi, generics, body) => {
- let generics = self.fold_generics(generics);
- let decl = self.fold_fn_decl(decl);
- let body = self.fold_block(body);
- // default fold_item_kind does not fold this span
- let constness = self.fold_spanned(constness);
- ItemKind::Fn(decl, unsafety, constness, abi, generics, body)
- }
- _ => fold::noop_fold_item_kind(i, self),
- }
- }
-
- fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
- e.map(|e| {
- let folded = fold::noop_fold_expr(e, self);
- Expr {
- node: match folded.node {
- ExprKind::Lit(l) => {
- // default fold_expr does not fold lits
- ExprKind::Lit(l.map(|l| self.fold_spanned(l)))
- }
- ExprKind::Binary(op, lhs, rhs) => {
- // default fold_expr does not fold the op span
- ExprKind::Binary(self.fold_spanned(op),
- self.fold_expr(lhs),
- self.fold_expr(rhs))
- }
- ExprKind::AssignOp(op, lhs, rhs) => {
- // default fold_expr does not fold the op span
- ExprKind::AssignOp(self.fold_spanned(op),
- self.fold_expr(lhs),
- self.fold_expr(rhs))
- }
- other => other,
- },
- ..folded
- }
- })
- }
-
- fn fold_ty_param(&mut self, tp: TyParam) -> TyParam {
- TyParam {
- // default fold_ty_param does not fold the span
- span: self.new_span(tp.span),
- ..fold::noop_fold_ty_param(tp, self)
- }
- }
-
- fn fold_fn_decl(&mut self, decl: P<FnDecl>) -> P<FnDecl> {
- decl.map(|FnDecl { inputs, output, variadic }| {
- FnDecl {
- inputs: inputs.move_map(|x| self.fold_arg(x)),
- output: match output {
- FunctionRetTy::Ty(ty) => FunctionRetTy::Ty(self.fold_ty(ty)),
- // default fold_fn_decl does not fold this span
- FunctionRetTy::Default(span) => FunctionRetTy::Default(self.new_span(span)),
- },
- variadic: variadic,
- }
- })
- }
-
- fn fold_field(&mut self, field: Field) -> Field {
- Field {
- ident: codemap::respan(// default fold_field does not fold this span
- self.new_span(field.ident.span),
- self.fold_ident(field.ident.node)),
- expr: self.fold_expr(field.expr),
- span: self.new_span(field.span),
- is_shorthand: field.is_shorthand,
- attrs: ast::ThinVec::new(),
- }
- }
-
- fn fold_trait_item(&mut self, i: TraitItem) -> SmallVector<TraitItem> {
- let noop = fold::noop_fold_trait_item(i, self).expect_one("");
- SmallVector::one(TraitItem {
- node: match noop.node {
- TraitItemKind::Method(sig, body) => {
- TraitItemKind::Method(MethodSig {
- constness: self.fold_spanned(sig.constness),
- ..sig
- },
- body)
- }
- node => node,
- },
- ..noop
- })
- }
-
- fn fold_impl_item(&mut self, i: ImplItem) -> SmallVector<ImplItem> {
- let noop = fold::noop_fold_impl_item(i, self).expect_one("");
- SmallVector::one(ImplItem {
- node: match noop.node {
- ImplItemKind::Method(sig, body) => {
- ImplItemKind::Method(MethodSig {
- constness: self.fold_spanned(sig.constness),
- ..sig
- },
- body)
- }
- node => node,
- },
- ..noop
- })
- }
-
- fn fold_attribute(&mut self, mut at: Attribute) -> Option<Attribute> {
- at.id.0 = 0;
- fold::noop_fold_attribute(at, self)
- }
-
- fn fold_meta_item(&mut self, meta_item: MetaItem) -> MetaItem {
- let MetaItem { name, node, span } = meta_item;
- MetaItem {
- name: name,
- node: match node {
- MetaItemKind::Word => MetaItemKind::Word,
- MetaItemKind::List(nested) => {
- MetaItemKind::List(nested.move_map(|e| self.fold_meta_list_item(e)))
- }
- // default fold_meta_item does not fold the value span
- MetaItemKind::NameValue(lit) => MetaItemKind::NameValue(self.fold_spanned(lit)),
- },
- span: self.new_span(span),
- }
- }
-
- fn fold_meta_list_item(&mut self, list_item: NestedMetaItem) -> NestedMetaItem {
- Spanned {
- node: match list_item.node {
- NestedMetaItemKind::MetaItem(mi) => {
- NestedMetaItemKind::MetaItem(self.fold_meta_item(mi))
- }
- // default fold_meta_list_item does not fold the span
- NestedMetaItemKind::Literal(lit) => {
- NestedMetaItemKind::Literal(self.fold_spanned(lit))
- }
- },
- span: self.new_span(list_item.span),
- }
- }
-
- fn fold_mac(&mut self, mac: Mac) -> Mac {
- fold::noop_fold_mac(mac, self)
- }
-
- fn fold_tt(&mut self, tt: TokenTree) -> TokenTree {
- match tt {
- TokenTree::Token(span, ref tok) => {
- TokenTree::Token(self.new_span(span), self.fold_token(tok.clone()))
- }
- TokenTree::Delimited(span, ref delimed) => {
- TokenTree::Delimited(self.new_span(span),
- Delimited {
- delim: delimed.delim,
- tts: self.fold_tts(delimed.tts.clone().into()).into(),
- })
- }
- }
- }
-
- fn fold_token(&mut self, t: Token) -> Token {
- match t {
- // default fold_token does not fold literals
- Token::Literal(lit, repr) => Token::Literal(self.fold_lit(lit), repr),
- Token::Ident(id) => Token::Ident(self.fold_ident(id)),
- Token::Lifetime(id) => Token::Lifetime(self.fold_ident(id)),
- Token::Interpolated(nt) => {
- let nt = match Rc::try_unwrap(nt) {
- Ok(nt) => nt,
- Err(nt) => (*nt).clone(),
- };
- Token::Interpolated(Rc::new(self.fold_interpolated(nt)))
- }
- Token::SubstNt(ident) => Token::SubstNt(self.fold_ident(ident)),
- _ => t,
- }
- }
-
- fn fold_vis(&mut self, vis: Visibility) -> Visibility {
- match vis {
- Visibility::Crate(span) => Visibility::Crate(self.new_span(span)),
- _ => fold::noop_fold_vis(vis, self),
- }
- }
- }
-
- Respanner.fold_crate(krate)
+ parse::parse_crate_from_source_str(name, content, sess)
+ .map(common::respan::respan_crate)
}