From 6c97f136126557a982990a1d0ef879996b268cba Mon Sep 17 00:00:00 2001 From: Ethan Brierley Date: Fri, 20 Oct 2023 09:46:17 +0100 Subject: [PATCH 01/18] Invalid `?` suggestion on mismatched `Ok(T)` --- compiler/rustc_hir_typeck/src/demand.rs | 5 +++++ .../issue-116967-cannot-coerce-returned-result.rs | 6 ++++++ ...ue-116967-cannot-coerce-returned-result.stderr | 15 +++++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.rs create mode 100644 tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.stderr diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 65ec2f232aed7..3b09fdda0ce0e 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -983,6 +983,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { return false; } + let e_ok = args_e.type_at(0); + let f_ok = args_f.type_at(0); + if !self.infcx.can_eq(self.param_env, f_ok, e_ok) { + return false; + } let e = args_e.type_at(1); let f = args_f.type_at(1); if self diff --git a/tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.rs b/tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.rs new file mode 100644 index 0000000000000..adf3049b4bab9 --- /dev/null +++ b/tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.rs @@ -0,0 +1,6 @@ +fn foo() -> Result { + let out: Result<(), ()> = Ok(()); + out //~ ERROR mismatched types +} + +fn main() {} diff --git a/tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.stderr b/tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.stderr new file mode 100644 index 0000000000000..447b22a152d7e --- /dev/null +++ b/tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> $DIR/issue-116967-cannot-coerce-returned-result.rs:3:5 + | +LL | fn foo() -> Result { + | ------------------ expected `Result` because of return type +LL | let out: Result<(), ()> = Ok(()); +LL | out + | ^^^ expected `Result`, found `Result<(), ()>` + | + = note: expected enum `Result` + found enum `Result<(), _>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From 24cdb27e2842e5abab2875ce29c365ac5503773e Mon Sep 17 00:00:00 2001 From: Ethan Brierley Date: Sun, 22 Oct 2023 12:05:28 +0100 Subject: [PATCH 02/18] let_chainify `suggest_coercing_result_via_try_operator` --- compiler/rustc_hir_typeck/src/demand.rs | 58 ++++++++++--------------- 1 file changed, 22 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 3b09fdda0ce0e..b385902c2869a 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -961,43 +961,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, found: Ty<'tcx>, ) -> bool { - let ty::Adt(e, args_e) = expected.kind() else { - return false; - }; - let ty::Adt(f, args_f) = found.kind() else { - return false; - }; - if e.did() != f.did() { - return false; - } - if Some(e.did()) != self.tcx.get_diagnostic_item(sym::Result) { - return false; - } let map = self.tcx.hir(); - if let Some(hir::Node::Expr(expr)) = map.find_parent(expr.hir_id) - && let hir::ExprKind::Ret(_) = expr.kind - { - // `return foo;` - } else if map.get_return_block(expr.hir_id).is_some() { - // Function's tail expression. - } else { - return false; - } - let e_ok = args_e.type_at(0); - let f_ok = args_f.type_at(0); - if !self.infcx.can_eq(self.param_env, f_ok, e_ok) { - return false; - } - let e = args_e.type_at(1); - let f = args_f.type_at(1); - if self - .infcx - .type_implements_trait( - self.tcx.get_diagnostic_item(sym::Into).unwrap(), - [f, e], - self.param_env, - ) - .must_apply_modulo_regions() + let returned = matches!( + map.find_parent(expr.hir_id), + Some(hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. })) + ) || map.get_return_block(expr.hir_id).is_some(); + if returned + && let ty::Adt(e, args_e) = expected.kind() + && let ty::Adt(f, args_f) = found.kind() + && e.did() == f.did() + && Some(e.did()) == self.tcx.get_diagnostic_item(sym::Result) + && let e_ok = args_e.type_at(0) + && let f_ok = args_f.type_at(0) + && self.infcx.can_eq(self.param_env, f_ok, e_ok) + && let e_err = args_e.type_at(1) + && let f_err = args_f.type_at(1) + && self + .infcx + .type_implements_trait( + self.tcx.get_diagnostic_item(sym::Into).unwrap(), + [f_err, e_err], + self.param_env, + ) + .must_apply_modulo_regions() { err.multipart_suggestion( "use `?` to coerce and return an appropriate `Err`, and wrap the resulting value \ From 87168909b0ad24ef82066480768e2cabd7fd600d Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 21 Oct 2023 20:08:32 +0000 Subject: [PATCH 03/18] Enable cg_clif tests for riscv64gc --- src/bootstrap/src/core/build_steps/test.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 831a86940fb48..fa35fff333ec6 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2999,7 +2999,10 @@ impl Step for CodegenCranelift { let triple = run.target.triple; let target_supported = if triple.contains("linux") { - triple.contains("x86_64") || triple.contains("aarch64") || triple.contains("s390x") + triple.contains("x86_64") + || triple.contains("aarch64") + || triple.contains("s390x") + || triple.contains("riscv64gc") } else if triple.contains("darwin") || triple.contains("windows") { triple.contains("x86_64") } else { From c1bfd46c7b2b5ec21275fdab2fa931c54f9b1f9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 23 Oct 2023 20:41:15 +0000 Subject: [PATCH 04/18] When expecting closure argument but finding block provide suggestion Detect if there is a potential typo where the `{` meant to open the closure body was written before the body. ``` error[E0277]: expected a `FnOnce<({integer},)>` closure, found `Option` --> $DIR/ruby_style_closure_successful_parse.rs:3:31 | LL | let p = Some(45).and_then({|x| | ______________________--------_^ | | | | | required by a bound introduced by this call LL | | 1 + 1; LL | | Some(x * 2) | | ----------- this tail expression is of type `Option` LL | | }); | |_____^ expected an `FnOnce<({integer},)>` closure, found `Option` | = help: the trait `FnOnce<({integer},)>` is not implemented for `Option` note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL help: you might have meant to open the closure body instead of placing a closure within a block | LL - let p = Some(45).and_then({|x| LL + let p = Some(45).and_then(|x| { | ``` Detect the potential typo where the closure header is missing. ``` error[E0277]: expected a `FnOnce<(&bool,)>` closure, found `bool` --> $DIR/block_instead_of_closure_in_arg.rs:3:23 | LL | Some(true).filter({ | _________________------_^ | | | | | required by a bound introduced by this call LL | |/ if number % 2 == 0 { LL | || number == 0 LL | || } else { LL | || number != 0 LL | || } | ||_________- this tail expression is of type `bool` LL | | }); | |______^ expected an `FnOnce<(&bool,)>` closure, found `bool` | = help: the trait `for<'a> FnOnce<(&'a bool,)>` is not implemented for `bool` note: required by a bound in `Option::::filter` --> $SRC_DIR/core/src/option.rs:LL:COL help: you might have meant to create the closure instead of a block | LL | Some(true).filter(|_| { | +++ ``` Partially address #27300. --- .../src/traits/error_reporting/suggestions.rs | 53 +++++++++++++++++-- .../block_instead_of_closure_in_arg.rs | 10 ++++ .../block_instead_of_closure_in_arg.stderr | 27 ++++++++++ .../ruby_style_closure_successful_parse.rs | 7 +++ ...ruby_style_closure_successful_parse.stderr | 25 +++++++++ 5 files changed, 117 insertions(+), 5 deletions(-) create mode 100644 tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.rs create mode 100644 tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr create mode 100644 tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.rs create mode 100644 tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 66529b67c17b3..393eca9b24cf4 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -3442,11 +3442,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let Some(Node::Expr(expr)) = hir.find(arg_hir_id) && let Some(typeck_results) = &self.typeck_results { - if let hir::Expr { kind: hir::ExprKind::Block(..), .. } = expr { - let expr = expr.peel_blocks(); - let ty = - typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)); - let span = expr.span; + if let hir::Expr { kind: hir::ExprKind::Block(block, _), .. } = expr { + let inner_expr = expr.peel_blocks(); + let ty = typeck_results.expr_ty_adjusted_opt(inner_expr) + .unwrap_or(Ty::new_misc_error(tcx)); + let span = inner_expr.span; if Some(span) != err.span.primary_span() { err.span_label( span, @@ -3457,6 +3457,49 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { format!("this tail expression is of type `{ty}`") }, ); + if let ty::PredicateKind::Clause(clause) = failed_pred.kind().skip_binder() + && let ty::ClauseKind::Trait(pred) = clause + && [ + tcx.lang_items().fn_once_trait(), + tcx.lang_items().fn_mut_trait(), + tcx.lang_items().fn_trait(), + ].contains(&Some(pred.def_id())) + { + if let [stmt, ..] = block.stmts + && let hir::StmtKind::Semi(value) = stmt.kind + && let hir::ExprKind::Closure(hir::Closure { + body, + fn_decl_span, + .. + }) = value.kind + && let body = hir.body(*body) + && !matches!(body.value.kind, hir::ExprKind::Block(..)) + { + // Check if the failed predicate was an expectation of a closure type + // and if there might have been a `{ |args|` typo instead of `|args| {`. + err.multipart_suggestion( + "you might have meant to open the closure body instead of placing \ + a closure within a block", + vec![ + (expr.span.with_hi(value.span.lo()), String::new()), + (fn_decl_span.shrink_to_hi(), " {".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } else { + // Maybe the bare block was meant to be a closure. + err.span_suggestion_verbose( + expr.span.shrink_to_lo(), + "you might have meant to create the closure instead of a block", + format!( + "|{}| ", + (0..pred.trait_ref.args.len() - 1).map(|_| "_") + .collect::>() + .join(", ")), + Applicability::MaybeIncorrect, + ); + } + } } } diff --git a/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.rs b/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.rs new file mode 100644 index 0000000000000..9c685590b8bba --- /dev/null +++ b/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.rs @@ -0,0 +1,10 @@ +fn main() { + let number = 2; + Some(true).filter({ //~ ERROR expected a `FnOnce<(&bool,)>` closure, found `bool` + if number % 2 == 0 { + number == 0 + } else { + number != 0 + } + }); +} diff --git a/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr b/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr new file mode 100644 index 0000000000000..b72360562f261 --- /dev/null +++ b/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr @@ -0,0 +1,27 @@ +error[E0277]: expected a `FnOnce<(&bool,)>` closure, found `bool` + --> $DIR/block_instead_of_closure_in_arg.rs:3:23 + | +LL | Some(true).filter({ + | _________________------_^ + | | | + | | required by a bound introduced by this call +LL | |/ if number % 2 == 0 { +LL | || number == 0 +LL | || } else { +LL | || number != 0 +LL | || } + | ||_________- this tail expression is of type `bool` +LL | | }); + | |______^ expected an `FnOnce<(&bool,)>` closure, found `bool` + | + = help: the trait `for<'a> FnOnce<(&'a bool,)>` is not implemented for `bool` +note: required by a bound in `Option::::filter` + --> $SRC_DIR/core/src/option.rs:LL:COL +help: you might have meant to create the closure instead of a block + | +LL | Some(true).filter(|_| { + | +++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.rs b/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.rs new file mode 100644 index 0000000000000..982b9fd00f647 --- /dev/null +++ b/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.rs @@ -0,0 +1,7 @@ +const x: usize =42; +fn main() { + let p = Some(45).and_then({|x| //~ ERROR expected a `FnOnce<({integer},)>` closure, found `Option` + 1 + 1; + Some(x * 2) + }); +} diff --git a/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr b/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr new file mode 100644 index 0000000000000..32d34430b7cb7 --- /dev/null +++ b/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr @@ -0,0 +1,25 @@ +error[E0277]: expected a `FnOnce<({integer},)>` closure, found `Option` + --> $DIR/ruby_style_closure_successful_parse.rs:3:31 + | +LL | let p = Some(45).and_then({|x| + | ______________________--------_^ + | | | + | | required by a bound introduced by this call +LL | | 1 + 1; +LL | | Some(x * 2) + | | ----------- this tail expression is of type `Option` +LL | | }); + | |_____^ expected an `FnOnce<({integer},)>` closure, found `Option` + | + = help: the trait `FnOnce<({integer},)>` is not implemented for `Option` +note: required by a bound in `Option::::and_then` + --> $SRC_DIR/core/src/option.rs:LL:COL +help: you might have meant to open the closure body instead of placing a closure within a block + | +LL - let p = Some(45).and_then({|x| +LL + let p = Some(45).and_then(|x| { + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. From f0a2635960375598c377e10b3461a0231ad69b8e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 19 Oct 2023 15:53:01 +1100 Subject: [PATCH 05/18] Redo `stringify.rs` test. Currently it only tests AST pretty-printing. This commit changes it to run every example through both AST pretty-printing and TokenStream pretty-printing. This makes it clear where there two pretty-printing approaches produce different results. --- tests/ui/macros/stringify.rs | 951 +++++++++++++++-------------------- 1 file changed, 412 insertions(+), 539 deletions(-) diff --git a/tests/ui/macros/stringify.rs b/tests/ui/macros/stringify.rs index 1e9e7a89dde77..939b4ce299fc9 100644 --- a/tests/ui/macros/stringify.rs +++ b/tests/ui/macros/stringify.rs @@ -15,483 +15,404 @@ #![feature(type_ascription)] #![deny(unused_macros)] -macro_rules! stringify_block { - ($block:block) => { - stringify!($block) +// These macros force the use of AST pretty-printing by converting the input to +// a particular fragment specifier. +macro_rules! block { ($block:block) => { stringify!($block) }; } +macro_rules! expr { ($expr:expr) => { stringify!($expr) }; } +macro_rules! item { ($item:item) => { stringify!($item) }; } +macro_rules! meta { ($meta:meta) => { stringify!($meta) }; } +macro_rules! pat { ($pat:pat) => { stringify!($pat) }; } +macro_rules! path { ($path:path) => { stringify!($path) }; } +macro_rules! stmt { ($stmt:stmt) => { stringify!($stmt) }; } +macro_rules! ty { ($ty:ty) => { stringify!($ty) }; } +macro_rules! vis { ($vis:vis) => { stringify!($vis) }; } + +// Use this when AST pretty-printing and TokenStream pretty-printing give +// the same result (which is preferable.) +macro_rules! c1 { + ($frag:ident, [$($tt:tt)*], $s:literal) => { + assert_eq!($frag!($($tt)*), $s); + assert_eq!(stringify!($($tt)*), $s); }; } -macro_rules! stringify_expr { - ($expr:expr) => { - stringify!($expr) - }; -} - -macro_rules! stringify_item { - ($item:item) => { - stringify!($item) - }; -} - -macro_rules! stringify_meta { - ($meta:meta) => { - stringify!($meta) - }; -} - -macro_rules! stringify_pat { - ($pat:pat) => { - stringify!($pat) - }; -} - -macro_rules! stringify_path { - ($path:path) => { - stringify!($path) - }; -} - -macro_rules! stringify_stmt { - ($stmt:stmt) => { - stringify!($stmt) - }; -} - -macro_rules! stringify_ty { - ($ty:ty) => { - stringify!($ty) - }; -} - -macro_rules! stringify_vis { - ($vis:vis) => { - stringify!($vis) +// Use this when AST pretty-printing and TokenStream pretty-printing give +// different results. +// +// `c1` and `c2` could be in a single macro, but having them separate makes it +// easy to find the cases where the two pretty-printing approaches give +// different results. +macro_rules! c2 { + ($frag:ident, [$($tt:tt)*], $s1:literal, $s2:literal) => { + assert_ne!($s1, $s2, "should use `c1!` instead"); + assert_eq!($frag!($($tt)*), $s1); + assert_eq!(stringify!($($tt)*), $s2); }; } #[test] fn test_block() { - assert_eq!(stringify_block!({}), "{}"); - assert_eq!(stringify_block!({ true }), "{ true }"); - assert_eq!(stringify_block!({ return }), "{ return }"); - assert_eq!( - stringify_block!({ + c1!(block, [ {} ], "{}"); + c1!(block, [ { true } ], "{ true }"); + c1!(block, [ { return } ], "{ return }"); + c2!(block, [ { return; - }), + } ], "{ return; }", + "{ return ; }" ); - assert_eq!( - stringify_block!({ + c2!(block, + [ { let _; true - }), + } ], "{ let _; true }", + "{ let _ ; true }" ); } #[test] fn test_expr() { // ExprKind::Array - assert_eq!(stringify_expr!([]), "[]"); - assert_eq!(stringify_expr!([true]), "[true]"); - assert_eq!(stringify_expr!([true,]), "[true]"); - assert_eq!(stringify_expr!([true, true]), "[true, true]"); + c1!(expr, [ [] ], "[]"); + c1!(expr, [ [true] ], "[true]"); + c2!(expr, [ [true,] ], "[true]", "[true,]"); + c1!(expr, [ [true, true] ], "[true, true]"); // ExprKind::Call - assert_eq!(stringify_expr!(f()), "f()"); - assert_eq!(stringify_expr!(f::()), "f::()"); - assert_eq!(stringify_expr!(f::<1>()), "f::<1>()"); - assert_eq!(stringify_expr!(f::<'a, u8, 1>()), "f::<'a, u8, 1>()"); - assert_eq!(stringify_expr!(f(true)), "f(true)"); - assert_eq!(stringify_expr!(f(true,)), "f(true)"); - assert_eq!(stringify_expr!(()()), "()()"); + c1!(expr, [ f() ], "f()"); + c2!(expr, [ f::() ], "f::()", "f :: < u8 > ()"); + c2!(expr, [ f::<1>() ], "f::<1>()", "f :: < 1 > ()"); + c2!(expr, [ f::<'a, u8, 1>() ], "f::<'a, u8, 1>()", "f :: < 'a, u8, 1 > ()"); + c1!(expr, [ f(true) ], "f(true)"); + c2!(expr, [ f(true,) ], "f(true)", "f(true,)"); + c2!(expr, [ ()() ], "()()", "() ()"); // ExprKind::MethodCall - assert_eq!(stringify_expr!(x.f()), "x.f()"); - assert_eq!(stringify_expr!(x.f::()), "x.f::()"); + c1!(expr, [ x.f() ], "x.f()"); + c2!(expr, [ x.f::() ], "x.f::()", "x.f :: < u8 > ()"); // ExprKind::Tup - assert_eq!(stringify_expr!(()), "()"); - assert_eq!(stringify_expr!((true,)), "(true,)"); - assert_eq!(stringify_expr!((true, false)), "(true, false)"); - assert_eq!(stringify_expr!((true, false,)), "(true, false)"); + c1!(expr, [ () ], "()"); + c1!(expr, [ (true,) ], "(true,)"); + c1!(expr, [ (true, false) ], "(true, false)"); + c2!(expr, [ (true, false,) ], "(true, false)", "(true, false,)"); // ExprKind::Binary - assert_eq!(stringify_expr!(true || false), "true || false"); - assert_eq!(stringify_expr!(true || false && false), "true || false && false"); + c1!(expr, [ true || false ], "true || false"); + c1!(expr, [ true || false && false ], "true || false && false"); // ExprKind::Unary - assert_eq!(stringify_expr!(*expr), "*expr"); - assert_eq!(stringify_expr!(!expr), "!expr"); - assert_eq!(stringify_expr!(-expr), "-expr"); + c2!(expr, [ *expr ], "*expr", "* expr"); + c2!(expr, [ !expr ], "!expr", "! expr"); + c2!(expr, [ -expr ], "-expr", "- expr"); // ExprKind::Lit - assert_eq!(stringify_expr!('x'), "'x'"); - assert_eq!(stringify_expr!(1_000_i8), "1_000_i8"); - assert_eq!(stringify_expr!(1.00000000000000001), "1.00000000000000001"); + c1!(expr, [ 'x' ], "'x'"); + c1!(expr, [ 1_000_i8 ], "1_000_i8"); + c1!(expr, [ 1.00000000000000001 ], "1.00000000000000001"); // ExprKind::Cast - assert_eq!(stringify_expr!(expr as T), "expr as T"); - assert_eq!(stringify_expr!(expr as T), "expr as T"); + c1!(expr, [ expr as T ], "expr as T"); + c2!(expr, [ expr as T ], "expr as T", "expr as T < u8 >"); // ExprKind::Type // There is no syntax for type ascription. // ExprKind::If - assert_eq!(stringify_expr!(if true {}), "if true {}"); - assert_eq!( - stringify_expr!(if true { + c1!(expr, [ if true {} ], "if true {}"); + c1!(expr, + [ if true { } else { - }), - "if true {} else {}", + } ], + "if true {} else {}" ); - assert_eq!( - stringify_expr!(if let true = true { + c1!(expr, + [ if let true = true { } else { - }), - "if let true = true {} else {}", + } ], + "if let true = true {} else {}" ); - assert_eq!( - stringify_expr!(if true { + c1!(expr, + [ if true { } else if false { - }), - "if true {} else if false {}", + } ], + "if true {} else if false {}" ); - assert_eq!( - stringify_expr!(if true { + c1!(expr, + [ if true { } else if false { } else { - }), - "if true {} else if false {} else {}", + } ], + "if true {} else if false {} else {}" ); - assert_eq!( - stringify_expr!(if true { + c2!(expr, + [ if true { return; } else if false { 0 } else { 0 - }), + } ], "if true { return; } else if false { 0 } else { 0 }", + "if true { return ; } else if false { 0 } else { 0 }" ); // ExprKind::While - assert_eq!(stringify_expr!(while true {}), "while true {}"); - assert_eq!(stringify_expr!('a: while true {}), "'a: while true {}"); - assert_eq!(stringify_expr!(while let true = true {}), "while let true = true {}"); + c1!(expr, [ while true {} ], "while true {}"); + c2!(expr, [ 'a: while true {} ], "'a: while true {}", "'a : while true {}"); + c1!(expr, [ while let true = true {} ], "while let true = true {}"); // ExprKind::ForLoop - assert_eq!(stringify_expr!(for _ in x {}), "for _ in x {}"); - assert_eq!(stringify_expr!('a: for _ in x {}), "'a: for _ in x {}"); + c1!(expr, [ for _ in x {} ], "for _ in x {}"); + c2!(expr, [ 'a: for _ in x {} ], "'a: for _ in x {}", "'a : for _ in x {}"); // ExprKind::Loop - assert_eq!(stringify_expr!(loop {}), "loop {}"); - assert_eq!(stringify_expr!('a: loop {}), "'a: loop {}"); + c1!(expr, [ loop {} ], "loop {}"); + c2!(expr, [ 'a: loop {} ], "'a: loop {}", "'a : loop {}"); // ExprKind::Match - assert_eq!(stringify_expr!(match self {}), "match self {}"); - assert_eq!( - stringify_expr!(match self { + c1!(expr, [ match self {} ], "match self {}"); + c1!(expr, + [ match self { Ok => 1, - }), - "match self { Ok => 1, }", + } ], + "match self { Ok => 1, }" ); - assert_eq!( - stringify_expr!(match self { + c1!(expr, + [ match self { Ok => 1, Err => 0, - }), - "match self { Ok => 1, Err => 0, }", + } ], + "match self { Ok => 1, Err => 0, }" ); // ExprKind::Closure - assert_eq!(stringify_expr!(|| {}), "|| {}"); - assert_eq!(stringify_expr!(|x| {}), "|x| {}"); - assert_eq!(stringify_expr!(|x: u8| {}), "|x: u8| {}"); - assert_eq!(stringify_expr!(|| ()), "|| ()"); - assert_eq!(stringify_expr!(move || self), "move || self"); - assert_eq!(stringify_expr!(async || self), "async || self"); - assert_eq!(stringify_expr!(async move || self), "async move || self"); - assert_eq!(stringify_expr!(static || self), "static || self"); - assert_eq!(stringify_expr!(static move || self), "static move || self"); + c1!(expr, [ || {} ], "|| {}"); + c2!(expr, [ |x| {} ], "|x| {}", "| x | {}"); + c2!(expr, [ |x: u8| {} ], "|x: u8| {}", "| x : u8 | {}"); + c1!(expr, [ || () ], "|| ()"); + c1!(expr, [ move || self ], "move || self"); + c1!(expr, [ async || self ], "async || self"); + c1!(expr, [ async move || self ], "async move || self"); + c1!(expr, [ static || self ], "static || self"); + c1!(expr, [ static move || self ], "static move || self"); #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5149 - assert_eq!( - stringify_expr!(static async || self), - "static async || self", + c1!(expr, + [ static async || self ], + "static async || self" ); #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5149 - assert_eq!( - stringify_expr!(static async move || self), - "static async move || self", + c1!(expr, + [ static async move || self ], + "static async move || self" ); - assert_eq!(stringify_expr!(|| -> u8 { self }), "|| -> u8 { self }"); - assert_eq!(stringify_expr!(1 + || {}), "1 + (|| {})"); // ?? + c1!(expr, [ || -> u8 { self } ], "|| -> u8 { self }"); + c2!(expr, [ 1 + || {} ], "1 + (|| {})", "1 + || {}"); // AST?? // ExprKind::Block - assert_eq!(stringify_expr!({}), "{}"); - assert_eq!(stringify_expr!(unsafe {}), "unsafe {}"); - assert_eq!(stringify_expr!('a: {}), "'a: {}"); - assert_eq!( - stringify_expr!( + c1!(expr, [ {} ], "{}"); + c1!(expr, [ unsafe {} ], "unsafe {}"); + c2!(expr, [ 'a: {} ], "'a: {}", "'a : {}"); + c1!(expr, + [ #[attr] {} - ), - "#[attr] {}", + ], + "#[attr] {}" ); - assert_eq!( - stringify_expr!( + c2!(expr, + [ { #![attr] } - ), + ], "{\n\ \x20 #![attr]\n\ }", + "{ #! [attr] }" ); // ExprKind::Async - assert_eq!(stringify_expr!(async {}), "async {}"); - assert_eq!(stringify_expr!(async move {}), "async move {}"); + c1!(expr, [ async {} ], "async {}"); + c1!(expr, [ async move {} ], "async move {}"); // ExprKind::Await - assert_eq!(stringify_expr!(expr.await), "expr.await"); + c1!(expr, [ expr.await ], "expr.await"); // ExprKind::TryBlock - assert_eq!(stringify_expr!(try {}), "try {}"); + c1!(expr, [ try {} ], "try {}"); // ExprKind::Assign - assert_eq!(stringify_expr!(expr = true), "expr = true"); + c1!(expr, [ expr = true ], "expr = true"); // ExprKind::AssignOp - assert_eq!(stringify_expr!(expr += true), "expr += true"); + c1!(expr, [ expr += true ], "expr += true"); // ExprKind::Field - assert_eq!(stringify_expr!(expr.field), "expr.field"); - assert_eq!(stringify_expr!(expr.0), "expr.0"); + c1!(expr, [ expr.field ], "expr.field"); + c1!(expr, [ expr.0 ], "expr.0"); // ExprKind::Index - assert_eq!(stringify_expr!(expr[true]), "expr[true]"); + c2!(expr, [ expr[true] ], "expr[true]", "expr [true]"); // ExprKind::Range - assert_eq!(stringify_expr!(..), ".."); - assert_eq!(stringify_expr!(..hi), "..hi"); - assert_eq!(stringify_expr!(lo..), "lo.."); - assert_eq!(stringify_expr!(lo..hi), "lo..hi"); - assert_eq!(stringify_expr!(..=hi), "..=hi"); - assert_eq!(stringify_expr!(lo..=hi), "lo..=hi"); - assert_eq!(stringify_expr!(-2..=-1), "-2..=-1"); + c1!(expr, [ .. ], ".."); + c2!(expr, [ ..hi ], "..hi", ".. hi"); + c2!(expr, [ lo.. ], "lo..", "lo .."); + c2!(expr, [ lo..hi ], "lo..hi", "lo .. hi"); + c2!(expr, [ ..=hi ], "..=hi", "..= hi"); + c2!(expr, [ lo..=hi ], "lo..=hi", "lo ..= hi"); + c2!(expr, [ -2..=-1 ], "-2..=-1", "- 2 ..= - 1"); // ExprKind::Path - assert_eq!(stringify_expr!(thing), "thing"); - assert_eq!(stringify_expr!(m::thing), "m::thing"); - assert_eq!(stringify_expr!(self::thing), "self::thing"); - assert_eq!(stringify_expr!(crate::thing), "crate::thing"); - assert_eq!(stringify_expr!(Self::thing), "Self::thing"); - assert_eq!(stringify_expr!(::thing), "::thing"); - assert_eq!(stringify_expr!(Self::<'static>), "Self::<'static>"); + c1!(expr, [ thing ], "thing"); + c2!(expr, [ m::thing ], "m::thing", "m :: thing"); + c2!(expr, [ self::thing ], "self::thing", "self :: thing"); + c2!(expr, [ crate::thing ], "crate::thing", "crate :: thing"); + c2!(expr, [ Self::thing ], "Self::thing", "Self :: thing"); + c2!(expr, [ ::thing ], "::thing", "< Self as T > :: thing"); + c2!(expr, [ Self::<'static> ], "Self::<'static>", "Self :: < 'static >"); // ExprKind::AddrOf - assert_eq!(stringify_expr!(&expr), "&expr"); - assert_eq!(stringify_expr!(&mut expr), "&mut expr"); - assert_eq!(stringify_expr!(&raw const expr), "&raw const expr"); - assert_eq!(stringify_expr!(&raw mut expr), "&raw mut expr"); + c2!(expr, [ &expr ], "&expr", "& expr"); + c2!(expr, [ &mut expr ], "&mut expr", "& mut expr"); + c2!(expr, [ &raw const expr ], "&raw const expr", "& raw const expr"); + c2!(expr, [ &raw mut expr ], "&raw mut expr", "& raw mut expr"); // ExprKind::Break - assert_eq!(stringify_expr!(break), "break"); - assert_eq!(stringify_expr!(break 'a), "break 'a"); - assert_eq!(stringify_expr!(break true), "break true"); - assert_eq!(stringify_expr!(break 'a true), "break 'a true"); + c1!(expr, [ break ], "break"); + c1!(expr, [ break 'a ], "break 'a"); + c1!(expr, [ break true ], "break true"); + c1!(expr, [ break 'a true ], "break 'a true"); // ExprKind::Continue - assert_eq!(stringify_expr!(continue), "continue"); - assert_eq!(stringify_expr!(continue 'a), "continue 'a"); + c1!(expr, [ continue ], "continue"); + c1!(expr, [ continue 'a ], "continue 'a"); // ExprKind::Ret - assert_eq!(stringify_expr!(return), "return"); - assert_eq!(stringify_expr!(return true), "return true"); + c1!(expr, [ return ], "return"); + c1!(expr, [ return true ], "return true"); // ExprKind::MacCall - assert_eq!(stringify_expr!(mac!(...)), "mac!(...)"); - assert_eq!(stringify_expr!(mac![...]), "mac![...]"); - assert_eq!(stringify_expr!(mac! { ... }), "mac! { ... }"); + c2!(expr, [ mac!(...) ], "mac!(...)", "mac! (...)"); + c2!(expr, [ mac![...] ], "mac![...]", "mac! [...]"); + c1!(expr, [ mac! { ... } ], "mac! { ... }"); // ExprKind::Struct - assert_eq!(stringify_expr!(Struct {}), "Struct {}"); + c1!(expr, [ Struct {} ], "Struct {}"); #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5151 - assert_eq!(stringify_expr!(::Type {}), "::Type {}"); - assert_eq!(stringify_expr!(Struct { .. }), "Struct { .. }"); - assert_eq!(stringify_expr!(Struct { ..base }), "Struct { ..base }"); - assert_eq!(stringify_expr!(Struct { x }), "Struct { x }"); - assert_eq!(stringify_expr!(Struct { x, .. }), "Struct { x, .. }"); - assert_eq!(stringify_expr!(Struct { x, ..base }), "Struct { x, ..base }"); - assert_eq!(stringify_expr!(Struct { x: true }), "Struct { x: true }"); - assert_eq!(stringify_expr!(Struct { x: true, .. }), "Struct { x: true, .. }"); - assert_eq!(stringify_expr!(Struct { x: true, ..base }), "Struct { x: true, ..base }"); + c2!(expr, + [ ::Type {} ], + "::Type {}", + "< Struct as Trait > :: Type {}" + ); + c1!(expr, [ Struct { .. } ], "Struct { .. }"); + c2!(expr, [ Struct { ..base } ], "Struct { ..base }", "Struct { .. base }"); + c1!(expr, [ Struct { x } ], "Struct { x }"); + c1!(expr, [ Struct { x, .. } ], "Struct { x, .. }"); + c2!(expr, [ Struct { x, ..base } ], "Struct { x, ..base }", "Struct { x, .. base }"); + c2!(expr, [ Struct { x: true } ], "Struct { x: true }", "Struct { x : true }"); + c2!(expr, [ Struct { x: true, .. } ], "Struct { x: true, .. }", "Struct { x : true, .. }"); + c2!(expr, + [ Struct { x: true, ..base } ], + "Struct { x: true, ..base }", + "Struct { x : true, .. base }" + ); // ExprKind::Repeat - assert_eq!(stringify_expr!([(); 0]), "[(); 0]"); + c2!(expr, [ [(); 0] ], "[(); 0]", "[() ; 0]"); // ExprKind::Paren - assert_eq!(stringify_expr!((expr)), "(expr)"); + c1!(expr, [ (expr) ], "(expr)"); // ExprKind::Try - assert_eq!(stringify_expr!(expr?), "expr?"); + c2!(expr, [ expr? ], "expr?", "expr ?"); // ExprKind::Yield - assert_eq!(stringify_expr!(yield), "yield"); - assert_eq!(stringify_expr!(yield true), "yield true"); + c1!(expr, [ yield ], "yield"); + c1!(expr, [ yield true ], "yield true"); } #[test] fn test_item() { // ItemKind::ExternCrate - assert_eq!( - stringify_item!( - extern crate std; - ), - "extern crate std;", - ); - assert_eq!( - stringify_item!( - pub extern crate self as std; - ), + c2!(item, [ extern crate std; ], "extern crate std;", "extern crate std ;"); + c2!(item, + [ pub extern crate self as std; ], "pub extern crate self as std;", + "pub extern crate self as std ;" ); // ItemKind::Use - assert_eq!( - stringify_item!( - pub use crate::{a, b::c}; - ), + c2!(item, + [ pub use crate::{a, b::c}; ], "pub use crate::{a, b::c};", + "pub use crate :: { a, b :: c } ;" ); // ItemKind::Static - assert_eq!( - stringify_item!( - pub static S: () = {}; - ), - "pub static S: () = {};", - ); - assert_eq!( - stringify_item!( - static mut S: () = {}; - ), - "static mut S: () = {};", - ); - assert_eq!( - stringify_item!( - static S: (); - ), - "static S: ();", - ); - assert_eq!( - stringify_item!( - static mut S: (); - ), - "static mut S: ();", - ); + c2!(item, [ pub static S: () = {}; ], "pub static S: () = {};", "pub static S : () = {} ;"); + c2!(item, [ static mut S: () = {}; ], "static mut S: () = {};", "static mut S : () = {} ;"); + c2!(item, [ static S: (); ], "static S: ();", "static S : () ;"); + c2!(item, [ static mut S: (); ], "static mut S: ();", "static mut S : () ;"); // ItemKind::Const - assert_eq!( - stringify_item!( - pub const S: () = {}; - ), - "pub const S: () = {};", - ); - assert_eq!( - stringify_item!( - const S: (); - ), - "const S: ();", - ); + c2!(item, [ pub const S: () = {}; ], "pub const S: () = {};", "pub const S : () = {} ;"); + c2!(item, [ const S: (); ], "const S: ();", "const S : () ;"); // ItemKind::Fn - assert_eq!( - stringify_item!( - pub default const async unsafe extern "C" fn f() {} - ), - "pub default const async unsafe extern \"C\" fn f() {}", + c1!(item, + [ pub default const async unsafe extern "C" fn f() {} ], + "pub default const async unsafe extern \"C\" fn f() {}" ); // ItemKind::Mod - assert_eq!( - stringify_item!( - pub mod m; - ), - "pub mod m;", - ); - assert_eq!( - stringify_item!( - mod m {} - ), - "mod m {}", - ); - assert_eq!( - stringify_item!( - unsafe mod m; - ), - "unsafe mod m;", - ); - assert_eq!( - stringify_item!( - unsafe mod m {} - ), - "unsafe mod m {}", - ); + c2!(item, [ pub mod m; ], "pub mod m;", "pub mod m ;"); + c1!(item, [ mod m {} ], "mod m {}"); + c2!(item, [ unsafe mod m; ], "unsafe mod m;", "unsafe mod m ;"); + c1!(item, [ unsafe mod m {} ], "unsafe mod m {}"); // ItemKind::ForeignMod - assert_eq!( - stringify_item!( - extern "C" {} - ), - "extern \"C\" {}", - ); + c1!(item, [ extern "C" {} ], "extern \"C\" {}"); #[rustfmt::skip] - assert_eq!( - stringify_item!( - pub extern "C" {} - ), - "extern \"C\" {}", + c2!(item, + [ pub extern "C" {} ], + "extern \"C\" {}", // ?? + "pub extern \"C\" {}" ); - assert_eq!( - stringify_item!( - unsafe extern "C++" {} - ), - "unsafe extern \"C++\" {}", + c1!(item, + [ unsafe extern "C++" {} ], + "unsafe extern \"C++\" {}" ); // ItemKind::TyAlias #[rustfmt::skip] - assert_eq!( - stringify_item!( + c2!(item, + [ pub default type Type<'a>: Bound where Self: 'a, = T; - ), + ], "pub default type Type<'a>: Bound where Self: 'a = T;", + "pub default type Type < 'a > : Bound where Self : 'a, = T ;" ); // ItemKind::Enum - assert_eq!( - stringify_item!( - pub enum Void {} - ), - "pub enum Void {}", - ); - assert_eq!( - stringify_item!( + c1!(item, [ pub enum Void {} ], "pub enum Void {}"); + c1!(item, + [ enum Empty { Unit, Tuple(), Struct {}, } - ), - "enum Empty { Unit, Tuple(), Struct {}, }", + ], + "enum Empty { Unit, Tuple(), Struct {}, }" ); - assert_eq!( - stringify_item!( + c2!(item, + [ enum Enum where T: 'a, @@ -500,7 +421,7 @@ fn test_item() { Tuple(T), Struct { t: T }, } - ), + ], "enum Enum where T: 'a {\n\ \x20 Unit,\n\ \x20 Tuple(T),\n\ @@ -508,378 +429,330 @@ fn test_item() { \x20 t: T,\n\ \x20 },\n\ }", + "enum Enum < T > where T : 'a, { Unit, Tuple(T), Struct { t : T }, }" ); // ItemKind::Struct - assert_eq!( - stringify_item!( - pub struct Unit; - ), - "pub struct Unit;", - ); - assert_eq!( - stringify_item!( - struct Tuple(); - ), - "struct Tuple();", - ); - assert_eq!( - stringify_item!( - struct Tuple(T); - ), - "struct Tuple(T);", - ); - assert_eq!( - stringify_item!( - struct Struct {} - ), - "struct Struct {}", - ); - assert_eq!( - stringify_item!( + c2!(item, [ pub struct Unit; ], "pub struct Unit;", "pub struct Unit ;"); + c2!(item, [ struct Tuple(); ], "struct Tuple();", "struct Tuple() ;"); + c2!(item, [ struct Tuple(T); ], "struct Tuple(T);", "struct Tuple(T) ;"); + c1!(item, [ struct Struct {} ], "struct Struct {}"); + c2!(item, + [ struct Struct where T: 'a, { t: T, } - ), + ], "struct Struct where T: 'a {\n\ \x20 t: T,\n\ }", + "struct Struct < T > where T : 'a, { t : T, }" ); // ItemKind::Union - assert_eq!( - stringify_item!( - pub union Union {} - ), - "pub union Union {}", - ); - assert_eq!( - stringify_item!( + c1!(item, [ pub union Union {} ], "pub union Union {}"); + c2!(item, + [ union Union where T: 'a { t: T, } - ), + ], "union Union where T: 'a {\n\ \x20 t: T,\n\ }", + "union Union < T > where T : 'a { t : T, }" ); // ItemKind::Trait - assert_eq!( - stringify_item!( - pub unsafe auto trait Send {} - ), - "pub unsafe auto trait Send {}", - ); - assert_eq!( - stringify_item!( + c1!(item, [ pub unsafe auto trait Send {} ], "pub unsafe auto trait Send {}"); + c2!(item, + [ trait Trait<'a>: Sized where Self: 'a, { } - ), + ], "trait Trait<'a>: Sized where Self: 'a {}", + "trait Trait < 'a > : Sized where Self : 'a, {}" ); // ItemKind::TraitAlias - assert_eq!( - stringify_item!( - pub trait Trait = Sized where T: 'a; - ), + c2!(item, + [ pub trait Trait = Sized where T: 'a; ], "pub trait Trait = Sized where T: 'a;", + "pub trait Trait < T > = Sized where T : 'a ;" ); // ItemKind::Impl - assert_eq!( - stringify_item!( - pub impl Struct {} - ), - "pub impl Struct {}", - ); - assert_eq!( - stringify_item!( - impl Struct {} - ), - "impl Struct {}", - ); - assert_eq!( - stringify_item!( - pub impl Trait for Struct {} - ), - "pub impl Trait for Struct {}", - ); - assert_eq!( - stringify_item!( - impl const Trait for T {} - ), + c1!(item, [ pub impl Struct {} ], "pub impl Struct {}"); + c2!(item, [ impl Struct {} ], "impl Struct {}", "impl < T > Struct < T > {}"); + c1!(item, [ pub impl Trait for Struct {} ], "pub impl Trait for Struct {}"); + c2!(item, + [ impl const Trait for T {} ], "impl const Trait for T {}", + "impl < T > const Trait for T {}" ); - assert_eq!( - stringify_item!( - impl ~const Struct {} - ), - "impl ~const Struct {}", - ); + c2!(item, [ impl ~const Struct {} ], "impl ~const Struct {}", "impl ~ const Struct {}"); // ItemKind::MacCall - assert_eq!(stringify_item!(mac!(...);), "mac!(...);"); - assert_eq!(stringify_item!(mac![...];), "mac![...];"); - assert_eq!(stringify_item!(mac! { ... }), "mac! { ... }"); + c2!(item, [ mac!(...); ], "mac!(...);", "mac! (...) ;"); + c2!(item, [ mac![...]; ], "mac![...];", "mac! [...] ;"); + c1!(item, [ mac! { ... } ], "mac! { ... }"); // ItemKind::MacroDef - assert_eq!( - stringify_item!( + c1!(item, + [ macro_rules! stringify { () => {}; } - ), - "macro_rules! stringify { () => {} ; }", // FIXME + ], + "macro_rules! stringify { () => {} ; }" ); - assert_eq!( - stringify_item!( - pub macro stringify() {} - ), - "pub macro stringify { () => {} }", + c2!(item, + [ pub macro stringify() {} ], + "pub macro stringify { () => {} }", // ?? + "pub macro stringify() {}" ); } #[test] fn test_meta() { - assert_eq!(stringify_meta!(k), "k"); - assert_eq!(stringify_meta!(k = "v"), "k = \"v\""); - assert_eq!(stringify_meta!(list(k1, k2 = "v")), "list(k1, k2 = \"v\")"); - assert_eq!(stringify_meta!(serde::k), "serde::k"); + c1!(meta, [ k ], "k"); + c1!(meta, [ k = "v" ], "k = \"v\""); + c1!(meta, [ list(k1, k2 = "v") ], "list(k1, k2 = \"v\")"); + c2!(meta, [ serde::k ], "serde::k", "serde :: k"); } #[test] fn test_pat() { // PatKind::Wild - assert_eq!(stringify_pat!(_), "_"); + c1!(pat, [ _ ], "_"); // PatKind::Ident - assert_eq!(stringify_pat!(_x), "_x"); - assert_eq!(stringify_pat!(ref _x), "ref _x"); - assert_eq!(stringify_pat!(mut _x), "mut _x"); - assert_eq!(stringify_pat!(ref mut _x), "ref mut _x"); - assert_eq!(stringify_pat!(ref mut _x @ _), "ref mut _x @ _"); + c1!(pat, [ _x ], "_x"); + c1!(pat, [ ref _x ], "ref _x"); + c1!(pat, [ mut _x ], "mut _x"); + c1!(pat, [ ref mut _x ], "ref mut _x"); + c1!(pat, [ ref mut _x @ _ ], "ref mut _x @ _"); // PatKind::Struct - assert_eq!(stringify_pat!(Struct {}), "Struct {}"); - assert_eq!(stringify_pat!(Struct:: {}), "Struct:: {}"); - assert_eq!(stringify_pat!(Struct::<'static> {}), "Struct::<'static> {}"); - assert_eq!(stringify_pat!(Struct { x }), "Struct { x }"); - assert_eq!(stringify_pat!(Struct { x: _x }), "Struct { x: _x }"); - assert_eq!(stringify_pat!(Struct { .. }), "Struct { .. }"); - assert_eq!(stringify_pat!(Struct { x, .. }), "Struct { x, .. }"); - assert_eq!(stringify_pat!(Struct { x: _x, .. }), "Struct { x: _x, .. }"); + c1!(pat, [ Struct {} ], "Struct {}"); + c2!(pat, [ Struct:: {} ], "Struct:: {}", "Struct :: < u8 > {}"); + c2!(pat, [ Struct::<'static> {} ], "Struct::<'static> {}", "Struct :: < 'static > {}"); + c1!(pat, [ Struct { x } ], "Struct { x }"); + c2!(pat, [ Struct { x: _x } ], "Struct { x: _x }", "Struct { x : _x }"); + c1!(pat, [ Struct { .. } ], "Struct { .. }"); + c1!(pat, [ Struct { x, .. } ], "Struct { x, .. }"); + c2!(pat, [ Struct { x: _x, .. } ], "Struct { x: _x, .. }", "Struct { x : _x, .. }"); #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5151 - assert_eq!( - stringify_pat!(::Type {}), + c2!(pat, + [ ::Type {} ], "::Type {}", + "< Struct as Trait > :: Type {}" ); // PatKind::TupleStruct - assert_eq!(stringify_pat!(Tuple()), "Tuple()"); - assert_eq!(stringify_pat!(Tuple::()), "Tuple::()"); - assert_eq!(stringify_pat!(Tuple::<'static>()), "Tuple::<'static>()"); - assert_eq!(stringify_pat!(Tuple(x)), "Tuple(x)"); - assert_eq!(stringify_pat!(Tuple(..)), "Tuple(..)"); - assert_eq!(stringify_pat!(Tuple(x, ..)), "Tuple(x, ..)"); - assert_eq!(stringify_pat!(::Type()), "::Type()"); + c1!(pat, [ Tuple() ], "Tuple()"); + c2!(pat, [ Tuple::() ], "Tuple::()", "Tuple :: < u8 > ()"); + c2!(pat, [ Tuple::<'static>() ], "Tuple::<'static>()", "Tuple :: < 'static > ()"); + c1!(pat, [ Tuple(x) ], "Tuple(x)"); + c1!(pat, [ Tuple(..) ], "Tuple(..)"); + c1!(pat, [ Tuple(x, ..) ], "Tuple(x, ..)"); + c2!(pat, + [ ::Type() ], + "::Type()", + "< Struct as Trait > :: Type()" + ); // PatKind::Or - assert_eq!(stringify_pat!(true | false), "true | false"); - assert_eq!(stringify_pat!(| true), "true"); - assert_eq!(stringify_pat!(|true| false), "true | false"); + c1!(pat, [ true | false ], "true | false"); + c2!(pat, [ | true ], "true", "| true"); + c2!(pat, [ |true| false ], "true | false", "| true | false"); // PatKind::Path - assert_eq!(stringify_pat!(crate::Path), "crate::Path"); - assert_eq!(stringify_pat!(Path::), "Path::"); - assert_eq!(stringify_pat!(Path::<'static>), "Path::<'static>"); - assert_eq!(stringify_pat!(::Type), "::Type"); + c2!(pat, [ crate::Path ], "crate::Path", "crate :: Path"); + c2!(pat, [ Path:: ], "Path::", "Path :: < u8 >"); + c2!(pat, [ Path::<'static> ], "Path::<'static>", "Path :: < 'static >"); + c2!(pat, [ ::Type ], "::Type", "< Struct as Trait > :: Type"); // PatKind::Tuple - assert_eq!(stringify_pat!(()), "()"); - assert_eq!(stringify_pat!((true,)), "(true,)"); - assert_eq!(stringify_pat!((true, false)), "(true, false)"); + c1!(pat, [ () ], "()"); + c1!(pat, [ (true,) ], "(true,)"); + c1!(pat, [ (true, false) ], "(true, false)"); // PatKind::Box - assert_eq!(stringify_pat!(box pat), "box pat"); + c1!(pat, [ box pat ], "box pat"); // PatKind::Ref - assert_eq!(stringify_pat!(&pat), "&pat"); - assert_eq!(stringify_pat!(&mut pat), "&mut pat"); + c2!(pat, [ &pat ], "&pat", "& pat"); + c2!(pat, [ &mut pat ], "&mut pat", "& mut pat"); // PatKind::Lit - assert_eq!(stringify_pat!(1_000_i8), "1_000_i8"); + c1!(pat, [ 1_000_i8 ], "1_000_i8"); // PatKind::Range - assert_eq!(stringify_pat!(..1), "..1"); - assert_eq!(stringify_pat!(0..), "0.."); - assert_eq!(stringify_pat!(0..1), "0..1"); - assert_eq!(stringify_pat!(0..=1), "0..=1"); - assert_eq!(stringify_pat!(-2..=-1), "-2..=-1"); + c2!(pat, [ ..1 ], "..1", ".. 1"); + c2!(pat, [ 0.. ], "0..", "0 .."); + c2!(pat, [ 0..1 ], "0..1", "0 .. 1"); + c2!(pat, [ 0..=1 ], "0..=1", "0 ..= 1"); + c2!(pat, [ -2..=-1 ], "-2..=-1", "- 2 ..= - 1"); // PatKind::Slice - assert_eq!(stringify_pat!([]), "[]"); - assert_eq!(stringify_pat!([true]), "[true]"); - assert_eq!(stringify_pat!([true,]), "[true]"); - assert_eq!(stringify_pat!([true, false]), "[true, false]"); + c1!(pat, [ [] ], "[]"); + c1!(pat, [ [true] ], "[true]"); + c2!(pat, [ [true,] ], "[true]", "[true,]"); + c1!(pat, [ [true, false] ], "[true, false]"); // PatKind::Rest - assert_eq!(stringify_pat!(..), ".."); + c1!(pat, [ .. ], ".."); // PatKind::Paren - assert_eq!(stringify_pat!((pat)), "(pat)"); + c1!(pat, [ (pat) ], "(pat)"); // PatKind::MacCall - assert_eq!(stringify_pat!(mac!(...)), "mac!(...)"); - assert_eq!(stringify_pat!(mac![...]), "mac![...]"); - assert_eq!(stringify_pat!(mac! { ... }), "mac! { ... }"); + c2!(pat, [ mac!(...) ], "mac!(...)", "mac! (...)"); + c2!(pat, [ mac![...] ], "mac![...]", "mac! [...]"); + c1!(pat, [ mac! { ... } ], "mac! { ... }"); } #[test] fn test_path() { - assert_eq!(stringify_path!(thing), "thing"); - assert_eq!(stringify_path!(m::thing), "m::thing"); - assert_eq!(stringify_path!(self::thing), "self::thing"); - assert_eq!(stringify_path!(crate::thing), "crate::thing"); - assert_eq!(stringify_path!(Self::thing), "Self::thing"); - assert_eq!(stringify_path!(Self<'static>), "Self<'static>"); - assert_eq!(stringify_path!(Self::<'static>), "Self<'static>"); - assert_eq!(stringify_path!(Self()), "Self()"); - assert_eq!(stringify_path!(Self() -> ()), "Self() -> ()"); + c1!(path, [ thing ], "thing"); + c2!(path, [ m::thing ], "m::thing", "m :: thing"); + c2!(path, [ self::thing ], "self::thing", "self :: thing"); + c2!(path, [ crate::thing ], "crate::thing", "crate :: thing"); + c2!(path, [ Self::thing ], "Self::thing", "Self :: thing"); + c2!(path, [ Self<'static> ], "Self<'static>", "Self < 'static >"); + c2!(path, [ Self::<'static> ], "Self<'static>", "Self :: < 'static >"); + c1!(path, [ Self() ], "Self()"); + c1!(path, [ Self() -> () ], "Self() -> ()"); } #[test] fn test_stmt() { // StmtKind::Local - assert_eq!(stringify_stmt!(let _), "let _;"); - assert_eq!(stringify_stmt!(let x = true), "let x = true;"); - assert_eq!(stringify_stmt!(let x: bool = true), "let x: bool = true;"); + c2!(stmt, [ let _ ], "let _;", "let _"); + c2!(stmt, [ let x = true ], "let x = true;", "let x = true"); + c2!(stmt, [ let x: bool = true ], "let x: bool = true;", "let x : bool = true"); // StmtKind::Item - assert_eq!( - stringify_stmt!( - struct S; - ), - "struct S;", - ); + c2!(stmt, [ struct S; ], "struct S;", "struct S ;"); // StmtKind::Expr - assert_eq!(stringify_stmt!(loop {}), "loop {}"); + c1!(stmt, [ loop {} ], "loop {}"); // StmtKind::Semi - assert_eq!(stringify_stmt!(1 + 1), "1 + 1;"); + c2!(stmt, [ 1 + 1 ], "1 + 1;", "1 + 1"); // StmtKind::Empty - assert_eq!(stringify_stmt!(;), ";"); + c1!(stmt, [ ; ], ";"); // StmtKind::MacCall - assert_eq!(stringify_stmt!(mac!(...)), "mac!(...)"); - assert_eq!(stringify_stmt!(mac![...]), "mac![...]"); - assert_eq!(stringify_stmt!(mac! { ... }), "mac! { ... }"); + c2!(stmt, [ mac!(...) ], "mac!(...)", "mac! (...)"); + c2!(stmt, [ mac![...] ], "mac![...]", "mac! [...]"); + c1!(stmt, [ mac! { ... } ], "mac! { ... }"); } #[test] fn test_ty() { // TyKind::Slice - assert_eq!(stringify_ty!([T]), "[T]"); + c1!(ty, [ [T] ], "[T]"); // TyKind::Array - assert_eq!(stringify_ty!([T; 0]), "[T; 0]"); + c2!(ty, [ [T; 0] ], "[T; 0]", "[T ; 0]"); // TyKind::Ptr - assert_eq!(stringify_ty!(*const T), "*const T"); - assert_eq!(stringify_ty!(*mut T), "*mut T"); + c2!(ty, [ *const T ], "*const T", "* const T"); + c2!(ty, [ *mut T ], "*mut T", "* mut T"); // TyKind::Ref - assert_eq!(stringify_ty!(&T), "&T"); - assert_eq!(stringify_ty!(&mut T), "&mut T"); - assert_eq!(stringify_ty!(&'a T), "&'a T"); - assert_eq!(stringify_ty!(&'a mut T), "&'a mut T"); + c2!(ty, [ &T ], "&T", "& T"); + c2!(ty, [ &mut T ], "&mut T", "& mut T"); + c2!(ty, [ &'a T ], "&'a T", "& 'a T"); + c2!(ty, [ &'a mut T ], "&'a mut T", "& 'a mut T"); // TyKind::BareFn - assert_eq!(stringify_ty!(fn()), "fn()"); - assert_eq!(stringify_ty!(fn() -> ()), "fn() -> ()"); - assert_eq!(stringify_ty!(fn(u8)), "fn(u8)"); - assert_eq!(stringify_ty!(fn(x: u8)), "fn(x: u8)"); + c1!(ty, [ fn() ], "fn()"); + c1!(ty, [ fn() -> () ], "fn() -> ()"); + c1!(ty, [ fn(u8) ], "fn(u8)"); + c2!(ty, [ fn(x: u8) ], "fn(x: u8)", "fn(x : u8)"); #[rustfmt::skip] - assert_eq!(stringify_ty!(for<> fn()), "fn()"); - assert_eq!(stringify_ty!(for<'a> fn()), "for<'a> fn()"); + c2!(ty, [ for<> fn() ], "fn()", "for < > fn()"); + c2!(ty, [ for<'a> fn() ], "for<'a> fn()", "for < 'a > fn()"); // TyKind::Never - assert_eq!(stringify_ty!(!), "!"); + c1!(ty, [ ! ], "!"); // TyKind::Tup - assert_eq!(stringify_ty!(()), "()"); - assert_eq!(stringify_ty!((T,)), "(T,)"); - assert_eq!(stringify_ty!((T, U)), "(T, U)"); + c1!(ty, [ () ], "()"); + c1!(ty, [ (T,) ], "(T,)"); + c1!(ty, [ (T, U) ], "(T, U)"); // TyKind::Path - assert_eq!(stringify_ty!(T), "T"); - assert_eq!(stringify_ty!(Ref<'a>), "Ref<'a>"); - assert_eq!(stringify_ty!(PhantomData), "PhantomData"); - assert_eq!(stringify_ty!(PhantomData::), "PhantomData"); - assert_eq!(stringify_ty!(Fn() -> !), "Fn() -> !"); - assert_eq!(stringify_ty!(Fn(u8) -> !), "Fn(u8) -> !"); - assert_eq!(stringify_ty!(::Type), "::Type"); + c1!(ty, [ T ], "T"); + c2!(ty, [ Ref<'a> ], "Ref<'a>", "Ref < 'a >"); + c2!(ty, [ PhantomData ], "PhantomData", "PhantomData < T >"); + c2!(ty, [ PhantomData:: ], "PhantomData", "PhantomData :: < T >"); + c2!(ty, [ Fn() -> ! ], "Fn() -> !", "Fn() ->!"); + c2!(ty, [ Fn(u8) -> ! ], "Fn(u8) -> !", "Fn(u8) ->!"); // FIXME + c2!(ty, [ ::Type ], "::Type", "< Struct as Trait > :: Type"); // TyKind::TraitObject - assert_eq!(stringify_ty!(dyn Send), "dyn Send"); - assert_eq!(stringify_ty!(dyn Send + 'a), "dyn Send + 'a"); - assert_eq!(stringify_ty!(dyn 'a + Send), "dyn 'a + Send"); - assert_eq!(stringify_ty!(dyn ?Sized), "dyn ?Sized"); - assert_eq!(stringify_ty!(dyn ~const Clone), "dyn ~const Clone"); - assert_eq!(stringify_ty!(dyn for<'a> Send), "dyn for<'a> Send"); + c1!(ty, [ dyn Send ], "dyn Send"); + c1!(ty, [ dyn Send + 'a ], "dyn Send + 'a"); + c1!(ty, [ dyn 'a + Send ], "dyn 'a + Send"); + c2!(ty, [ dyn ?Sized ], "dyn ?Sized", "dyn ? Sized"); + c2!(ty, [ dyn ~const Clone ], "dyn ~const Clone", "dyn ~ const Clone"); + c2!(ty, [ dyn for<'a> Send ], "dyn for<'a> Send", "dyn for < 'a > Send"); // TyKind::ImplTrait - assert_eq!(stringify_ty!(impl Send), "impl Send"); - assert_eq!(stringify_ty!(impl Send + 'a), "impl Send + 'a"); - assert_eq!(stringify_ty!(impl 'a + Send), "impl 'a + Send"); - assert_eq!(stringify_ty!(impl ?Sized), "impl ?Sized"); - assert_eq!(stringify_ty!(impl ~const Clone), "impl ~const Clone"); - assert_eq!(stringify_ty!(impl for<'a> Send), "impl for<'a> Send"); + c1!(ty, [ impl Send ], "impl Send"); + c1!(ty, [ impl Send + 'a ], "impl Send + 'a"); + c1!(ty, [ impl 'a + Send ], "impl 'a + Send"); + c2!(ty, [ impl ?Sized ], "impl ?Sized", "impl ? Sized"); + c2!(ty, [ impl ~const Clone ], "impl ~const Clone", "impl ~ const Clone"); + c2!(ty, [ impl for<'a> Send ], "impl for<'a> Send", "impl for < 'a > Send"); // TyKind::Paren - assert_eq!(stringify_ty!((T)), "(T)"); + c1!(ty, [ (T) ], "(T)"); // TyKind::Infer - assert_eq!(stringify_ty!(_), "_"); + c1!(ty, [ _ ], "_"); // TyKind::MacCall - assert_eq!(stringify_ty!(mac!(...)), "mac!(...)"); - assert_eq!(stringify_ty!(mac![...]), "mac![...]"); - assert_eq!(stringify_ty!(mac! { ... }), "mac! { ... }"); + c2!(ty, [ mac!(...) ], "mac!(...)", "mac! (...)"); + c2!(ty, [ mac![...] ], "mac![...]", "mac! [...]"); + c1!(ty, [ mac! { ... } ], "mac! { ... }"); } #[test] fn test_vis() { // VisibilityKind::Public - assert_eq!(stringify_vis!(pub), "pub "); + c2!(vis, [ pub ], "pub ", "pub"); // VisibilityKind::Restricted - assert_eq!(stringify_vis!(pub(crate)), "pub(crate) "); - assert_eq!(stringify_vis!(pub(self)), "pub(self) "); - assert_eq!(stringify_vis!(pub(super)), "pub(super) "); - assert_eq!(stringify_vis!(pub(in crate)), "pub(in crate) "); - assert_eq!(stringify_vis!(pub(in self)), "pub(in self) "); - assert_eq!(stringify_vis!(pub(in super)), "pub(in super) "); - assert_eq!(stringify_vis!(pub(in path::to)), "pub(in path::to) "); - assert_eq!(stringify_vis!(pub(in ::path::to)), "pub(in ::path::to) "); - assert_eq!(stringify_vis!(pub(in self::path::to)), "pub(in self::path::to) "); - assert_eq!(stringify_vis!(pub(in super::path::to)), "pub(in super::path::to) "); + c2!(vis, [ pub(crate) ], "pub(crate) ", "pub(crate)"); + c2!(vis, [ pub(self) ], "pub(self) ", "pub(self)"); + c2!(vis, [ pub(super) ], "pub(super) ", "pub(super)"); + c2!(vis, [ pub(in crate) ], "pub(in crate) ", "pub(in crate)"); + c2!(vis, [ pub(in self) ], "pub(in self) ", "pub(in self)"); + c2!(vis, [ pub(in super) ], "pub(in super) ", "pub(in super)"); + c2!(vis, [ pub(in path::to) ], "pub(in path::to) ", "pub(in path :: to)"); + c2!(vis, [ pub(in ::path::to) ], "pub(in ::path::to) ", "pub(in :: path :: to)"); + c2!(vis, [ pub(in self::path::to) ], "pub(in self::path::to) ", "pub(in self :: path :: to)"); + c2!(vis, + [ pub(in super::path::to) ], + "pub(in super::path::to) ", + "pub(in super :: path :: to)" + ); // VisibilityKind::Inherited - // Directly calling `stringify_vis!()` does not work. - macro_rules! stringify_inherited_vis { - ($vis:vis struct) => { - stringify_vis!($vis) - }; - } - assert_eq!(stringify_inherited_vis!(struct), ""); + // This one is different because directly calling `vis!` does not work. + macro_rules! inherited_vis { ($vis:vis struct) => { vis!($vis) }; } + assert_eq!(inherited_vis!(struct), ""); + assert_eq!(stringify!(), ""); } From 2e2e7806ab3ad23eb71663e3845c9f2b483b8cc6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 20 Oct 2023 09:55:29 +1100 Subject: [PATCH 06/18] Augment `stringify.rs` test. By adding tests (or placeholders, or comments) for missing AST variants. --- compiler/rustc_ast/src/ast.rs | 7 +++++ tests/ui/macros/stringify.rs | 49 +++++++++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index e94e26a63bc6f..e09ba03d881a6 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -734,6 +734,8 @@ pub enum RangeSyntax { } /// All the different flavors of pattern that Rust recognizes. +// +// Adding a new variant? Please update `test_pat` in `tests/ui/macros/stringify.rs`. #[derive(Clone, Encodable, Decodable, Debug)] pub enum PatKind { /// Represents a wildcard pattern (`_`). @@ -967,6 +969,7 @@ impl Stmt { } } +// Adding a new variant? Please update `test_stmt` in `tests/ui/macros/stringify.rs`. #[derive(Clone, Encodable, Decodable, Debug)] pub enum StmtKind { /// A local (let) binding. @@ -1345,6 +1348,7 @@ pub struct StructExpr { pub rest: StructRest, } +// Adding a new variant? Please update `test_expr` in `tests/ui/macros/stringify.rs`. #[derive(Clone, Encodable, Decodable, Debug)] pub enum ExprKind { /// An array (`[a, b, c, d]`) @@ -2015,6 +2019,8 @@ pub struct BareFnTy { } /// The various kinds of type recognized by the compiler. +// +// Adding a new variant? Please update `test_ty` in `tests/ui/macros/stringify.rs`. #[derive(Clone, Encodable, Decodable, Debug)] pub enum TyKind { /// A variable-length slice (`[T]`). @@ -2880,6 +2886,7 @@ pub struct ConstItem { pub expr: Option>, } +// Adding a new variant? Please update `test_item` in `tests/ui/macros/stringify.rs`. #[derive(Clone, Encodable, Decodable, Debug)] pub enum ItemKind { /// An `extern crate` item, with the optional *original* crate name if the crate was renamed. diff --git a/tests/ui/macros/stringify.rs b/tests/ui/macros/stringify.rs index 939b4ce299fc9..0d23cfba7fd6f 100644 --- a/tests/ui/macros/stringify.rs +++ b/tests/ui/macros/stringify.rs @@ -2,17 +2,20 @@ // edition:2021 // compile-flags: --test +#![allow(incomplete_features)] #![feature(async_closure)] #![feature(auto_traits)] #![feature(box_patterns)] #![feature(const_trait_impl)] #![feature(decl_macro)] #![feature(coroutines)] +#![feature(explicit_tail_calls)] #![feature(more_qualified_paths)] #![feature(raw_ref_op)] #![feature(trait_alias)] #![feature(try_blocks)] #![feature(type_ascription)] +#![feature(yeet_expr)] #![deny(unused_macros)] // These macros force the use of AST pretty-printing by converting the input to @@ -79,6 +82,9 @@ fn test_expr() { c2!(expr, [ [true,] ], "[true]", "[true,]"); c1!(expr, [ [true, true] ], "[true, true]"); + // ExprKind::ConstBlock + // FIXME: todo + // ExprKind::Call c1!(expr, [ f() ], "f()"); c2!(expr, [ f::() ], "f::()", "f :: < u8 > ()"); @@ -116,8 +122,10 @@ fn test_expr() { c1!(expr, [ expr as T ], "expr as T"); c2!(expr, [ expr as T ], "expr as T", "expr as T < u8 >"); - // ExprKind::Type - // There is no syntax for type ascription. + // ExprKind::Type: there is no syntax for type ascription. + + // ExprKind::Let + c1!(expr, [ if let Some(a) = b { c } else { d } ], "if let Some(a) = b { c } else { d }"); // ExprKind::If c1!(expr, [ if true {} ], "if true {}"); @@ -265,6 +273,9 @@ fn test_expr() { c2!(expr, [ lo..=hi ], "lo..=hi", "lo ..= hi"); c2!(expr, [ -2..=-1 ], "-2..=-1", "- 2 ..= - 1"); + // ExprKind::Underscore + // FIXME: todo + // ExprKind::Path c1!(expr, [ thing ], "thing"); c2!(expr, [ m::thing ], "m::thing", "m :: thing"); @@ -294,6 +305,10 @@ fn test_expr() { c1!(expr, [ return ], "return"); c1!(expr, [ return true ], "return true"); + // ExprKind::InlineAsm: untestable because this test works pre-expansion. + + // ExprKind::OffsetOf: untestable because this test works pre-expansion. + // ExprKind::MacCall c2!(expr, [ mac!(...) ], "mac!(...)", "mac! (...)"); c2!(expr, [ mac![...] ], "mac![...]", "mac! [...]"); @@ -332,6 +347,20 @@ fn test_expr() { // ExprKind::Yield c1!(expr, [ yield ], "yield"); c1!(expr, [ yield true ], "yield true"); + + // ExprKind::Yeet + c1!(expr, [ do yeet ], "do yeet"); + c1!(expr, [ do yeet 0 ], "do yeet 0"); + + // ExprKind::Become + // FIXME: todo + + // ExprKind::IncludedBytes + // FIXME: todo + + // ExprKind::FormatArgs: untestable because this test works pre-expansion. + + // ExprKind::Err: untestable. } #[test] @@ -386,6 +415,8 @@ fn test_item() { "unsafe extern \"C++\" {}" ); + // ItemKind::GlobalAsm: untestable because this test works pre-expansion. + // ItemKind::TyAlias #[rustfmt::skip] c2!(item, @@ -641,6 +672,7 @@ fn test_stmt() { // StmtKind::Item c2!(stmt, [ struct S; ], "struct S;", "struct S ;"); + c1!(stmt, [ struct S {} ], "struct S {}"); // StmtKind::Expr c1!(stmt, [ loop {} ], "loop {}"); @@ -692,6 +724,10 @@ fn test_ty() { c1!(ty, [ (T,) ], "(T,)"); c1!(ty, [ (T, U) ], "(T, U)"); + // TyKind::AnonStruct: untestable in isolation. + + // TyKind::AnonUnion: untestable in isolation. + // TyKind::Path c1!(ty, [ T ], "T"); c2!(ty, [ Ref<'a> ], "Ref<'a>", "Ref < 'a >"); @@ -720,13 +756,22 @@ fn test_ty() { // TyKind::Paren c1!(ty, [ (T) ], "(T)"); + // TyKind::Typeof: unused for now. + // TyKind::Infer c1!(ty, [ _ ], "_"); + + // TyKind::ImplicitSelf: there is no syntax for this. // TyKind::MacCall c2!(ty, [ mac!(...) ], "mac!(...)", "mac! (...)"); c2!(ty, [ mac![...] ], "mac![...]", "mac! [...]"); c1!(ty, [ mac! { ... } ], "mac! { ... }"); + + // TyKind::Err: untestable. + + // TyKind::CVarArgs + // FIXME: todo } #[test] From c1800fbb175f212f93b9f7d57dd2133508a80c01 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 24 Oct 2023 15:04:46 +1100 Subject: [PATCH 07/18] Augment `stringify.rs` test some more. By making some case more complex, adding some new cases, tweaking formatting, and removing unnecessary `rustfmt` attributes. --- tests/ui/macros/stringify.rs | 78 ++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/tests/ui/macros/stringify.rs b/tests/ui/macros/stringify.rs index 0d23cfba7fd6f..f1bbd4a621095 100644 --- a/tests/ui/macros/stringify.rs +++ b/tests/ui/macros/stringify.rs @@ -7,8 +7,8 @@ #![feature(auto_traits)] #![feature(box_patterns)] #![feature(const_trait_impl)] -#![feature(decl_macro)] #![feature(coroutines)] +#![feature(decl_macro)] #![feature(explicit_tail_calls)] #![feature(more_qualified_paths)] #![feature(raw_ref_op)] @@ -97,6 +97,7 @@ fn test_expr() { // ExprKind::MethodCall c1!(expr, [ x.f() ], "x.f()"); c2!(expr, [ x.f::() ], "x.f::()", "x.f :: < u8 > ()"); + c2!(expr, [ x.collect::>() ], "x.collect::>()", "x.collect :: < Vec < _ >> ()"); // ExprKind::Tup c1!(expr, [ () ], "()"); @@ -107,6 +108,13 @@ fn test_expr() { // ExprKind::Binary c1!(expr, [ true || false ], "true || false"); c1!(expr, [ true || false && false ], "true || false && false"); + c1!(expr, [ a < 1 && 2 < b && c > 3 && 4 > d ], "a < 1 && 2 < b && c > 3 && 4 > d"); + c2!(expr, [ a & b & !c ], "a & b & !c", "a & b &! c"); // FIXME + c2!(expr, + [ a + b * c - d + -1 * -2 - -3], + "a + b * c - d + -1 * -2 - -3", + "a + b * c - d + - 1 * - 2 - - 3" + ); // ExprKind::Unary c2!(expr, [ *expr ], "*expr", "* expr"); @@ -129,18 +137,12 @@ fn test_expr() { // ExprKind::If c1!(expr, [ if true {} ], "if true {}"); - c1!(expr, - [ if true { - } else { - } ], - "if true {} else {}" - ); - c1!(expr, - [ if let true = true { - } else { - } ], - "if let true = true {} else {}" + c2!(expr, + [ if ::std::blah() { } else { } ], + "if ::std::blah() {} else {}", + "if :: std :: blah() {} else {}" ); + c1!(expr, [ if let true = true {} else {} ], "if let true = true {} else {}"); c1!(expr, [ if true { } else if false { @@ -205,16 +207,8 @@ fn test_expr() { c1!(expr, [ async move || self ], "async move || self"); c1!(expr, [ static || self ], "static || self"); c1!(expr, [ static move || self ], "static move || self"); - #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5149 - c1!(expr, - [ static async || self ], - "static async || self" - ); - #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5149 - c1!(expr, - [ static async move || self ], - "static async move || self" - ); + c1!(expr, [ static async || self ], "static async || self"); + c1!(expr, [ static async move || self ], "static async move || self"); c1!(expr, [ || -> u8 { self } ], "|| -> u8 { self }"); c2!(expr, [ 1 + || {} ], "1 + (|| {})", "1 + || {}"); // AST?? @@ -222,13 +216,7 @@ fn test_expr() { c1!(expr, [ {} ], "{}"); c1!(expr, [ unsafe {} ], "unsafe {}"); c2!(expr, [ 'a: {} ], "'a: {}", "'a : {}"); - c1!(expr, - [ - #[attr] - {} - ], - "#[attr] {}" - ); + c1!(expr, [ #[attr] {} ], "#[attr] {}"); c2!(expr, [ { @@ -316,7 +304,6 @@ fn test_expr() { // ExprKind::Struct c1!(expr, [ Struct {} ], "Struct {}"); - #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5151 c2!(expr, [ ::Type {} ], "::Type {}", @@ -379,6 +366,7 @@ fn test_item() { "pub use crate::{a, b::c};", "pub use crate :: { a, b :: c } ;" ); + c2!(item, [ pub use A::*; ], "pub use A::*;", "pub use A :: * ;"); // ItemKind::Static c2!(item, [ pub static S: () = {}; ], "pub static S: () = {};", "pub static S : () = {} ;"); @@ -395,6 +383,16 @@ fn test_item() { [ pub default const async unsafe extern "C" fn f() {} ], "pub default const async unsafe extern \"C\" fn f() {}" ); + c2!(item, + [ fn g(t: Vec>>) {} ], + "fn g(t: Vec>>) {}", + "fn g < T > (t : Vec < Vec < Vec < T >> >) {}" + ); + c2!(item, + [ fn h<'a>(t: &'a Vec>) {} ], + "fn h<'a>(t: &'a Vec>) {}", + "fn h < 'a > (t : & 'a Vec < Cell < dyn D >>) {}" + ); // ItemKind::Mod c2!(item, [ pub mod m; ], "pub mod m;", "pub mod m ;"); @@ -404,21 +402,16 @@ fn test_item() { // ItemKind::ForeignMod c1!(item, [ extern "C" {} ], "extern \"C\" {}"); - #[rustfmt::skip] c2!(item, [ pub extern "C" {} ], "extern \"C\" {}", // ?? "pub extern \"C\" {}" ); - c1!(item, - [ unsafe extern "C++" {} ], - "unsafe extern \"C++\" {}" - ); + c1!(item, [ unsafe extern "C++" {} ], "unsafe extern \"C++\" {}"); // ItemKind::GlobalAsm: untestable because this test works pre-expansion. // ItemKind::TyAlias - #[rustfmt::skip] c2!(item, [ pub default type Type<'a>: Bound @@ -579,7 +572,6 @@ fn test_pat() { c1!(pat, [ Struct { .. } ], "Struct { .. }"); c1!(pat, [ Struct { x, .. } ], "Struct { x, .. }"); c2!(pat, [ Struct { x: _x, .. } ], "Struct { x: _x, .. }", "Struct { x : _x, .. }"); - #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5151 c2!(pat, [ ::Type {} ], "::Type {}", @@ -669,6 +661,12 @@ fn test_stmt() { c2!(stmt, [ let _ ], "let _;", "let _"); c2!(stmt, [ let x = true ], "let x = true;", "let x = true"); c2!(stmt, [ let x: bool = true ], "let x: bool = true;", "let x : bool = true"); + c2!(stmt, [ let (a, b) = (1, 2) ], "let (a, b) = (1, 2);", "let(a, b) = (1, 2)"); // FIXME + c2!(stmt, + [ let (a, b): (u32, u32) = (1, 2) ], + "let (a, b): (u32, u32) = (1, 2);", + "let(a, b) : (u32, u32) = (1, 2)" + ); // StmtKind::Item c2!(stmt, [ struct S; ], "struct S;", "struct S ;"); @@ -705,14 +703,14 @@ fn test_ty() { c2!(ty, [ &T ], "&T", "& T"); c2!(ty, [ &mut T ], "&mut T", "& mut T"); c2!(ty, [ &'a T ], "&'a T", "& 'a T"); - c2!(ty, [ &'a mut T ], "&'a mut T", "& 'a mut T"); + c2!(ty, [ &'a mut [T] ], "&'a mut [T]", "& 'a mut [T]"); + c2!(ty, [ &A>>> ], "&A>>>", "& A < B < C < D < E >> >>"); // TyKind::BareFn c1!(ty, [ fn() ], "fn()"); c1!(ty, [ fn() -> () ], "fn() -> ()"); c1!(ty, [ fn(u8) ], "fn(u8)"); c2!(ty, [ fn(x: u8) ], "fn(x: u8)", "fn(x : u8)"); - #[rustfmt::skip] c2!(ty, [ for<> fn() ], "fn()", "for < > fn()"); c2!(ty, [ for<'a> fn() ], "for<'a> fn()", "for < 'a > fn()"); @@ -760,7 +758,7 @@ fn test_ty() { // TyKind::Infer c1!(ty, [ _ ], "_"); - + // TyKind::ImplicitSelf: there is no syntax for this. // TyKind::MacCall From 173dcb211a08352900b29968cded2024aee6166d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 25 Oct 2023 11:10:21 -0700 Subject: [PATCH 08/18] Touch up syn parsing in symbols macro --- compiler/rustc_macros/src/symbols.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_macros/src/symbols.rs b/compiler/rustc_macros/src/symbols.rs index 04facbf657d22..46ae1779bbe45 100644 --- a/compiler/rustc_macros/src/symbols.rs +++ b/compiler/rustc_macros/src/symbols.rs @@ -59,10 +59,8 @@ struct Symbol { impl Parse for Symbol { fn parse(input: ParseStream<'_>) -> Result { let name = input.parse()?; - let value = match input.parse::() { - Ok(_) => Some(input.parse()?), - Err(_) => None, - }; + let colon_token: Option = input.parse()?; + let value = if colon_token.is_some() { Some(input.parse()?) } else { None }; Ok(Symbol { name, value }) } From ba17934bc1f6fce2e4f9e5494efdb90508eafecd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 25 Oct 2023 11:12:32 -0700 Subject: [PATCH 09/18] Represent symbol value as enum to prepare for supporting env vars --- compiler/rustc_macros/src/symbols.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_macros/src/symbols.rs b/compiler/rustc_macros/src/symbols.rs index 46ae1779bbe45..4df7eb6310493 100644 --- a/compiler/rustc_macros/src/symbols.rs +++ b/compiler/rustc_macros/src/symbols.rs @@ -53,14 +53,20 @@ impl Parse for Keyword { struct Symbol { name: Ident, - value: Option, + value: Value, +} + +enum Value { + SameAsName, + String(LitStr), } impl Parse for Symbol { fn parse(input: ParseStream<'_>) -> Result { let name = input.parse()?; let colon_token: Option = input.parse()?; - let value = if colon_token.is_some() { Some(input.parse()?) } else { None }; + let value = + if colon_token.is_some() { Value::String(input.parse()?) } else { Value::SameAsName }; Ok(Symbol { name, value }) } @@ -168,8 +174,8 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { for symbol in input.symbols.iter() { let name = &symbol.name; let value = match &symbol.value { - Some(value) => value.value(), - None => name.to_string(), + Value::SameAsName => name.to_string(), + Value::String(lit) => lit.value(), }; check_dup(symbol.name.span(), &value, &mut errors); check_order(symbol.name.span(), &name.to_string(), &mut errors); From 95742ff23cf2283da796f483386367f7fdc06bc6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 25 Oct 2023 11:15:18 -0700 Subject: [PATCH 10/18] Add a Parse impl for symbol Value --- compiler/rustc_macros/src/symbols.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_macros/src/symbols.rs b/compiler/rustc_macros/src/symbols.rs index 4df7eb6310493..c48df8313b23c 100644 --- a/compiler/rustc_macros/src/symbols.rs +++ b/compiler/rustc_macros/src/symbols.rs @@ -65,13 +65,19 @@ impl Parse for Symbol { fn parse(input: ParseStream<'_>) -> Result { let name = input.parse()?; let colon_token: Option = input.parse()?; - let value = - if colon_token.is_some() { Value::String(input.parse()?) } else { Value::SameAsName }; + let value = if colon_token.is_some() { input.parse()? } else { Value::SameAsName }; Ok(Symbol { name, value }) } } +impl Parse for Value { + fn parse(input: ParseStream<'_>) -> Result { + let lit: LitStr = input.parse()?; + Ok(Value::String(lit)) + } +} + struct Input { keywords: Punctuated, symbols: Punctuated, From 8dea49ad7baa4fbb8b2394a6682d601065350134 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 25 Oct 2023 13:20:47 -0700 Subject: [PATCH 11/18] Delete counter from symbols proc macro in favor of hashmap as source of truth --- compiler/rustc_macros/src/symbols.rs | 42 ++++++++++++++++------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_macros/src/symbols.rs b/compiler/rustc_macros/src/symbols.rs index c48df8313b23c..d06f1d0821411 100644 --- a/compiler/rustc_macros/src/symbols.rs +++ b/compiler/rustc_macros/src/symbols.rs @@ -121,6 +121,11 @@ pub fn symbols(input: TokenStream) -> TokenStream { output } +struct Preinterned { + idx: u32, + span_of_name: Span, +} + fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { let mut errors = Errors::default(); @@ -137,17 +142,20 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { let mut keyword_stream = quote! {}; let mut symbols_stream = quote! {}; let mut prefill_stream = quote! {}; - let mut counter = 0u32; - let mut keys = - HashMap::::with_capacity(input.keywords.len() + input.symbols.len() + 10); + let mut entries = HashMap::::with_capacity( + input.keywords.len() + input.symbols.len() + 10, + ); let mut prev_key: Option<(Span, String)> = None; - let mut check_dup = |span: Span, str: &str, errors: &mut Errors| { - if let Some(prev_span) = keys.get(str) { + let mut insert = |span: Span, str: &str, errors: &mut Errors| -> u32 { + if let Some(prev) = entries.get(str) { errors.error(span, format!("Symbol `{str}` is duplicated")); - errors.error(*prev_span, "location of previous definition".to_string()); + errors.error(prev.span_of_name, "location of previous definition".to_string()); + prev.idx } else { - keys.insert(str.to_string(), span); + let idx = u32::try_from(entries.len()).expect("way too many symbols"); + entries.insert(str.to_string(), Preinterned { idx, span_of_name: span }); + idx } }; @@ -166,14 +174,13 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { let name = &keyword.name; let value = &keyword.value; let value_string = value.value(); - check_dup(keyword.name.span(), &value_string, &mut errors); + let idx = insert(keyword.name.span(), &value_string, &mut errors); prefill_stream.extend(quote! { #value, }); keyword_stream.extend(quote! { - pub const #name: Symbol = Symbol::new(#counter); + pub const #name: Symbol = Symbol::new(#idx); }); - counter += 1; } // Generate the listed symbols. @@ -183,32 +190,31 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { Value::SameAsName => name.to_string(), Value::String(lit) => lit.value(), }; - check_dup(symbol.name.span(), &value, &mut errors); + let idx = insert(symbol.name.span(), &value, &mut errors); check_order(symbol.name.span(), &name.to_string(), &mut errors); prefill_stream.extend(quote! { #value, }); symbols_stream.extend(quote! { - pub const #name: Symbol = Symbol::new(#counter); + pub const #name: Symbol = Symbol::new(#idx); }); - counter += 1; } // Generate symbols for the strings "0", "1", ..., "9". - let digits_base = counter; - counter += 10; for n in 0..10 { let n = n.to_string(); - check_dup(Span::call_site(), &n, &mut errors); + insert(Span::call_site(), &n, &mut errors); prefill_stream.extend(quote! { #n, }); } + let symbol_digits_base = entries["0"].idx; + let preinterned_symbols_count = u32::try_from(entries.len()).expect("way too many symbols"); let output = quote! { - const SYMBOL_DIGITS_BASE: u32 = #digits_base; - const PREINTERNED_SYMBOLS_COUNT: u32 = #counter; + const SYMBOL_DIGITS_BASE: u32 = #symbol_digits_base; + const PREINTERNED_SYMBOLS_COUNT: u32 = #preinterned_symbols_count; #[doc(hidden)] #[allow(non_upper_case_globals)] From 726a43c1de78eb21a483896c052b353939531cdb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 25 Oct 2023 13:42:58 -0700 Subject: [PATCH 12/18] Move symbols macro map into a struct --- compiler/rustc_macros/src/symbols.rs | 52 +++++++++++++++++----------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_macros/src/symbols.rs b/compiler/rustc_macros/src/symbols.rs index d06f1d0821411..76f77e6efeca0 100644 --- a/compiler/rustc_macros/src/symbols.rs +++ b/compiler/rustc_macros/src/symbols.rs @@ -126,6 +126,32 @@ struct Preinterned { span_of_name: Span, } +struct Entries { + map: HashMap, +} + +impl Entries { + fn with_capacity(capacity: usize) -> Self { + Entries { map: HashMap::with_capacity(capacity) } + } + + fn insert(&mut self, span: Span, str: &str, errors: &mut Errors) -> u32 { + if let Some(prev) = self.map.get(str) { + errors.error(span, format!("Symbol `{str}` is duplicated")); + errors.error(prev.span_of_name, "location of previous definition".to_string()); + prev.idx + } else { + let idx = self.len(); + self.map.insert(str.to_string(), Preinterned { idx, span_of_name: span }); + idx + } + } + + fn len(&self) -> u32 { + u32::try_from(self.map.len()).expect("way too many symbols") + } +} + fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { let mut errors = Errors::default(); @@ -142,23 +168,9 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { let mut keyword_stream = quote! {}; let mut symbols_stream = quote! {}; let mut prefill_stream = quote! {}; - let mut entries = HashMap::::with_capacity( - input.keywords.len() + input.symbols.len() + 10, - ); + let mut entries = Entries::with_capacity(input.keywords.len() + input.symbols.len() + 10); let mut prev_key: Option<(Span, String)> = None; - let mut insert = |span: Span, str: &str, errors: &mut Errors| -> u32 { - if let Some(prev) = entries.get(str) { - errors.error(span, format!("Symbol `{str}` is duplicated")); - errors.error(prev.span_of_name, "location of previous definition".to_string()); - prev.idx - } else { - let idx = u32::try_from(entries.len()).expect("way too many symbols"); - entries.insert(str.to_string(), Preinterned { idx, span_of_name: span }); - idx - } - }; - let mut check_order = |span: Span, str: &str, errors: &mut Errors| { if let Some((prev_span, ref prev_str)) = prev_key { if str < prev_str { @@ -174,7 +186,7 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { let name = &keyword.name; let value = &keyword.value; let value_string = value.value(); - let idx = insert(keyword.name.span(), &value_string, &mut errors); + let idx = entries.insert(keyword.name.span(), &value_string, &mut errors); prefill_stream.extend(quote! { #value, }); @@ -190,7 +202,7 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { Value::SameAsName => name.to_string(), Value::String(lit) => lit.value(), }; - let idx = insert(symbol.name.span(), &value, &mut errors); + let idx = entries.insert(symbol.name.span(), &value, &mut errors); check_order(symbol.name.span(), &name.to_string(), &mut errors); prefill_stream.extend(quote! { @@ -204,14 +216,14 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { // Generate symbols for the strings "0", "1", ..., "9". for n in 0..10 { let n = n.to_string(); - insert(Span::call_site(), &n, &mut errors); + entries.insert(Span::call_site(), &n, &mut errors); prefill_stream.extend(quote! { #n, }); } - let symbol_digits_base = entries["0"].idx; - let preinterned_symbols_count = u32::try_from(entries.len()).expect("way too many symbols"); + let symbol_digits_base = entries.map["0"].idx; + let preinterned_symbols_count = entries.len(); let output = quote! { const SYMBOL_DIGITS_BASE: u32 = #symbol_digits_base; const PREINTERNED_SYMBOLS_COUNT: u32 = #preinterned_symbols_count; From 65f025333427a881a3fa651a6fefcfa7f6a79354 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 25 Oct 2023 11:20:07 -0700 Subject: [PATCH 13/18] Support environment variable for interned Symbol value --- compiler/rustc_macros/src/lib.rs | 1 + compiler/rustc_macros/src/symbols.rs | 61 ++++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 85829906f4ef5..776ba8f9ca11a 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -4,6 +4,7 @@ #![feature(never_type)] #![feature(proc_macro_diagnostic)] #![feature(proc_macro_span)] +#![feature(proc_macro_tracked_env)] #![allow(rustc::default_hash_types)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] diff --git a/compiler/rustc_macros/src/symbols.rs b/compiler/rustc_macros/src/symbols.rs index 76f77e6efeca0..fd2d56eb29c67 100644 --- a/compiler/rustc_macros/src/symbols.rs +++ b/compiler/rustc_macros/src/symbols.rs @@ -26,7 +26,7 @@ use proc_macro2::{Span, TokenStream}; use quote::quote; use std::collections::HashMap; use syn::parse::{Parse, ParseStream, Result}; -use syn::{braced, punctuated::Punctuated, Ident, LitStr, Token}; +use syn::{braced, punctuated::Punctuated, Expr, Ident, Lit, LitStr, Token}; #[cfg(test)] mod tests; @@ -59,6 +59,7 @@ struct Symbol { enum Value { SameAsName, String(LitStr), + Env(LitStr), } impl Parse for Symbol { @@ -73,8 +74,27 @@ impl Parse for Symbol { impl Parse for Value { fn parse(input: ParseStream<'_>) -> Result { - let lit: LitStr = input.parse()?; - Ok(Value::String(lit)) + let expr: Expr = input.parse()?; + match &expr { + Expr::Lit(expr) => { + if let Lit::Str(lit) = &expr.lit { + return Ok(Value::String(lit.clone())); + } + } + Expr::Macro(expr) => { + if expr.mac.path.is_ident("env") && let Ok(lit) = expr.mac.parse_body() { + return Ok(Value::Env(lit)); + } + } + _ => {} + } + Err(syn::Error::new_spanned( + expr, + concat!( + "unsupported expression for symbol value; implement support for this in ", + file!(), + ), + )) } } @@ -198,12 +218,14 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { // Generate the listed symbols. for symbol in input.symbols.iter() { let name = &symbol.name; + check_order(symbol.name.span(), &name.to_string(), &mut errors); + let value = match &symbol.value { Value::SameAsName => name.to_string(), Value::String(lit) => lit.value(), + Value::Env(_) => continue, }; let idx = entries.insert(symbol.name.span(), &value, &mut errors); - check_order(symbol.name.span(), &name.to_string(), &mut errors); prefill_stream.extend(quote! { #value, @@ -222,6 +244,37 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { }); } + // Symbols whose value comes from an environment variable. It's allowed for + // these to have the same value as another symbol. + for symbol in &input.symbols { + let env_var = match &symbol.value { + Value::Env(lit) => lit, + _ => continue, + }; + + let value = match proc_macro::tracked_env::var(env_var.value()) { + Ok(value) => value, + Err(err) => { + errors.error(symbol.name.span(), err.to_string()); + continue; + } + }; + + let idx = if let Some(prev) = entries.map.get(&value) { + prev.idx + } else { + prefill_stream.extend(quote! { + #value, + }); + entries.insert(symbol.name.span(), &value, &mut errors) + }; + + let name = &symbol.name; + symbols_stream.extend(quote! { + pub const #name: Symbol = Symbol::new(#idx); + }); + } + let symbol_digits_base = entries.map["0"].idx; let preinterned_symbols_count = entries.len(); let output = quote! { From 1078250f483ded63e4526ff379813ab367e2a6c7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 25 Oct 2023 13:46:59 -0700 Subject: [PATCH 14/18] Continue generating other symbols if an expr is not supported --- compiler/rustc_macros/src/symbols.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_macros/src/symbols.rs b/compiler/rustc_macros/src/symbols.rs index fd2d56eb29c67..b808b858357fa 100644 --- a/compiler/rustc_macros/src/symbols.rs +++ b/compiler/rustc_macros/src/symbols.rs @@ -60,6 +60,7 @@ enum Value { SameAsName, String(LitStr), Env(LitStr), + Unsupported(Expr), } impl Parse for Symbol { @@ -88,13 +89,7 @@ impl Parse for Value { } _ => {} } - Err(syn::Error::new_spanned( - expr, - concat!( - "unsupported expression for symbol value; implement support for this in ", - file!(), - ), - )) + Ok(Value::Unsupported(expr)) } } @@ -223,7 +218,17 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { let value = match &symbol.value { Value::SameAsName => name.to_string(), Value::String(lit) => lit.value(), - Value::Env(_) => continue, + Value::Env(_) => continue, // in another loop below + Value::Unsupported(expr) => { + errors.list.push(syn::Error::new_spanned( + expr, + concat!( + "unsupported expression for symbol value; implement support for this in ", + file!(), + ), + )); + continue; + } }; let idx = entries.insert(symbol.name.span(), &value, &mut errors); @@ -249,7 +254,7 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { for symbol in &input.symbols { let env_var = match &symbol.value { Value::Env(lit) => lit, - _ => continue, + Value::SameAsName | Value::String(_) | Value::Unsupported(_) => continue, }; let value = match proc_macro::tracked_env::var(env_var.value()) { From 5563a9ba3d0e7759772c553481896cb803624e79 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 25 Oct 2023 13:52:38 -0700 Subject: [PATCH 15/18] Improve span of env-related errors --- compiler/rustc_macros/src/symbols.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_macros/src/symbols.rs b/compiler/rustc_macros/src/symbols.rs index b808b858357fa..e63bb06eb3f4e 100644 --- a/compiler/rustc_macros/src/symbols.rs +++ b/compiler/rustc_macros/src/symbols.rs @@ -26,7 +26,7 @@ use proc_macro2::{Span, TokenStream}; use quote::quote; use std::collections::HashMap; use syn::parse::{Parse, ParseStream, Result}; -use syn::{braced, punctuated::Punctuated, Expr, Ident, Lit, LitStr, Token}; +use syn::{braced, punctuated::Punctuated, Expr, Ident, Lit, LitStr, Macro, Token}; #[cfg(test)] mod tests; @@ -59,7 +59,7 @@ struct Symbol { enum Value { SameAsName, String(LitStr), - Env(LitStr), + Env(LitStr, Macro), Unsupported(Expr), } @@ -84,7 +84,7 @@ impl Parse for Value { } Expr::Macro(expr) => { if expr.mac.path.is_ident("env") && let Ok(lit) = expr.mac.parse_body() { - return Ok(Value::Env(lit)); + return Ok(Value::Env(lit, expr.mac.clone())); } } _ => {} @@ -218,7 +218,7 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { let value = match &symbol.value { Value::SameAsName => name.to_string(), Value::String(lit) => lit.value(), - Value::Env(_) => continue, // in another loop below + Value::Env(..) => continue, // in another loop below Value::Unsupported(expr) => { errors.list.push(syn::Error::new_spanned( expr, @@ -252,15 +252,15 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { // Symbols whose value comes from an environment variable. It's allowed for // these to have the same value as another symbol. for symbol in &input.symbols { - let env_var = match &symbol.value { - Value::Env(lit) => lit, + let (env_var, expr) = match &symbol.value { + Value::Env(lit, expr) => (lit, expr), Value::SameAsName | Value::String(_) | Value::Unsupported(_) => continue, }; let value = match proc_macro::tracked_env::var(env_var.value()) { Ok(value) => value, Err(err) => { - errors.error(symbol.name.span(), err.to_string()); + errors.list.push(syn::Error::new_spanned(expr, err)); continue; } }; From ac4fa3f2456c0f131982c871d2dffcb7b33daf92 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 25 Oct 2023 14:05:42 -0700 Subject: [PATCH 16/18] Pre-intern a symbol for env!("CFG_RELEASE") --- compiler/rustc_attr/src/builtin.rs | 4 ---- compiler/rustc_passes/src/lib_features.rs | 4 ++-- compiler/rustc_passes/src/stability.rs | 6 +++--- compiler/rustc_span/src/symbol.rs | 1 + 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index bd85483885e36..363ba0d4a815b 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -26,10 +26,6 @@ pub const VERSION_PLACEHOLDER: &str = "CURRENT_RUSTC_VERSION"; pub const CURRENT_RUSTC_VERSION: &str = env!("CFG_RELEASE"); -pub fn rust_version_symbol() -> Symbol { - Symbol::intern(CURRENT_RUSTC_VERSION) -} - pub fn is_builtin_attr(attr: &Attribute) -> bool { attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name)) } diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs index ceb8f58cac0c2..0daa273db6761 100644 --- a/compiler/rustc_passes/src/lib_features.rs +++ b/compiler/rustc_passes/src/lib_features.rs @@ -5,7 +5,7 @@ //! collect them instead. use rustc_ast::Attribute; -use rustc_attr::{rust_version_symbol, VERSION_PLACEHOLDER}; +use rustc_attr::VERSION_PLACEHOLDER; use rustc_hir::intravisit::Visitor; use rustc_middle::hir::nested_filter; use rustc_middle::middle::lib_features::LibFeatures; @@ -59,7 +59,7 @@ impl<'tcx> LibFeatureCollector<'tcx> { if let Some(s) = since && s.as_str() == VERSION_PLACEHOLDER { - since = Some(rust_version_symbol()); + since = Some(sym::env_CFG_RELEASE); } if let Some(feature) = feature { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 41a240fa880d3..7bfb0742b8be9 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -3,8 +3,8 @@ use crate::errors; use rustc_attr::{ - self as attr, rust_version_symbol, ConstStability, Since, Stability, StabilityLevel, Unstable, - UnstableReason, VERSION_PLACEHOLDER, + self as attr, ConstStability, Since, Stability, StabilityLevel, Unstable, UnstableReason, + VERSION_PLACEHOLDER, }; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_hir as hir; @@ -1115,7 +1115,7 @@ fn unnecessary_stable_feature_lint( mut since: Symbol, ) { if since.as_str() == VERSION_PLACEHOLDER { - since = rust_version_symbol(); + since = sym::env_CFG_RELEASE; } tcx.emit_spanned_lint( lint::builtin::STABLE_FEATURES, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index ff61143a12ba2..88d9dab2ba560 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -713,6 +713,7 @@ symbols! { encode, end, env, + env_CFG_RELEASE: env!("CFG_RELEASE"), eprint_macro, eprintln_macro, eq, From c1552dfddd7a98f37e3269002a5624409d8f3481 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 25 Oct 2023 14:45:31 -0700 Subject: [PATCH 17/18] Fix symbols::tests::test_symbols ---- symbols::tests::test_symbols stdout ---- thread 'symbols::tests::test_symbols' panicked at library/proc_macro/src/bridge/client.rs:311:17: procedural macro API is used outside of a procedural macro --- compiler/rustc_macros/src/symbols.rs | 8 ++++++++ compiler/rustc_macros/src/symbols/tests.rs | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_macros/src/symbols.rs b/compiler/rustc_macros/src/symbols.rs index e63bb06eb3f4e..4129712a6b218 100644 --- a/compiler/rustc_macros/src/symbols.rs +++ b/compiler/rustc_macros/src/symbols.rs @@ -257,6 +257,14 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { Value::SameAsName | Value::String(_) | Value::Unsupported(_) => continue, }; + if !proc_macro::is_available() { + errors.error( + Span::call_site(), + "proc_macro::tracked_env is not available in unit test".to_owned(), + ); + break; + } + let value = match proc_macro::tracked_env::var(env_var.value()) { Ok(value) => value, Err(err) => { diff --git a/compiler/rustc_macros/src/symbols/tests.rs b/compiler/rustc_macros/src/symbols/tests.rs index bd0c08a53c4f2..9c53453df5b54 100644 --- a/compiler/rustc_macros/src/symbols/tests.rs +++ b/compiler/rustc_macros/src/symbols/tests.rs @@ -27,7 +27,7 @@ fn test_symbols() { let body_tokens = m.mac.tokens.clone(); - test_symbols_macro(body_tokens, &[]); + test_symbols_macro(body_tokens, &["proc_macro::tracked_env is not available in unit test"]); } fn test_symbols_macro(input: TokenStream, expected_errors: &[&str]) { From 94ecabfc4eea1c58dfd412b00e4a518f0dc87431 Mon Sep 17 00:00:00 2001 From: Carter Hunt Fogelman Date: Thu, 26 Oct 2023 11:10:04 -0700 Subject: [PATCH 18/18] Add comment to mem::replace to explain why it's not implemented via mem::swap --- library/core/src/mem/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index a79a204e2c6a0..7ef84b0f5b540 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -909,6 +909,10 @@ pub fn take(dest: &mut T) -> T { #[rustc_const_unstable(feature = "const_replace", issue = "83164")] #[cfg_attr(not(test), rustc_diagnostic_item = "mem_replace")] pub const fn replace(dest: &mut T, src: T) -> T { + // It may be tempting to use `swap` to avoid `unsafe` here. Don't! + // The compiler optimizes the implementation below to two `memcpy`s + // while `swap` would require at least three. See PR#83022 for details. + // SAFETY: We read from `dest` but directly write `src` into it afterwards, // such that the old value is not duplicated. Nothing is dropped and // nothing here can panic.