about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-12-17 20:57:02 +0000
committerbors <bors@rust-lang.org>2023-12-17 20:57:02 +0000
commit6a62871320e262661bb1a0ea7f8aec9d3abeddf2 (patch)
treec0c4f54eaf7b69cba5e4e239b5508e9cc7349c61
parent4283aeaca5c0c14864d4af3369a42c6b8b21f80f (diff)
parent39fe059a35f5faab8c1cfdf0ffacc5372355c090 (diff)
downloadrust-6a62871320e262661bb1a0ea7f8aec9d3abeddf2.tar.gz
rust-6a62871320e262661bb1a0ea7f8aec9d3abeddf2.zip
Auto merge of #119053 - matthiaskrgr:rollup-hky3ld3, r=matthiaskrgr
Rollup of 4 pull requests

Successful merges:

 - #118880 (More expressions correctly are marked to end with curly braces)
 - #118928 (fix: Overlapping spans in delimited meta-vars)
 - #119022 (Remove unnecessary constness from ProjectionCandidate)
 - #119052 (Avoid overflow in GVN constant indexing.)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_ast/src/util/classify.rs35
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs7
-rw-r--r--compiler/rustc_middle/src/traits/select.rs6
-rw-r--r--compiler/rustc_mir_transform/src/gvn.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs14
-rw-r--r--tests/mir-opt/gvn.constant_index_overflow.GVN.panic-abort.diff104
-rw-r--r--tests/mir-opt/gvn.constant_index_overflow.GVN.panic-unwind.diff104
-rw-r--r--tests/mir-opt/gvn.rs18
-rw-r--r--tests/ui/macros/issue-118786.rs16
-rw-r--r--tests/ui/macros/issue-118786.stderr47
-rw-r--r--tests/ui/parser/bad-let-else-statement.rs164
-rw-r--r--tests/ui/parser/bad-let-else-statement.stderr232
14 files changed, 739 insertions, 27 deletions
diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs
index 821fca6656c..4dece079783 100644
--- a/compiler/rustc_ast/src/util/classify.rs
+++ b/compiler/rustc_ast/src/util/classify.rs
@@ -40,15 +40,44 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
             | Range(_, Some(e), _)
             | Ret(Some(e))
             | Unary(_, e)
-            | Yield(Some(e)) => {
+            | Yield(Some(e))
+            | Yeet(Some(e))
+            | Become(e) => {
                 expr = e;
             }
             Closure(closure) => {
                 expr = &closure.body;
             }
             Gen(..) | Block(..) | ForLoop(..) | If(..) | Loop(..) | Match(..) | Struct(..)
-            | TryBlock(..) | While(..) => break Some(expr),
-            _ => break None,
+            | TryBlock(..) | While(..) | ConstBlock(_) => break Some(expr),
+
+            // FIXME: These can end in `}`, but changing these would break stable code.
+            InlineAsm(_) | OffsetOf(_, _) | MacCall(_) | IncludedBytes(_) | FormatArgs(_) => {
+                break None;
+            }
+
+            Break(_, None)
+            | Range(_, None, _)
+            | Ret(None)
+            | Yield(None)
+            | Array(_)
+            | Call(_, _)
+            | MethodCall(_)
+            | Tup(_)
+            | Lit(_)
+            | Cast(_, _)
+            | Type(_, _)
+            | Await(_, _)
+            | Field(_, _)
+            | Index(_, _, _)
+            | Underscore
+            | Path(_, _)
+            | Continue(_)
+            | Repeat(_, _)
+            | Paren(_)
+            | Try(_)
+            | Yeet(None)
+            | Err => break None,
         }
     }
 }
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index 393eec3997b..14d22cbe2dd 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -236,6 +236,13 @@ fn expand_macro<'cx>(
                             target_sp.open = source_sp.open.with_ctxt(ctxt);
                             target_sp.close = source_sp.close.with_ctxt(ctxt);
                         }
+                        (
+                            TokenTree::Delimited(target_sp, ..),
+                            mbe::TokenTree::MetaVar(source_sp, ..),
+                        ) => {
+                            target_sp.open = source_sp.with_ctxt(ctxt);
+                            target_sp.close = source_sp.with_ctxt(ctxt).shrink_to_hi();
+                        }
                         _ => {
                             let sp = rhs_tt.span().with_ctxt(ctxt);
                             tt.set_span(sp);
diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs
index e8e2907eb33..c52103eb247 100644
--- a/compiler/rustc_middle/src/traits/select.rs
+++ b/compiler/rustc_middle/src/traits/select.rs
@@ -125,10 +125,8 @@ pub enum SelectionCandidate<'tcx> {
 
     /// This is a trait matching with a projected type as `Self`, and we found
     /// an applicable bound in the trait definition. The `usize` is an index
-    /// into the list returned by `tcx.item_bounds`. The constness is the
-    /// constness of the bound in the trait.
-    // FIXME(effects) do we need this constness
-    ProjectionCandidate(usize, ty::BoundConstness),
+    /// into the list returned by `tcx.item_bounds`.
+    ProjectionCandidate(usize),
 
     /// Implementation of a `Fn`-family trait by one of the anonymous types
     /// generated for an `||` expression.
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index b21988e179d..3b8adf7e86b 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -644,12 +644,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             {
                 if let Some(offset) = self.evaluated[idx].as_ref()
                     && let Ok(offset) = self.ecx.read_target_usize(offset)
+                    && let Some(min_length) = offset.checked_add(1)
                 {
-                    projection.to_mut()[i] = ProjectionElem::ConstantIndex {
-                        offset,
-                        min_length: offset + 1,
-                        from_end: false,
-                    };
+                    projection.to_mut()[i] =
+                        ProjectionElem::ConstantIndex { offset, min_length, from_end: false };
                 } else if let Some(new_idx) = self.try_as_local(idx, location) {
                     projection.to_mut()[i] = ProjectionElem::Index(new_idx);
                     self.reused_locals.insert(new_idx);
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index c7d0ab71644..cd7fd028a46 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -154,10 +154,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             .infcx
             .probe(|_| self.match_projection_obligation_against_definition_bounds(obligation));
 
-        // FIXME(effects) proper constness needed?
-        candidates.vec.extend(
-            result.into_iter().map(|idx| ProjectionCandidate(idx, ty::BoundConstness::NotConst)),
-        );
+        candidates.vec.extend(result.into_iter().map(|idx| ProjectionCandidate(idx)));
     }
 
     /// Given an obligation like `<SomeTrait for T>`, searches the obligations that the caller
@@ -585,7 +582,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 }
 
                 ty::Alias(ty::Opaque, _) => {
-                    if candidates.vec.iter().any(|c| matches!(c, ProjectionCandidate(..))) {
+                    if candidates.vec.iter().any(|c| matches!(c, ProjectionCandidate(_))) {
                         // We do not generate an auto impl candidate for `impl Trait`s which already
                         // reference our auto trait.
                         //
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 7e3d304d1cd..bcaf6aff66b 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -71,7 +71,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 ImplSource::Builtin(BuiltinImplSource::Misc, data)
             }
 
-            ProjectionCandidate(idx, _) => {
+            ProjectionCandidate(idx) => {
                 let obligations = self.confirm_projection_candidate(obligation, idx)?;
                 ImplSource::Param(obligations)
             }
@@ -1313,7 +1313,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 // If we have a projection type, make sure to normalize it so we replace it
                 // with a fresh infer variable
                 ty::Alias(ty::Projection | ty::Inherent, ..) => {
-                    // FIXME(effects) this needs constness
                     let predicate = normalize_with_depth_to(
                         self,
                         obligation.param_env,
@@ -1344,7 +1343,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 // since it's either not `const Drop` (and we raise an error during selection),
                 // or it's an ADT (and we need to check for a custom impl during selection)
                 _ => {
-                    // FIXME(effects) this needs constness
                     let predicate = self_ty.rebind(ty::TraitPredicate {
                         trait_ref: ty::TraitRef::from_lang_item(
                             self.tcx(),
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 7f31a2529f5..9886e33ca3b 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -1883,7 +1883,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 | BuiltinCandidate { .. }
                 | TraitAliasCandidate
                 | ObjectCandidate(_)
-                | ProjectionCandidate(..),
+                | ProjectionCandidate(_),
             ) => {
                 // We have a where clause so don't go around looking
                 // for impls. Arbitrarily give param candidates priority
@@ -1893,7 +1893,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 // here (see issue #50825).
                 DropVictim::drop_if(!is_global(other_cand))
             }
-            (ObjectCandidate(_) | ProjectionCandidate(..), ParamCandidate(ref victim_cand)) => {
+            (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref victim_cand)) => {
                 // Prefer these to a global where-clause bound
                 // (see issue #50825).
                 if is_global(victim_cand) { DropVictim::Yes } else { DropVictim::No }
@@ -1921,20 +1921,20 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 )
             }
 
-            (ProjectionCandidate(i, _), ProjectionCandidate(j, _))
+            (ProjectionCandidate(i), ProjectionCandidate(j))
             | (ObjectCandidate(i), ObjectCandidate(j)) => {
                 // Arbitrarily pick the lower numbered candidate for backwards
                 // compatibility reasons. Don't let this affect inference.
                 DropVictim::drop_if(i < j && !has_non_region_infer)
             }
-            (ObjectCandidate(_), ProjectionCandidate(..))
-            | (ProjectionCandidate(..), ObjectCandidate(_)) => {
+            (ObjectCandidate(_), ProjectionCandidate(_))
+            | (ProjectionCandidate(_), ObjectCandidate(_)) => {
                 bug!("Have both object and projection candidate")
             }
 
             // Arbitrarily give projection and object candidates priority.
             (
-                ObjectCandidate(_) | ProjectionCandidate(..),
+                ObjectCandidate(_) | ProjectionCandidate(_),
                 ImplCandidate(..)
                 | AutoImplCandidate
                 | ClosureCandidate { .. }
@@ -1964,7 +1964,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 | TraitUpcastingUnsizeCandidate(_)
                 | BuiltinCandidate { .. }
                 | TraitAliasCandidate,
-                ObjectCandidate(_) | ProjectionCandidate(..),
+                ObjectCandidate(_) | ProjectionCandidate(_),
             ) => DropVictim::No,
 
             (&ImplCandidate(other_def), &ImplCandidate(victim_def)) => {
diff --git a/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-abort.diff b/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-abort.diff
new file mode 100644
index 00000000000..4b1e3379114
--- /dev/null
+++ b/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-abort.diff
@@ -0,0 +1,104 @@
+- // MIR for `constant_index_overflow` before GVN
++ // MIR for `constant_index_overflow` after GVN
+  
+  fn constant_index_overflow(_1: &[T]) -> () {
+      debug x => _1;
+      let mut _0: ();
+      let _2: usize;
+      let mut _4: bool;
+      let mut _5: usize;
+      let mut _6: usize;
+      let mut _7: &[T];
+      let _8: usize;
+      let mut _9: usize;
+      let mut _10: bool;
+      let _11: usize;
+      let mut _12: usize;
+      let mut _13: bool;
+      let mut _14: T;
+      scope 1 {
+          debug a => _2;
+          let _3: T;
+          scope 2 {
+              debug b => _3;
+          }
+      }
+  
+      bb0: {
+-         StorageLive(_2);
+-         _2 = const _ as usize (IntToInt);
++         nop;
++         _2 = const usize::MAX;
+          StorageLive(_3);
+          StorageLive(_4);
+          StorageLive(_5);
+-         _5 = _2;
++         _5 = const usize::MAX;
+          StorageLive(_6);
+          StorageLive(_7);
+          _7 = &(*_1);
+          _6 = core::slice::<impl [T]>::len(move _7) -> [return: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+          StorageDead(_7);
+-         _4 = Lt(move _5, move _6);
++         _4 = Lt(const usize::MAX, move _6);
+          switchInt(move _4) -> [0: bb4, otherwise: bb2];
+      }
+  
+      bb2: {
+          StorageDead(_6);
+          StorageDead(_5);
+          StorageLive(_8);
+-         _8 = _2;
++         _8 = const usize::MAX;
+          _9 = Len((*_1));
+-         _10 = Lt(_8, _9);
+-         assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> [success: bb3, unwind unreachable];
++         _10 = Lt(const usize::MAX, _9);
++         assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, const usize::MAX) -> [success: bb3, unwind unreachable];
+      }
+  
+      bb3: {
+-         _3 = (*_1)[_8];
++         _3 = (*_1)[_2];
+          StorageDead(_8);
+          goto -> bb6;
+      }
+  
+      bb4: {
+          StorageDead(_6);
+          StorageDead(_5);
+          StorageLive(_11);
+          _11 = const 0_usize;
+          _12 = Len((*_1));
+-         _13 = Lt(_11, _12);
+-         assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb5, unwind unreachable];
++         _13 = Lt(const 0_usize, _12);
++         assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, const 0_usize) -> [success: bb5, unwind unreachable];
+      }
+  
+      bb5: {
+-         _3 = (*_1)[_11];
++         _3 = (*_1)[0 of 1];
+          StorageDead(_11);
+          goto -> bb6;
+      }
+  
+      bb6: {
+          StorageDead(_4);
+          StorageLive(_14);
+          _14 = _3;
+          _0 = opaque::<T>(move _14) -> [return: bb7, unwind unreachable];
+      }
+  
+      bb7: {
+          StorageDead(_14);
+          StorageDead(_3);
+-         StorageDead(_2);
++         nop;
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-unwind.diff b/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-unwind.diff
new file mode 100644
index 00000000000..8abcd7e9387
--- /dev/null
+++ b/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-unwind.diff
@@ -0,0 +1,104 @@
+- // MIR for `constant_index_overflow` before GVN
++ // MIR for `constant_index_overflow` after GVN
+  
+  fn constant_index_overflow(_1: &[T]) -> () {
+      debug x => _1;
+      let mut _0: ();
+      let _2: usize;
+      let mut _4: bool;
+      let mut _5: usize;
+      let mut _6: usize;
+      let mut _7: &[T];
+      let _8: usize;
+      let mut _9: usize;
+      let mut _10: bool;
+      let _11: usize;
+      let mut _12: usize;
+      let mut _13: bool;
+      let mut _14: T;
+      scope 1 {
+          debug a => _2;
+          let _3: T;
+          scope 2 {
+              debug b => _3;
+          }
+      }
+  
+      bb0: {
+-         StorageLive(_2);
+-         _2 = const _ as usize (IntToInt);
++         nop;
++         _2 = const usize::MAX;
+          StorageLive(_3);
+          StorageLive(_4);
+          StorageLive(_5);
+-         _5 = _2;
++         _5 = const usize::MAX;
+          StorageLive(_6);
+          StorageLive(_7);
+          _7 = &(*_1);
+          _6 = core::slice::<impl [T]>::len(move _7) -> [return: bb1, unwind continue];
+      }
+  
+      bb1: {
+          StorageDead(_7);
+-         _4 = Lt(move _5, move _6);
++         _4 = Lt(const usize::MAX, move _6);
+          switchInt(move _4) -> [0: bb4, otherwise: bb2];
+      }
+  
+      bb2: {
+          StorageDead(_6);
+          StorageDead(_5);
+          StorageLive(_8);
+-         _8 = _2;
++         _8 = const usize::MAX;
+          _9 = Len((*_1));
+-         _10 = Lt(_8, _9);
+-         assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> [success: bb3, unwind continue];
++         _10 = Lt(const usize::MAX, _9);
++         assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, const usize::MAX) -> [success: bb3, unwind continue];
+      }
+  
+      bb3: {
+-         _3 = (*_1)[_8];
++         _3 = (*_1)[_2];
+          StorageDead(_8);
+          goto -> bb6;
+      }
+  
+      bb4: {
+          StorageDead(_6);
+          StorageDead(_5);
+          StorageLive(_11);
+          _11 = const 0_usize;
+          _12 = Len((*_1));
+-         _13 = Lt(_11, _12);
+-         assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb5, unwind continue];
++         _13 = Lt(const 0_usize, _12);
++         assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, const 0_usize) -> [success: bb5, unwind continue];
+      }
+  
+      bb5: {
+-         _3 = (*_1)[_11];
++         _3 = (*_1)[0 of 1];
+          StorageDead(_11);
+          goto -> bb6;
+      }
+  
+      bb6: {
+          StorageDead(_4);
+          StorageLive(_14);
+          _14 = _3;
+          _0 = opaque::<T>(move _14) -> [return: bb7, unwind continue];
+      }
+  
+      bb7: {
+          StorageDead(_14);
+          StorageDead(_3);
+-         StorageDead(_2);
++         nop;
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs
index 6e082acdbd3..db131f7f97d 100644
--- a/tests/mir-opt/gvn.rs
+++ b/tests/mir-opt/gvn.rs
@@ -609,6 +609,22 @@ fn indirect_static() {
     })
 }
 
+/// Verify that having constant index `u64::MAX` does not yield to an overflow in rustc.
+fn constant_index_overflow<T: Copy>(x: &[T]) {
+    // CHECK-LABEL: fn constant_index_overflow(
+    // CHECK: debug a => [[a:_.*]];
+    // CHECK: debug b => [[b:_.*]];
+    // CHECK: [[a]] = const usize::MAX;
+    // CHECK-NOT: = (*_1)[{{.*}} of 0];
+    // CHECK: [[b]] = (*_1)[[[a]]];
+    // CHECK-NOT: = (*_1)[{{.*}} of 0];
+    // CHECK: [[b]] = (*_1)[0 of 1];
+    // CHECK-NOT: = (*_1)[{{.*}} of 0];
+    let a = u64::MAX as usize;
+    let b = if a < x.len() { x[a] } else { x[0] };
+    opaque(b)
+}
+
 fn main() {
     subexpression_elimination(2, 4, 5);
     wrap_unwrap(5);
@@ -627,6 +643,7 @@ fn main() {
     repeat();
     fn_pointers();
     indirect_static();
+    constant_index_overflow(&[5, 3]);
 }
 
 #[inline(never)]
@@ -653,3 +670,4 @@ fn identity<T>(x: T) -> T {
 // EMIT_MIR gvn.repeat.GVN.diff
 // EMIT_MIR gvn.fn_pointers.GVN.diff
 // EMIT_MIR gvn.indirect_static.GVN.diff
+// EMIT_MIR gvn.constant_index_overflow.GVN.diff
diff --git a/tests/ui/macros/issue-118786.rs b/tests/ui/macros/issue-118786.rs
new file mode 100644
index 00000000000..84af3a65113
--- /dev/null
+++ b/tests/ui/macros/issue-118786.rs
@@ -0,0 +1,16 @@
+// compile-flags: --crate-type lib -O -C debug-assertions=yes
+
+// Regression test for issue 118786
+
+macro_rules! make_macro {
+    ($macro_name:tt) => {
+        macro_rules! $macro_name {
+        //~^ ERROR macros that expand to items must be delimited with braces or followed by a semicolon
+        //~| ERROR macro expansion ignores token `{` and any following
+        //~| ERROR cannot find macro `macro_rules` in this scope
+            () => {}
+        }
+    }
+}
+
+make_macro!((meow));
diff --git a/tests/ui/macros/issue-118786.stderr b/tests/ui/macros/issue-118786.stderr
new file mode 100644
index 00000000000..ca3a40f31c1
--- /dev/null
+++ b/tests/ui/macros/issue-118786.stderr
@@ -0,0 +1,47 @@
+error: macros that expand to items must be delimited with braces or followed by a semicolon
+  --> $DIR/issue-118786.rs:7:22
+   |
+LL |         macro_rules! $macro_name {
+   |                      ^^^^^^^^^^^
+   |
+help: change the delimiters to curly braces
+   |
+LL |         macro_rules! {} {
+   |                      ~          +
+help: add a semicolon
+   |
+LL |         macro_rules! $macro_name; {
+   |                                 +
+
+error: macro expansion ignores token `{` and any following
+  --> $DIR/issue-118786.rs:7:34
+   |
+LL |         macro_rules! $macro_name {
+   |                                  ^
+...
+LL | make_macro!((meow));
+   | ------------------- caused by the macro expansion here
+   |
+   = note: the usage of `make_macro!` is likely invalid in item context
+
+error: cannot find macro `macro_rules` in this scope
+  --> $DIR/issue-118786.rs:7:9
+   |
+LL |         macro_rules! $macro_name {
+   |         ^^^^^^^^^^^
+...
+LL | make_macro!((meow));
+   | ------------------- in this macro invocation
+   |
+note: maybe you have forgotten to define a name for this `macro_rules!`
+  --> $DIR/issue-118786.rs:7:9
+   |
+LL |         macro_rules! $macro_name {
+   |         ^^^^^^^^^^^
+...
+LL | make_macro!((meow));
+   | ------------------- in this macro invocation
+   = note: this error originates in the macro `make_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/parser/bad-let-else-statement.rs b/tests/ui/parser/bad-let-else-statement.rs
new file mode 100644
index 00000000000..7b927c89ba0
--- /dev/null
+++ b/tests/ui/parser/bad-let-else-statement.rs
@@ -0,0 +1,164 @@
+#![feature(inline_const)]
+#![feature(yeet_expr)]
+#![allow(incomplete_features)] // Necessary for now, while explicit_tail_calls is incomplete
+#![feature(explicit_tail_calls)]
+
+fn a() {
+    let foo = {
+        1
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn b() {
+    let foo = for i in 1..2 {
+        break;
+    } else {
+        //~^ ERROR `for...else` loops are not supported
+        return;
+    };
+}
+
+fn c() {
+    let foo = if true {
+        1
+    } else {
+        0
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn d() {
+    let foo = loop {
+        break;
+    } else {
+        //~^ ERROR loop...else` loops are not supported
+        return;
+    };
+}
+
+fn e() {
+    let foo = match true {
+        true => 1,
+        false => 0
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+struct X {a: i32}
+fn f() {
+    let foo = X {
+        a: 1
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn g() {
+    let foo = while false {
+        break;
+    } else {
+        //~^ ERROR `while...else` loops are not supported
+        return;
+    };
+}
+
+fn h() {
+    let foo = const {
+        1
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn i() {
+    let foo = &{
+        1
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn j() {
+    let bar = 0;
+    let foo = bar = {
+        1
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn k() {
+    let foo = 1 + {
+        1
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn l() {
+    let foo = 1..{
+        1
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn m() {
+    let foo = return {
+        ()
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn n() {
+    let foo = -{
+        1
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn o() -> Result<(), ()> {
+    let foo = do yeet {
+        ()
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return Ok(());
+    };
+}
+
+fn p() {
+    let foo = become {
+        ()
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn q() {
+    let foo = |x: i32| {
+        x
+    } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+fn main() {}
diff --git a/tests/ui/parser/bad-let-else-statement.stderr b/tests/ui/parser/bad-let-else-statement.stderr
new file mode 100644
index 00000000000..7cbda25e417
--- /dev/null
+++ b/tests/ui/parser/bad-let-else-statement.stderr
@@ -0,0 +1,232 @@
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:9:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = ({
+LL |         1
+LL ~     }) else {
+   |
+
+error: `for...else` loops are not supported
+  --> $DIR/bad-let-else-statement.rs:18:7
+   |
+LL |       let foo = for i in 1..2 {
+   |                 --- `else` is attached to this loop
+LL |           break;
+LL |       } else {
+   |  _______^
+LL | |
+LL | |         return;
+LL | |     };
+   | |_____^
+   |
+   = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:29:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = (if true {
+LL |         1
+LL |     } else {
+LL |         0
+LL ~     }) else {
+   |
+
+error: `loop...else` loops are not supported
+  --> $DIR/bad-let-else-statement.rs:38:7
+   |
+LL |       let foo = loop {
+   |                 ---- `else` is attached to this loop
+LL |           break;
+LL |       } else {
+   |  _______^
+LL | |
+LL | |         return;
+LL | |     };
+   | |_____^
+   |
+   = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:48:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = (match true {
+LL |         true => 1,
+LL |         false => 0
+LL ~     }) else {
+   |
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:58:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = (X {
+LL |         a: 1
+LL ~     }) else {
+   |
+
+error: `while...else` loops are not supported
+  --> $DIR/bad-let-else-statement.rs:67:7
+   |
+LL |       let foo = while false {
+   |                 ----- `else` is attached to this loop
+LL |           break;
+LL |       } else {
+   |  _______^
+LL | |
+LL | |         return;
+LL | |     };
+   | |_____^
+   |
+   = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:76:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = (const {
+LL |         1
+LL ~     }) else {
+   |
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:85:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = &({
+LL |         1
+LL ~     }) else {
+   |
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:95:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = bar = ({
+LL |         1
+LL ~     }) else {
+   |
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:104:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = 1 + ({
+LL |         1
+LL ~     }) else {
+   |
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:113:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = 1..({
+LL |         1
+LL ~     }) else {
+   |
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:122:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = return ({
+LL |         ()
+LL ~     }) else {
+   |
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:131:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = -({
+LL |         1
+LL ~     }) else {
+   |
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:140:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = do yeet ({
+LL |         ()
+LL ~     }) else {
+   |
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:149:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = become ({
+LL |         ()
+LL ~     }) else {
+   |
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/bad-let-else-statement.rs:158:5
+   |
+LL |     } else {
+   |     ^
+   |
+help: wrap the expression in parentheses
+   |
+LL ~     let foo = |x: i32| ({
+LL |         x
+LL ~     }) else {
+   |
+
+error: aborting due to 17 previous errors
+