From 8b186dfdb70ebfe722975970458c6230841e6911 Mon Sep 17 00:00:00 2001
From: clubby789 <jamie@hill-daniel.co.uk>
Date: Mon, 27 Feb 2023 13:22:43 +0000
Subject: [PATCH] Add recovery for use of removed `box` syntax

---
 compiler/rustc_parse/src/parser/expr.rs   | 28 +++++++++++
 tests/ui/parser/removed-syntax-box.fixed  | 14 ++++++
 tests/ui/parser/removed-syntax-box.rs     | 12 +++--
 tests/ui/parser/removed-syntax-box.stderr | 59 +++++++++++++++++++++--
 tests/ui/unpretty/box.stdout              |  2 +-
 5 files changed, 105 insertions(+), 10 deletions(-)
 create mode 100644 tests/ui/parser/removed-syntax-box.fixed

diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 27516716286..31a91e1ab6a 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -8,6 +8,7 @@ use super::{
 
 use crate::errors;
 use crate::maybe_recover_from_interpolated_ty_qpath;
+use ast::{Path, PathSegment};
 use core::mem;
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, Delimiter, Token, TokenKind};
@@ -29,6 +30,7 @@ use rustc_session::errors::{report_lit_error, ExprParenthesesNeeded};
 use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;
 use rustc_session::lint::BuiltinLintDiagnostics;
 use rustc_span::source_map::{self, Span, Spanned};
+use rustc_span::symbol::kw::PathRoot;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{BytePos, Pos};
 use thin_vec::{thin_vec, ThinVec};
@@ -607,6 +609,9 @@ impl<'a> Parser<'a> {
                 let operand_expr = this.parse_expr_dot_or_call(Default::default())?;
                 this.recover_from_prefix_increment(operand_expr, pre_span, starts_stmt)
             }
+            token::Ident(..) if this.token.is_keyword(kw::Box) => {
+                make_it!(this, attrs, |this, _| this.parse_expr_box(lo))
+            }
             token::Ident(..) if this.may_recover() && this.is_mistaken_not_ident_negation() => {
                 make_it!(this, attrs, |this, _| this.recover_not_expr(lo))
             }
@@ -633,6 +638,29 @@ impl<'a> Parser<'a> {
         self.parse_expr_unary(lo, UnOp::Not)
     }
 
+    /// Parse `box expr` - this syntax has been removed, but we still parse this
+    /// for now to provide an automated way to fix usages of it
+    fn parse_expr_box(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
+        let (span, expr) = self.parse_expr_prefix_common(lo)?;
+        let code = self.sess.source_map().span_to_snippet(span.with_lo(lo.hi())).unwrap();
+        self.sess.emit_err(errors::BoxSyntaxRemoved { span, code: code.trim() });
+        // So typechecking works, parse `box <expr>` as `::std::boxed::Box::new(expr)`
+        let path = Path {
+            span,
+            segments: [
+                PathSegment::from_ident(Ident::with_dummy_span(PathRoot)),
+                PathSegment::from_ident(Ident::with_dummy_span(sym::std)),
+                PathSegment::from_ident(Ident::from_str("boxed")),
+                PathSegment::from_ident(Ident::from_str("Box")),
+                PathSegment::from_ident(Ident::with_dummy_span(sym::new)),
+            ]
+            .into(),
+            tokens: None,
+        };
+        let path = self.mk_expr(span, ExprKind::Path(None, path));
+        Ok((span, self.mk_call(path, ThinVec::from([expr]))))
+    }
+
     fn is_mistaken_not_ident_negation(&self) -> bool {
         let token_cannot_continue_expr = |t: &Token| match t.uninterpolate().kind {
             // These tokens can start an expression after `!`, but
diff --git a/tests/ui/parser/removed-syntax-box.fixed b/tests/ui/parser/removed-syntax-box.fixed
new file mode 100644
index 00000000000..09d1304b775
--- /dev/null
+++ b/tests/ui/parser/removed-syntax-box.fixed
@@ -0,0 +1,14 @@
+// run-rustfix
+
+fn main() {
+    #[allow(dead_code)]
+    struct T {
+        a: u8,
+        b: u8,
+    }
+    let _ = Box::new(()); //~ ERROR `box_syntax` has been removed
+    let _ = Box::new(1); //~ ERROR `box_syntax` has been removed
+    let _ = Box::new(T { a: 12, b: 18 }); //~ ERROR `box_syntax` has been removed
+    let _ = Box::new([5; 30]); //~ ERROR `box_syntax` has been removed
+    let _: Box<()> = Box::new(()); //~ ERROR `box_syntax` has been removed
+}
diff --git a/tests/ui/parser/removed-syntax-box.rs b/tests/ui/parser/removed-syntax-box.rs
index b0e8eb0410e..1f5061b02c7 100644
--- a/tests/ui/parser/removed-syntax-box.rs
+++ b/tests/ui/parser/removed-syntax-box.rs
@@ -1,10 +1,14 @@
+// run-rustfix
+
 fn main() {
+    #[allow(dead_code)]
     struct T {
         a: u8,
         b: u8,
     }
-    let _ = box () //~ ERROR expected expression, found reserved keyword `box`
-    let _ = box 1;
-    let _ = box T { a: 12, b: 18 };
-    let _ = box [5; 30];
+    let _ = box (); //~ ERROR `box_syntax` has been removed
+    let _ = box 1; //~ ERROR `box_syntax` has been removed
+    let _ = box T { a: 12, b: 18 }; //~ ERROR `box_syntax` has been removed
+    let _ = box [5; 30]; //~ ERROR `box_syntax` has been removed
+    let _: Box<()> = box (); //~ ERROR `box_syntax` has been removed
 }
diff --git a/tests/ui/parser/removed-syntax-box.stderr b/tests/ui/parser/removed-syntax-box.stderr
index 8c8d5b3c4cb..46b891587d5 100644
--- a/tests/ui/parser/removed-syntax-box.stderr
+++ b/tests/ui/parser/removed-syntax-box.stderr
@@ -1,8 +1,57 @@
-error: expected expression, found reserved keyword `box`
-  --> $DIR/removed-syntax-box.rs:6:13
+error: `box_syntax` has been removed
+  --> $DIR/removed-syntax-box.rs:9:13
    |
-LL |     let _ = box ()
-   |             ^^^ expected expression
+LL |     let _ = box ();
+   |             ^^^^^^
+   |
+help: use `Box::new()` instead
+   |
+LL |     let _ = Box::new(());
+   |             ~~~~~~~~~~~~
 
-error: aborting due to previous error
+error: `box_syntax` has been removed
+  --> $DIR/removed-syntax-box.rs:10:13
+   |
+LL |     let _ = box 1;
+   |             ^^^^^
+   |
+help: use `Box::new()` instead
+   |
+LL |     let _ = Box::new(1);
+   |             ~~~~~~~~~~~
+
+error: `box_syntax` has been removed
+  --> $DIR/removed-syntax-box.rs:11:13
+   |
+LL |     let _ = box T { a: 12, b: 18 };
+   |             ^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `Box::new()` instead
+   |
+LL |     let _ = Box::new(T { a: 12, b: 18 });
+   |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: `box_syntax` has been removed
+  --> $DIR/removed-syntax-box.rs:12:13
+   |
+LL |     let _ = box [5; 30];
+   |             ^^^^^^^^^^^
+   |
+help: use `Box::new()` instead
+   |
+LL |     let _ = Box::new([5; 30]);
+   |             ~~~~~~~~~~~~~~~~~
+
+error: `box_syntax` has been removed
+  --> $DIR/removed-syntax-box.rs:13:22
+   |
+LL |     let _: Box<()> = box ();
+   |                      ^^^^^^
+   |
+help: use `Box::new()` instead
+   |
+LL |     let _: Box<()> = Box::new(());
+   |                      ~~~~~~~~~~~~
+
+error: aborting due to 5 previous errors
 
diff --git a/tests/ui/unpretty/box.stdout b/tests/ui/unpretty/box.stdout
index 1318a56e3be..0c6e012e698 100644
--- a/tests/ui/unpretty/box.stdout
+++ b/tests/ui/unpretty/box.stdout
@@ -10,5 +10,5 @@ extern crate std;
 fn main() {
         let _ =
             #[rustc_box]
-            Box::new (1);
+            Box::new(1);
     }