test_precedence.rs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. #![cfg(not(syn_disable_nightly_tests))]
  2. #![cfg(not(miri))]
  3. #![recursion_limit = "1024"]
  4. #![feature(rustc_private)]
  5. #![allow(
  6. clippy::explicit_deref_methods,
  7. clippy::manual_assert,
  8. clippy::match_wildcard_for_single_variants,
  9. clippy::too_many_lines
  10. )]
  11. //! The tests in this module do the following:
  12. //!
  13. //! 1. Parse a given expression in both `syn` and `librustc`.
  14. //! 2. Fold over the expression adding brackets around each subexpression (with
  15. //! some complications - see the `syn_brackets` and `librustc_brackets`
  16. //! methods).
  17. //! 3. Serialize the `syn` expression back into a string, and re-parse it with
  18. //! `librustc`.
  19. //! 4. Respan all of the expressions, replacing the spans with the default
  20. //! spans.
  21. //! 5. Compare the expressions with one another, if they are not equal fail.
  22. extern crate rustc_ast;
  23. extern crate rustc_data_structures;
  24. extern crate rustc_span;
  25. use crate::common::eq::SpanlessEq;
  26. use crate::common::parse;
  27. use quote::quote;
  28. use rayon::iter::{IntoParallelIterator, ParallelIterator};
  29. use regex::Regex;
  30. use rustc_ast::ast;
  31. use rustc_ast::ptr::P;
  32. use rustc_span::edition::Edition;
  33. use std::fs;
  34. use std::process;
  35. use std::sync::atomic::{AtomicUsize, Ordering};
  36. use walkdir::{DirEntry, WalkDir};
  37. #[macro_use]
  38. mod macros;
  39. #[allow(dead_code)]
  40. mod common;
  41. mod repo;
  42. /// Test some pre-set expressions chosen by us.
  43. #[test]
  44. fn test_simple_precedence() {
  45. const EXPRS: &[&str] = &[
  46. "1 + 2 * 3 + 4",
  47. "1 + 2 * ( 3 + 4 )",
  48. "{ for i in r { } *some_ptr += 1; }",
  49. "{ loop { break 5; } }",
  50. "{ if true { () }.mthd() }",
  51. "{ for i in unsafe { 20 } { } }",
  52. ];
  53. let mut failed = 0;
  54. for input in EXPRS {
  55. let expr = if let Some(expr) = parse::syn_expr(input) {
  56. expr
  57. } else {
  58. failed += 1;
  59. continue;
  60. };
  61. let pf = match test_expressions(Edition::Edition2018, vec![expr]) {
  62. (1, 0) => "passed",
  63. (0, 1) => {
  64. failed += 1;
  65. "failed"
  66. }
  67. _ => unreachable!(),
  68. };
  69. errorf!("=== {}: {}\n", input, pf);
  70. }
  71. if failed > 0 {
  72. panic!("Failed {} tests", failed);
  73. }
  74. }
  75. /// Test expressions from rustc, like in `test_round_trip`.
  76. #[test]
  77. fn test_rustc_precedence() {
  78. common::rayon_init();
  79. repo::clone_rust();
  80. let abort_after = common::abort_after();
  81. if abort_after == 0 {
  82. panic!("Skipping all precedence tests");
  83. }
  84. let passed = AtomicUsize::new(0);
  85. let failed = AtomicUsize::new(0);
  86. // 2018 edition is hard
  87. let edition_regex = Regex::new(r"\b(async|try)[!(]").unwrap();
  88. WalkDir::new("tests/rust")
  89. .sort_by(|a, b| a.file_name().cmp(b.file_name()))
  90. .into_iter()
  91. .filter_entry(repo::base_dir_filter)
  92. .collect::<Result<Vec<DirEntry>, walkdir::Error>>()
  93. .unwrap()
  94. .into_par_iter()
  95. .for_each(|entry| {
  96. let path = entry.path();
  97. if path.is_dir() {
  98. return;
  99. }
  100. let content = fs::read_to_string(path).unwrap();
  101. let content = edition_regex.replace_all(&content, "_$0");
  102. let (l_passed, l_failed) = match syn::parse_file(&content) {
  103. Ok(file) => {
  104. let edition = repo::edition(path).parse().unwrap();
  105. let exprs = collect_exprs(file);
  106. test_expressions(edition, exprs)
  107. }
  108. Err(msg) => {
  109. errorf!("syn failed to parse\n{:?}\n", msg);
  110. (0, 1)
  111. }
  112. };
  113. errorf!(
  114. "=== {}: {} passed | {} failed\n",
  115. path.display(),
  116. l_passed,
  117. l_failed
  118. );
  119. passed.fetch_add(l_passed, Ordering::Relaxed);
  120. let prev_failed = failed.fetch_add(l_failed, Ordering::Relaxed);
  121. if prev_failed + l_failed >= abort_after {
  122. process::exit(1);
  123. }
  124. });
  125. let passed = passed.load(Ordering::Relaxed);
  126. let failed = failed.load(Ordering::Relaxed);
  127. errorf!("\n===== Precedence Test Results =====\n");
  128. errorf!("{} passed | {} failed\n", passed, failed);
  129. if failed > 0 {
  130. panic!("{} failures", failed);
  131. }
  132. }
  133. fn test_expressions(edition: Edition, exprs: Vec<syn::Expr>) -> (usize, usize) {
  134. let mut passed = 0;
  135. let mut failed = 0;
  136. rustc_span::create_session_if_not_set_then(edition, |_| {
  137. for expr in exprs {
  138. let raw = quote!(#expr).to_string();
  139. let librustc_ast = if let Some(e) = librustc_parse_and_rewrite(&raw) {
  140. e
  141. } else {
  142. failed += 1;
  143. errorf!("\nFAIL - librustc failed to parse raw\n");
  144. continue;
  145. };
  146. let syn_expr = syn_brackets(expr);
  147. let syn_ast = if let Some(e) = parse::librustc_expr(&quote!(#syn_expr).to_string()) {
  148. e
  149. } else {
  150. failed += 1;
  151. errorf!("\nFAIL - librustc failed to parse bracketed\n");
  152. continue;
  153. };
  154. if SpanlessEq::eq(&syn_ast, &librustc_ast) {
  155. passed += 1;
  156. } else {
  157. failed += 1;
  158. errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, librustc_ast);
  159. }
  160. }
  161. });
  162. (passed, failed)
  163. }
  164. fn librustc_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
  165. parse::librustc_expr(input).and_then(librustc_brackets)
  166. }
  167. /// Wrap every expression which is not already wrapped in parens with parens, to
  168. /// reveal the precedence of the parsed expressions, and produce a stringified
  169. /// form of the resulting expression.
  170. ///
  171. /// This method operates on librustc objects.
  172. fn librustc_brackets(mut librustc_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
  173. use rustc_ast::ast::{
  174. Attribute, Block, BorrowKind, Expr, ExprField, ExprKind, GenericArg, Local, LocalKind, Pat,
  175. Stmt, StmtKind, StructExpr, StructRest, Ty,
  176. };
  177. use rustc_ast::mut_visit::{noop_visit_generic_arg, noop_visit_local, MutVisitor};
  178. use rustc_data_structures::map_in_place::MapInPlace;
  179. use rustc_data_structures::thin_vec::ThinVec;
  180. use rustc_span::DUMMY_SP;
  181. use std::mem;
  182. use std::ops::DerefMut;
  183. struct BracketsVisitor {
  184. failed: bool,
  185. }
  186. fn flat_map_field<T: MutVisitor>(mut f: ExprField, vis: &mut T) -> Vec<ExprField> {
  187. if f.is_shorthand {
  188. noop_visit_expr(&mut f.expr, vis);
  189. } else {
  190. vis.visit_expr(&mut f.expr);
  191. }
  192. vec![f]
  193. }
  194. fn flat_map_stmt<T: MutVisitor>(stmt: Stmt, vis: &mut T) -> Vec<Stmt> {
  195. let kind = match stmt.kind {
  196. // Don't wrap toplevel expressions in statements.
  197. StmtKind::Expr(mut e) => {
  198. noop_visit_expr(&mut e, vis);
  199. StmtKind::Expr(e)
  200. }
  201. StmtKind::Semi(mut e) => {
  202. noop_visit_expr(&mut e, vis);
  203. StmtKind::Semi(e)
  204. }
  205. s => s,
  206. };
  207. vec![Stmt { kind, ..stmt }]
  208. }
  209. fn noop_visit_expr<T: MutVisitor>(e: &mut Expr, vis: &mut T) {
  210. use rustc_ast::mut_visit::{noop_visit_expr, visit_thin_attrs};
  211. match &mut e.kind {
  212. ExprKind::AddrOf(BorrowKind::Raw, ..) => {}
  213. ExprKind::Struct(expr) => {
  214. let StructExpr {
  215. qself,
  216. path,
  217. fields,
  218. rest,
  219. } = expr.deref_mut();
  220. vis.visit_qself(qself);
  221. vis.visit_path(path);
  222. fields.flat_map_in_place(|field| flat_map_field(field, vis));
  223. if let StructRest::Base(rest) = rest {
  224. vis.visit_expr(rest);
  225. }
  226. vis.visit_id(&mut e.id);
  227. vis.visit_span(&mut e.span);
  228. visit_thin_attrs(&mut e.attrs, vis);
  229. }
  230. _ => noop_visit_expr(e, vis),
  231. }
  232. }
  233. impl MutVisitor for BracketsVisitor {
  234. fn visit_expr(&mut self, e: &mut P<Expr>) {
  235. match e.kind {
  236. ExprKind::ConstBlock(..) => {}
  237. _ => noop_visit_expr(e, self),
  238. }
  239. match e.kind {
  240. ExprKind::If(..) | ExprKind::Block(..) | ExprKind::Let(..) => {}
  241. _ => {
  242. let inner = mem::replace(
  243. e,
  244. P(Expr {
  245. id: ast::DUMMY_NODE_ID,
  246. kind: ExprKind::Err,
  247. span: DUMMY_SP,
  248. attrs: ThinVec::new(),
  249. tokens: None,
  250. }),
  251. );
  252. e.kind = ExprKind::Paren(inner);
  253. }
  254. }
  255. }
  256. fn visit_generic_arg(&mut self, arg: &mut GenericArg) {
  257. match arg {
  258. // Don't wrap unbraced const generic arg as that's invalid syntax.
  259. GenericArg::Const(anon_const) => {
  260. if let ExprKind::Block(..) = &mut anon_const.value.kind {
  261. noop_visit_expr(&mut anon_const.value, self);
  262. }
  263. }
  264. _ => noop_visit_generic_arg(arg, self),
  265. }
  266. }
  267. fn visit_block(&mut self, block: &mut P<Block>) {
  268. self.visit_id(&mut block.id);
  269. block
  270. .stmts
  271. .flat_map_in_place(|stmt| flat_map_stmt(stmt, self));
  272. self.visit_span(&mut block.span);
  273. }
  274. fn visit_local(&mut self, local: &mut P<Local>) {
  275. match local.kind {
  276. LocalKind::InitElse(..) => {}
  277. _ => noop_visit_local(local, self),
  278. }
  279. }
  280. // We don't want to look at expressions that might appear in patterns or
  281. // types yet. We'll look into comparing those in the future. For now
  282. // focus on expressions appearing in other places.
  283. fn visit_pat(&mut self, pat: &mut P<Pat>) {
  284. let _ = pat;
  285. }
  286. fn visit_ty(&mut self, ty: &mut P<Ty>) {
  287. let _ = ty;
  288. }
  289. fn visit_attribute(&mut self, attr: &mut Attribute) {
  290. let _ = attr;
  291. }
  292. }
  293. let mut folder = BracketsVisitor { failed: false };
  294. folder.visit_expr(&mut librustc_expr);
  295. if folder.failed {
  296. None
  297. } else {
  298. Some(librustc_expr)
  299. }
  300. }
  301. /// Wrap every expression which is not already wrapped in parens with parens, to
  302. /// reveal the precedence of the parsed expressions, and produce a stringified
  303. /// form of the resulting expression.
  304. fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
  305. use syn::fold::{fold_expr, fold_generic_argument, fold_generic_method_argument, Fold};
  306. use syn::{token, Expr, ExprParen, GenericArgument, GenericMethodArgument, Pat, Stmt, Type};
  307. struct ParenthesizeEveryExpr;
  308. impl Fold for ParenthesizeEveryExpr {
  309. fn fold_expr(&mut self, expr: Expr) -> Expr {
  310. match expr {
  311. Expr::Group(_) => unreachable!(),
  312. Expr::If(..) | Expr::Unsafe(..) | Expr::Block(..) | Expr::Let(..) => {
  313. fold_expr(self, expr)
  314. }
  315. _ => Expr::Paren(ExprParen {
  316. attrs: Vec::new(),
  317. expr: Box::new(fold_expr(self, expr)),
  318. paren_token: token::Paren::default(),
  319. }),
  320. }
  321. }
  322. fn fold_generic_argument(&mut self, arg: GenericArgument) -> GenericArgument {
  323. match arg {
  324. GenericArgument::Const(arg) => GenericArgument::Const(match arg {
  325. Expr::Block(_) => fold_expr(self, arg),
  326. // Don't wrap unbraced const generic arg as that's invalid syntax.
  327. _ => arg,
  328. }),
  329. _ => fold_generic_argument(self, arg),
  330. }
  331. }
  332. fn fold_generic_method_argument(
  333. &mut self,
  334. arg: GenericMethodArgument,
  335. ) -> GenericMethodArgument {
  336. match arg {
  337. GenericMethodArgument::Const(arg) => GenericMethodArgument::Const(match arg {
  338. Expr::Block(_) => fold_expr(self, arg),
  339. // Don't wrap unbraced const generic arg as that's invalid syntax.
  340. _ => arg,
  341. }),
  342. _ => fold_generic_method_argument(self, arg),
  343. }
  344. }
  345. fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
  346. match stmt {
  347. // Don't wrap toplevel expressions in statements.
  348. Stmt::Expr(e) => Stmt::Expr(fold_expr(self, e)),
  349. Stmt::Semi(e, semi) => {
  350. if let Expr::Verbatim(_) = e {
  351. Stmt::Semi(e, semi)
  352. } else {
  353. Stmt::Semi(fold_expr(self, e), semi)
  354. }
  355. }
  356. s => s,
  357. }
  358. }
  359. // We don't want to look at expressions that might appear in patterns or
  360. // types yet. We'll look into comparing those in the future. For now
  361. // focus on expressions appearing in other places.
  362. fn fold_pat(&mut self, pat: Pat) -> Pat {
  363. pat
  364. }
  365. fn fold_type(&mut self, ty: Type) -> Type {
  366. ty
  367. }
  368. }
  369. let mut folder = ParenthesizeEveryExpr;
  370. folder.fold_expr(syn_expr)
  371. }
  372. /// Walk through a crate collecting all expressions we can find in it.
  373. fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
  374. use syn::fold::Fold;
  375. use syn::punctuated::Punctuated;
  376. use syn::{token, Expr, ExprTuple, Path};
  377. struct CollectExprs(Vec<Expr>);
  378. impl Fold for CollectExprs {
  379. fn fold_expr(&mut self, expr: Expr) -> Expr {
  380. match expr {
  381. Expr::Verbatim(_) => {}
  382. _ => self.0.push(expr),
  383. }
  384. Expr::Tuple(ExprTuple {
  385. attrs: vec![],
  386. elems: Punctuated::new(),
  387. paren_token: token::Paren::default(),
  388. })
  389. }
  390. fn fold_path(&mut self, path: Path) -> Path {
  391. // Skip traversing into const generic path arguments
  392. path
  393. }
  394. }
  395. let mut folder = CollectExprs(vec![]);
  396. folder.fold_file(file);
  397. folder.0
  398. }