about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_parse/messages.ftl6
-rw-r--r--compiler/rustc_parse/src/errors.rs3
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs10
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs63
-rw-r--r--tests/ui/did_you_mean/E0178.stderr18
-rw-r--r--tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr8
-rw-r--r--tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.rs2
-rw-r--r--tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.stderr7
-rw-r--r--tests/ui/impl-trait/impl-trait-plus-priority.rs4
-rw-r--r--tests/ui/impl-trait/impl-trait-plus-priority.stderr10
-rw-r--r--tests/ui/parser/issues/issue-73568-lifetime-after-mut.rs2
-rw-r--r--tests/ui/parser/issues/issue-73568-lifetime-after-mut.stderr4
-rw-r--r--tests/ui/parser/macro/trait-object-macro-matcher.e2015.stderr32
-rw-r--r--tests/ui/parser/macro/trait-object-macro-matcher.e2021.stderr16
-rw-r--r--tests/ui/parser/macro/trait-object-macro-matcher.rs12
-rw-r--r--tests/ui/parser/macro/trait-object-macro-matcher.stderr23
-rw-r--r--tests/ui/parser/recover/recover-ampersand-less-ref-ty.rs13
-rw-r--r--tests/ui/parser/recover/recover-ampersand-less-ref-ty.stderr24
-rw-r--r--tests/ui/parser/trait-object-bad-parens.rs12
-rw-r--r--tests/ui/parser/trait-object-bad-parens.stderr22
-rw-r--r--tests/ui/parser/trait-object-lifetime-parens.e2015.stderr (renamed from tests/ui/parser/trait-object-lifetime-parens.stderr)13
-rw-r--r--tests/ui/parser/trait-object-lifetime-parens.e2021.stderr51
-rw-r--r--tests/ui/parser/trait-object-lifetime-parens.rs14
-rw-r--r--tests/ui/parser/trait-object-polytrait-priority.rs2
-rw-r--r--tests/ui/parser/trait-object-polytrait-priority.stderr4
25 files changed, 283 insertions, 92 deletions
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 93fa89b68b9..80cd87a69f7 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -543,7 +543,7 @@ parse_maybe_recover_from_bad_qpath_stage_2 =
     .suggestion = types that don't start with an identifier need to be surrounded with angle brackets in qualified paths
 
 parse_maybe_recover_from_bad_type_plus =
-    expected a path on the left-hand side of `+`, not `{$ty}`
+    expected a path on the left-hand side of `+`
 
 parse_maybe_report_ambiguous_plus =
     ambiguous `+` in a type
@@ -642,7 +642,9 @@ parse_mut_on_nested_ident_pattern = `mut` must be attached to each individual bi
     .suggestion = add `mut` to each binding
 parse_mut_on_non_ident_pattern = `mut` must be followed by a named binding
     .suggestion = remove the `mut` prefix
-parse_need_plus_after_trait_object_lifetime = lifetime in trait object type must be followed by `+`
+
+parse_need_plus_after_trait_object_lifetime = lifetimes must be followed by `+` to form a trait object type
+    .suggestion = consider adding a trait bound after the potential lifetime bound
 
 parse_nested_adt = `{$kw_str}` definition cannot be nested inside `{$keyword}`
     .suggestion = consider creating a new `{$kw_str}` definition instead of nesting
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index dfdef018bc3..aad8a8bc205 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -30,7 +30,6 @@ pub(crate) struct AmbiguousPlus {
 #[derive(Diagnostic)]
 #[diag(parse_maybe_recover_from_bad_type_plus, code = E0178)]
 pub(crate) struct BadTypePlus {
-    pub ty: String,
     #[primary_span]
     pub span: Span,
     #[subdiagnostic]
@@ -2806,6 +2805,8 @@ pub(crate) struct ReturnTypesUseThinArrow {
 pub(crate) struct NeedPlusAfterTraitObjectLifetime {
     #[primary_span]
     pub span: Span,
+    #[suggestion(code = " + /* Trait */", applicability = "has-placeholders")]
+    pub suggestion: Span,
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index a4978b5a0fe..fa38c5b9a71 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -1657,19 +1657,19 @@ impl<'a> Parser<'a> {
 
         self.bump(); // `+`
         let _bounds = self.parse_generic_bounds()?;
-        let sum_span = ty.span.to(self.prev_token.span);
-
         let sub = match &ty.kind {
             TyKind::Ref(_lifetime, mut_ty) => {
                 let lo = mut_ty.ty.span.shrink_to_lo();
                 let hi = self.prev_token.span.shrink_to_hi();
                 BadTypePlusSub::AddParen { suggestion: AddParen { lo, hi } }
             }
-            TyKind::Ptr(..) | TyKind::BareFn(..) => BadTypePlusSub::ForgotParen { span: sum_span },
-            _ => BadTypePlusSub::ExpectPath { span: sum_span },
+            TyKind::Ptr(..) | TyKind::BareFn(..) => {
+                BadTypePlusSub::ForgotParen { span: ty.span.to(self.prev_token.span) }
+            }
+            _ => BadTypePlusSub::ExpectPath { span: ty.span },
         };
 
-        self.dcx().emit_err(BadTypePlus { ty: pprust::ty_to_string(ty), span: sum_span, sub });
+        self.dcx().emit_err(BadTypePlus { span: ty.span, sub });
 
         Ok(())
     }
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index d0cff42a209..42ebf26784d 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -7,7 +7,7 @@ use rustc_ast::{
     Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty,
     TyKind, UnsafeBinderTy,
 };
-use rustc_errors::{Applicability, PResult};
+use rustc_errors::{Applicability, Diag, PResult};
 use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym};
 use thin_vec::{ThinVec, thin_vec};
 
@@ -411,6 +411,9 @@ impl<'a> Parser<'a> {
                 TyKind::Path(None, path) if maybe_bounds => {
                     self.parse_remaining_bounds_path(ThinVec::new(), path, lo, true)
                 }
+                // For `('a) + …`, we know that `'a` in type position already lead to an error being
+                // emitted. To reduce output, let's indirectly suppress E0178 (bad `+` in type) and
+                // other irrelevant consequential errors.
                 TyKind::TraitObject(bounds, TraitObjectSyntax::None)
                     if maybe_bounds && bounds.len() == 1 && !trailing_plus =>
                 {
@@ -425,12 +428,60 @@ impl<'a> Parser<'a> {
     }
 
     fn parse_bare_trait_object(&mut self, lo: Span, allow_plus: AllowPlus) -> PResult<'a, TyKind> {
-        let lt_no_plus = self.check_lifetime() && !self.look_ahead(1, |t| t.is_like_plus());
-        let bounds = self.parse_generic_bounds_common(allow_plus)?;
-        if lt_no_plus {
-            self.dcx().emit_err(NeedPlusAfterTraitObjectLifetime { span: lo });
+        // A lifetime only begins a bare trait object type if it is followed by `+`!
+        if self.token.is_lifetime() && !self.look_ahead(1, |t| t.is_like_plus()) {
+            // In Rust 2021 and beyond, we assume that the user didn't intend to write a bare trait
+            // object type with a leading lifetime bound since that seems very unlikely given the
+            // fact that `dyn`-less trait objects are *semantically* invalid.
+            if self.psess.edition.at_least_rust_2021() {
+                let lt = self.expect_lifetime();
+                let mut err = self.dcx().struct_span_err(lo, "expected type, found lifetime");
+                err.span_label(lo, "expected type");
+                return Ok(match self.maybe_recover_ref_ty_no_leading_ampersand(lt, lo, err) {
+                    Ok(ref_ty) => ref_ty,
+                    Err(err) => TyKind::Err(err.emit()),
+                });
+            }
+
+            self.dcx().emit_err(NeedPlusAfterTraitObjectLifetime {
+                span: lo,
+                suggestion: lo.shrink_to_hi(),
+            });
+        }
+        Ok(TyKind::TraitObject(
+            self.parse_generic_bounds_common(allow_plus)?,
+            TraitObjectSyntax::None,
+        ))
+    }
+
+    fn maybe_recover_ref_ty_no_leading_ampersand<'cx>(
+        &mut self,
+        lt: Lifetime,
+        lo: Span,
+        mut err: Diag<'cx>,
+    ) -> Result<TyKind, Diag<'cx>> {
+        if !self.may_recover() {
+            return Err(err);
+        }
+        let snapshot = self.create_snapshot_for_diagnostic();
+        let mutbl = self.parse_mutability();
+        match self.parse_ty_no_plus() {
+            Ok(ty) => {
+                err.span_suggestion_verbose(
+                    lo.shrink_to_lo(),
+                    "you might have meant to write a reference type here",
+                    "&",
+                    Applicability::MaybeIncorrect,
+                );
+                err.emit();
+                Ok(TyKind::Ref(Some(lt), MutTy { ty, mutbl }))
+            }
+            Err(diag) => {
+                diag.cancel();
+                self.restore_snapshot(snapshot);
+                Err(err)
+            }
         }
-        Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None))
     }
 
     fn parse_remaining_bounds_path(
diff --git a/tests/ui/did_you_mean/E0178.stderr b/tests/ui/did_you_mean/E0178.stderr
index 5f289da8a6c..36e4dbdf7c4 100644
--- a/tests/ui/did_you_mean/E0178.stderr
+++ b/tests/ui/did_you_mean/E0178.stderr
@@ -1,41 +1,43 @@
-error[E0178]: expected a path on the left-hand side of `+`, not `&'a Foo`
+error[E0178]: expected a path on the left-hand side of `+`
   --> $DIR/E0178.rs:6:8
    |
 LL |     w: &'a Foo + Copy,
-   |        ^^^^^^^^^^^^^^
+   |        ^^^^^^^
    |
 help: try adding parentheses
    |
 LL |     w: &'a (Foo + Copy),
    |            +          +
 
-error[E0178]: expected a path on the left-hand side of `+`, not `&'a Foo`
+error[E0178]: expected a path on the left-hand side of `+`
   --> $DIR/E0178.rs:7:8
    |
 LL |     x: &'a Foo + 'a,
-   |        ^^^^^^^^^^^^
+   |        ^^^^^^^
    |
 help: try adding parentheses
    |
 LL |     x: &'a (Foo + 'a),
    |            +        +
 
-error[E0178]: expected a path on the left-hand side of `+`, not `&'a mut Foo`
+error[E0178]: expected a path on the left-hand side of `+`
   --> $DIR/E0178.rs:8:8
    |
 LL |     y: &'a mut Foo + 'a,
-   |        ^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^
    |
 help: try adding parentheses
    |
 LL |     y: &'a mut (Foo + 'a),
    |                +        +
 
-error[E0178]: expected a path on the left-hand side of `+`, not `fn() -> Foo`
+error[E0178]: expected a path on the left-hand side of `+`
   --> $DIR/E0178.rs:9:8
    |
 LL |     z: fn() -> Foo + 'a,
-   |        ^^^^^^^^^^^^^^^^ perhaps you forgot parentheses?
+   |        ^^^^^^^^^^^-----
+   |        |
+   |        perhaps you forgot parentheses?
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr b/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr
index 4fee6cc9a22..762b37b9e9d 100644
--- a/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr
+++ b/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr
@@ -1,19 +1,19 @@
-error[E0178]: expected a path on the left-hand side of `+`, not `&Copy`
+error[E0178]: expected a path on the left-hand side of `+`
   --> $DIR/trait-object-reference-without-parens-suggestion.rs:4:12
    |
 LL |     let _: &Copy + 'static;
-   |            ^^^^^^^^^^^^^^^
+   |            ^^^^^
    |
 help: try adding parentheses
    |
 LL |     let _: &(Copy + 'static);
    |             +              +
 
-error[E0178]: expected a path on the left-hand side of `+`, not `&'static Copy`
+error[E0178]: expected a path on the left-hand side of `+`
   --> $DIR/trait-object-reference-without-parens-suggestion.rs:6:12
    |
 LL |     let _: &'static Copy + 'static;
-   |            ^^^^^^^^^^^^^^^^^^^^^^^
+   |            ^^^^^^^^^^^^^
    |
 help: try adding parentheses
    |
diff --git a/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.rs b/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.rs
index 85661c1b844..294fb6743fb 100644
--- a/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.rs
+++ b/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.rs
@@ -3,7 +3,7 @@ trait X {
 }
 
 fn foo<'a>(arg: Box<dyn X<Y('a) = &'a ()>>) {}
-  //~^ ERROR: lifetime in trait object type must be followed by `+`
+  //~^ ERROR: lifetimes must be followed by `+` to form a trait object type
   //~| ERROR: parenthesized generic arguments cannot be used
   //~| ERROR associated type takes 0 generic arguments but 1 generic argument
   //~| ERROR associated type takes 1 lifetime argument but 0 lifetime arguments
diff --git a/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.stderr b/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.stderr
index 99300ea1cb7..e18d8198c94 100644
--- a/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.stderr
+++ b/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.stderr
@@ -1,8 +1,13 @@
-error: lifetime in trait object type must be followed by `+`
+error: lifetimes must be followed by `+` to form a trait object type
   --> $DIR/gat-trait-path-parenthesised-args.rs:5:29
    |
 LL | fn foo<'a>(arg: Box<dyn X<Y('a) = &'a ()>>) {}
    |                             ^^
+   |
+help: consider adding a trait bound after the potential lifetime bound
+   |
+LL | fn foo<'a>(arg: Box<dyn X<Y('a + /* Trait */) = &'a ()>>) {}
+   |                                +++++++++++++
 
 error: parenthesized generic arguments cannot be used in associated type constraints
   --> $DIR/gat-trait-path-parenthesised-args.rs:5:27
diff --git a/tests/ui/impl-trait/impl-trait-plus-priority.rs b/tests/ui/impl-trait/impl-trait-plus-priority.rs
index 5575493a17d..8f76d412662 100644
--- a/tests/ui/impl-trait/impl-trait-plus-priority.rs
+++ b/tests/ui/impl-trait/impl-trait-plus-priority.rs
@@ -27,7 +27,7 @@ type A = fn() -> impl A + B;
 type A = fn() -> dyn A + B;
 //~^ ERROR ambiguous `+` in a type
 type A = fn() -> A + B;
-//~^ ERROR expected a path on the left-hand side of `+`, not `fn() -> A`
+//~^ ERROR expected a path on the left-hand side of `+`
 
 type A = Fn() -> impl A +;
 //~^ ERROR ambiguous `+` in a type
@@ -44,6 +44,6 @@ type A = &impl A + B;
 type A = &dyn A + B;
 //~^ ERROR ambiguous `+` in a type
 type A = &A + B;
-//~^ ERROR expected a path on the left-hand side of `+`, not `&A`
+//~^ ERROR expected a path on the left-hand side of `+`
 
 fn main() {}
diff --git a/tests/ui/impl-trait/impl-trait-plus-priority.stderr b/tests/ui/impl-trait/impl-trait-plus-priority.stderr
index 03e7910095a..16120657960 100644
--- a/tests/ui/impl-trait/impl-trait-plus-priority.stderr
+++ b/tests/ui/impl-trait/impl-trait-plus-priority.stderr
@@ -31,11 +31,13 @@ help: try adding parentheses
 LL | type A = fn() -> (dyn A + B);
    |                  +         +
 
-error[E0178]: expected a path on the left-hand side of `+`, not `fn() -> A`
+error[E0178]: expected a path on the left-hand side of `+`
   --> $DIR/impl-trait-plus-priority.rs:29:10
    |
 LL | type A = fn() -> A + B;
-   |          ^^^^^^^^^^^^^ perhaps you forgot parentheses?
+   |          ^^^^^^^^^----
+   |          |
+   |          perhaps you forgot parentheses?
 
 error: ambiguous `+` in a type
   --> $DIR/impl-trait-plus-priority.rs:32:18
@@ -103,11 +105,11 @@ help: try adding parentheses
 LL | type A = &(dyn A + B);
    |           +         +
 
-error[E0178]: expected a path on the left-hand side of `+`, not `&A`
+error[E0178]: expected a path on the left-hand side of `+`
   --> $DIR/impl-trait-plus-priority.rs:46:10
    |
 LL | type A = &A + B;
-   |          ^^^^^^
+   |          ^^
    |
 help: try adding parentheses
    |
diff --git a/tests/ui/parser/issues/issue-73568-lifetime-after-mut.rs b/tests/ui/parser/issues/issue-73568-lifetime-after-mut.rs
index cf754a6854e..782a46ab060 100644
--- a/tests/ui/parser/issues/issue-73568-lifetime-after-mut.rs
+++ b/tests/ui/parser/issues/issue-73568-lifetime-after-mut.rs
@@ -12,7 +12,7 @@ mac!('a);
 
 // avoid false positives
 fn y<'a>(y: &mut 'a + Send) {
-    //~^ ERROR expected a path on the left-hand side of `+`, not `&mut 'a`
+    //~^ ERROR expected a path on the left-hand side of `+`
     //~| ERROR at least one trait is required for an object type
     let z = y as &mut 'a + Send;
     //~^ ERROR expected value, found trait `Send`
diff --git a/tests/ui/parser/issues/issue-73568-lifetime-after-mut.stderr b/tests/ui/parser/issues/issue-73568-lifetime-after-mut.stderr
index 6b8f8e4fe4e..ae1ed72853d 100644
--- a/tests/ui/parser/issues/issue-73568-lifetime-after-mut.stderr
+++ b/tests/ui/parser/issues/issue-73568-lifetime-after-mut.stderr
@@ -10,11 +10,11 @@ LL - fn x<'a>(x: &mut 'a i32){}
 LL + fn x<'a>(x: &'a mut i32){}
    |
 
-error[E0178]: expected a path on the left-hand side of `+`, not `&mut 'a`
+error[E0178]: expected a path on the left-hand side of `+`
   --> $DIR/issue-73568-lifetime-after-mut.rs:14:13
    |
 LL | fn y<'a>(y: &mut 'a + Send) {
-   |             ^^^^^^^^^^^^^^
+   |             ^^^^^^^
    |
 help: try adding parentheses
    |
diff --git a/tests/ui/parser/macro/trait-object-macro-matcher.e2015.stderr b/tests/ui/parser/macro/trait-object-macro-matcher.e2015.stderr
new file mode 100644
index 00000000000..f2db351de4a
--- /dev/null
+++ b/tests/ui/parser/macro/trait-object-macro-matcher.e2015.stderr
@@ -0,0 +1,32 @@
+error: lifetimes must be followed by `+` to form a trait object type
+  --> $DIR/trait-object-macro-matcher.rs:17:8
+   |
+LL |     m!('static);
+   |        ^^^^^^^
+   |
+help: consider adding a trait bound after the potential lifetime bound
+   |
+LL |     m!('static + /* Trait */);
+   |                +++++++++++++
+
+error: lifetimes must be followed by `+` to form a trait object type
+  --> $DIR/trait-object-macro-matcher.rs:17:8
+   |
+LL |     m!('static);
+   |        ^^^^^^^
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+help: consider adding a trait bound after the potential lifetime bound
+   |
+LL |     m!('static + /* Trait */);
+   |                +++++++++++++
+
+error[E0224]: at least one trait is required for an object type
+  --> $DIR/trait-object-macro-matcher.rs:17:8
+   |
+LL |     m!('static);
+   |        ^^^^^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0224`.
diff --git a/tests/ui/parser/macro/trait-object-macro-matcher.e2021.stderr b/tests/ui/parser/macro/trait-object-macro-matcher.e2021.stderr
new file mode 100644
index 00000000000..7d9e8d795d1
--- /dev/null
+++ b/tests/ui/parser/macro/trait-object-macro-matcher.e2021.stderr
@@ -0,0 +1,16 @@
+error: expected type, found lifetime
+  --> $DIR/trait-object-macro-matcher.rs:17:8
+   |
+LL |     m!('static);
+   |        ^^^^^^^ expected type
+
+error: expected type, found lifetime
+  --> $DIR/trait-object-macro-matcher.rs:17:8
+   |
+LL |     m!('static);
+   |        ^^^^^^^ expected type
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/parser/macro/trait-object-macro-matcher.rs b/tests/ui/parser/macro/trait-object-macro-matcher.rs
index d4ec199070e..ba61752fe40 100644
--- a/tests/ui/parser/macro/trait-object-macro-matcher.rs
+++ b/tests/ui/parser/macro/trait-object-macro-matcher.rs
@@ -1,6 +1,10 @@
 // A single lifetime is not parsed as a type.
 // `ty` matcher in particular doesn't accept a single lifetime
 
+//@ revisions: e2015 e2021
+//@[e2015] edition: 2015
+//@[e2021] edition: 2021
+
 macro_rules! m {
     ($t: ty) => {
         let _: $t;
@@ -8,8 +12,10 @@ macro_rules! m {
 }
 
 fn main() {
+    //[e2021]~vv ERROR expected type, found lifetime
+    //[e2021]~v ERROR expected type, found lifetime
     m!('static);
-    //~^ ERROR lifetime in trait object type must be followed by `+`
-    //~| ERROR lifetime in trait object type must be followed by `+`
-    //~| ERROR at least one trait is required for an object type
+    //[e2015]~^ ERROR lifetimes must be followed by `+` to form a trait object type
+    //[e2015]~| ERROR lifetimes must be followed by `+` to form a trait object type
+    //[e2015]~| ERROR at least one trait is required for an object type
 }
diff --git a/tests/ui/parser/macro/trait-object-macro-matcher.stderr b/tests/ui/parser/macro/trait-object-macro-matcher.stderr
deleted file mode 100644
index 81dca6f71c4..00000000000
--- a/tests/ui/parser/macro/trait-object-macro-matcher.stderr
+++ /dev/null
@@ -1,23 +0,0 @@
-error: lifetime in trait object type must be followed by `+`
-  --> $DIR/trait-object-macro-matcher.rs:11:8
-   |
-LL |     m!('static);
-   |        ^^^^^^^
-
-error: lifetime in trait object type must be followed by `+`
-  --> $DIR/trait-object-macro-matcher.rs:11:8
-   |
-LL |     m!('static);
-   |        ^^^^^^^
-   |
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error[E0224]: at least one trait is required for an object type
-  --> $DIR/trait-object-macro-matcher.rs:11:8
-   |
-LL |     m!('static);
-   |        ^^^^^^^
-
-error: aborting due to 3 previous errors
-
-For more information about this error, try `rustc --explain E0224`.
diff --git a/tests/ui/parser/recover/recover-ampersand-less-ref-ty.rs b/tests/ui/parser/recover/recover-ampersand-less-ref-ty.rs
new file mode 100644
index 00000000000..8f1a42473b5
--- /dev/null
+++ b/tests/ui/parser/recover/recover-ampersand-less-ref-ty.rs
@@ -0,0 +1,13 @@
+//@ edition: 2021
+
+struct Entity<'a> {
+    name: 'a str, //~ ERROR expected type, found lifetime
+    //~^ HELP you might have meant to write a reference type here
+}
+
+struct Buffer<'buf> {
+    bytes: 'buf mut [u8], //~ ERROR expected type, found lifetime
+    //~^ HELP you might have meant to write a reference type here
+}
+
+fn main() {}
diff --git a/tests/ui/parser/recover/recover-ampersand-less-ref-ty.stderr b/tests/ui/parser/recover/recover-ampersand-less-ref-ty.stderr
new file mode 100644
index 00000000000..033348b2c40
--- /dev/null
+++ b/tests/ui/parser/recover/recover-ampersand-less-ref-ty.stderr
@@ -0,0 +1,24 @@
+error: expected type, found lifetime
+  --> $DIR/recover-ampersand-less-ref-ty.rs:4:11
+   |
+LL |     name: 'a str,
+   |           ^^ expected type
+   |
+help: you might have meant to write a reference type here
+   |
+LL |     name: &'a str,
+   |           +
+
+error: expected type, found lifetime
+  --> $DIR/recover-ampersand-less-ref-ty.rs:9:12
+   |
+LL |     bytes: 'buf mut [u8],
+   |            ^^^^ expected type
+   |
+help: you might have meant to write a reference type here
+   |
+LL |     bytes: &'buf mut [u8],
+   |            +
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/parser/trait-object-bad-parens.rs b/tests/ui/parser/trait-object-bad-parens.rs
index 8e267c7448f..bb047a4b431 100644
--- a/tests/ui/parser/trait-object-bad-parens.rs
+++ b/tests/ui/parser/trait-object-bad-parens.rs
@@ -5,12 +5,8 @@
 auto trait Auto {}
 
 fn main() {
-    let _: Box<((Auto)) + Auto>;
-    //~^ ERROR expected a path on the left-hand side of `+`, not `((Auto))`
-    let _: Box<(Auto + Auto) + Auto>;
-    //~^ ERROR expected a path on the left-hand side of `+`, not `(Auto + Auto)`
-    let _: Box<(Auto +) + Auto>;
-    //~^ ERROR expected a path on the left-hand side of `+`, not `(Auto)`
-    let _: Box<(dyn Auto) + Auto>;
-    //~^ ERROR expected a path on the left-hand side of `+`, not `(dyn Auto)`
+    let _: Box<((Auto)) + Auto>; //~ ERROR expected a path on the left-hand side of `+`
+    let _: Box<(Auto + Auto) + Auto>; //~ ERROR expected a path on the left-hand side of `+`
+    let _: Box<(Auto +) + Auto>; //~ ERROR expected a path on the left-hand side of `+`
+    let _: Box<(dyn Auto) + Auto>; //~ ERROR expected a path on the left-hand side of `+`
 }
diff --git a/tests/ui/parser/trait-object-bad-parens.stderr b/tests/ui/parser/trait-object-bad-parens.stderr
index 74e484eebee..7c2559ce89f 100644
--- a/tests/ui/parser/trait-object-bad-parens.stderr
+++ b/tests/ui/parser/trait-object-bad-parens.stderr
@@ -1,26 +1,26 @@
-error[E0178]: expected a path on the left-hand side of `+`, not `((Auto))`
+error[E0178]: expected a path on the left-hand side of `+`
   --> $DIR/trait-object-bad-parens.rs:8:16
    |
 LL |     let _: Box<((Auto)) + Auto>;
-   |                ^^^^^^^^^^^^^^^ expected a path
+   |                ^^^^^^^^ expected a path
 
-error[E0178]: expected a path on the left-hand side of `+`, not `(Auto + Auto)`
-  --> $DIR/trait-object-bad-parens.rs:10:16
+error[E0178]: expected a path on the left-hand side of `+`
+  --> $DIR/trait-object-bad-parens.rs:9:16
    |
 LL |     let _: Box<(Auto + Auto) + Auto>;
-   |                ^^^^^^^^^^^^^^^^^^^^ expected a path
+   |                ^^^^^^^^^^^^^ expected a path
 
-error[E0178]: expected a path on the left-hand side of `+`, not `(Auto)`
-  --> $DIR/trait-object-bad-parens.rs:12:16
+error[E0178]: expected a path on the left-hand side of `+`
+  --> $DIR/trait-object-bad-parens.rs:10:16
    |
 LL |     let _: Box<(Auto +) + Auto>;
-   |                ^^^^^^^^^^^^^^^ expected a path
+   |                ^^^^^^^^ expected a path
 
-error[E0178]: expected a path on the left-hand side of `+`, not `(dyn Auto)`
-  --> $DIR/trait-object-bad-parens.rs:14:16
+error[E0178]: expected a path on the left-hand side of `+`
+  --> $DIR/trait-object-bad-parens.rs:11:16
    |
 LL |     let _: Box<(dyn Auto) + Auto>;
-   |                ^^^^^^^^^^^^^^^^^ expected a path
+   |                ^^^^^^^^^^ expected a path
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/parser/trait-object-lifetime-parens.stderr b/tests/ui/parser/trait-object-lifetime-parens.e2015.stderr
index 280c0e40c64..cf0b3d77f5b 100644
--- a/tests/ui/parser/trait-object-lifetime-parens.stderr
+++ b/tests/ui/parser/trait-object-lifetime-parens.e2015.stderr
@@ -1,5 +1,5 @@
 error: parenthesized lifetime bounds are not supported
-  --> $DIR/trait-object-lifetime-parens.rs:5:21
+  --> $DIR/trait-object-lifetime-parens.rs:9:21
    |
 LL | fn f<'a, T: Trait + ('a)>() {}
    |                     ^^^^
@@ -11,7 +11,7 @@ LL + fn f<'a, T: Trait + 'a>() {}
    |
 
 error: parenthesized lifetime bounds are not supported
-  --> $DIR/trait-object-lifetime-parens.rs:8:24
+  --> $DIR/trait-object-lifetime-parens.rs:12:24
    |
 LL |     let _: Box<Trait + ('a)>;
    |                        ^^^^
@@ -22,11 +22,16 @@ LL -     let _: Box<Trait + ('a)>;
 LL +     let _: Box<Trait + 'a>;
    |
 
-error: lifetime in trait object type must be followed by `+`
-  --> $DIR/trait-object-lifetime-parens.rs:10:17
+error: lifetimes must be followed by `+` to form a trait object type
+  --> $DIR/trait-object-lifetime-parens.rs:16:17
    |
 LL |     let _: Box<('a) + Trait>;
    |                 ^^
+   |
+help: consider adding a trait bound after the potential lifetime bound
+   |
+LL |     let _: Box<('a + /* Trait */) + Trait>;
+   |                    +++++++++++++
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/parser/trait-object-lifetime-parens.e2021.stderr b/tests/ui/parser/trait-object-lifetime-parens.e2021.stderr
new file mode 100644
index 00000000000..b65c079788a
--- /dev/null
+++ b/tests/ui/parser/trait-object-lifetime-parens.e2021.stderr
@@ -0,0 +1,51 @@
+error: parenthesized lifetime bounds are not supported
+  --> $DIR/trait-object-lifetime-parens.rs:9:21
+   |
+LL | fn f<'a, T: Trait + ('a)>() {}
+   |                     ^^^^
+   |
+help: remove the parentheses
+   |
+LL - fn f<'a, T: Trait + ('a)>() {}
+LL + fn f<'a, T: Trait + 'a>() {}
+   |
+
+error: parenthesized lifetime bounds are not supported
+  --> $DIR/trait-object-lifetime-parens.rs:12:24
+   |
+LL |     let _: Box<Trait + ('a)>;
+   |                        ^^^^
+   |
+help: remove the parentheses
+   |
+LL -     let _: Box<Trait + ('a)>;
+LL +     let _: Box<Trait + 'a>;
+   |
+
+error: expected type, found lifetime
+  --> $DIR/trait-object-lifetime-parens.rs:16:17
+   |
+LL |     let _: Box<('a) + Trait>;
+   |                 ^^ expected type
+
+error[E0178]: expected a path on the left-hand side of `+`
+  --> $DIR/trait-object-lifetime-parens.rs:16:16
+   |
+LL |     let _: Box<('a) + Trait>;
+   |                ^^^^ expected a path
+
+error[E0782]: expected a type, found a trait
+  --> $DIR/trait-object-lifetime-parens.rs:12:16
+   |
+LL |     let _: Box<Trait + ('a)>;
+   |                ^^^^^^^^^^^^
+   |
+help: you can add the `dyn` keyword if you want a trait object
+   |
+LL |     let _: Box<dyn Trait + ('a)>;
+   |                +++
+
+error: aborting due to 5 previous errors
+
+Some errors have detailed explanations: E0178, E0782.
+For more information about an error, try `rustc --explain E0178`.
diff --git a/tests/ui/parser/trait-object-lifetime-parens.rs b/tests/ui/parser/trait-object-lifetime-parens.rs
index f44ebe5ba5b..0ff4660bb0d 100644
--- a/tests/ui/parser/trait-object-lifetime-parens.rs
+++ b/tests/ui/parser/trait-object-lifetime-parens.rs
@@ -1,4 +1,8 @@
-#![allow(bare_trait_objects)]
+//@ revisions: e2015 e2021
+//@[e2015] edition: 2015
+//@[e2021] edition: 2021
+
+#![cfg_attr(e2015, allow(bare_trait_objects))]
 
 trait Trait {}
 
@@ -6,8 +10,12 @@ fn f<'a, T: Trait + ('a)>() {} //~ ERROR parenthesized lifetime bounds are not s
 
 fn check<'a>() {
     let _: Box<Trait + ('a)>; //~ ERROR parenthesized lifetime bounds are not supported
-    // FIXME: It'd be great if we could add suggestion to the following case.
-    let _: Box<('a) + Trait>; //~ ERROR lifetime in trait object type must be followed by `+`
+    //[e2021]~^ ERROR expected a type, found a trait
+    // FIXME: It'd be great if we could suggest removing the parentheses here too.
+    //[e2015]~v ERROR lifetimes must be followed by `+` to form a trait object type
+    let _: Box<('a) + Trait>;
+    //[e2021]~^ ERROR expected type, found lifetime
+    //[e2021]~| ERROR expected a path on the left-hand side of `+`
 }
 
 fn main() {}
diff --git a/tests/ui/parser/trait-object-polytrait-priority.rs b/tests/ui/parser/trait-object-polytrait-priority.rs
index e7f085104ae..85568f0fe1b 100644
--- a/tests/ui/parser/trait-object-polytrait-priority.rs
+++ b/tests/ui/parser/trait-object-polytrait-priority.rs
@@ -4,6 +4,6 @@ trait Trait<'a> {}
 
 fn main() {
     let _: &for<'a> Trait<'a> + 'static;
-    //~^ ERROR expected a path on the left-hand side of `+`, not `&for<'a> Trait<'a>`
+    //~^ ERROR expected a path on the left-hand side of `+`
     //~| HELP try adding parentheses
 }
diff --git a/tests/ui/parser/trait-object-polytrait-priority.stderr b/tests/ui/parser/trait-object-polytrait-priority.stderr
index 8cb564e7930..a291a8e229c 100644
--- a/tests/ui/parser/trait-object-polytrait-priority.stderr
+++ b/tests/ui/parser/trait-object-polytrait-priority.stderr
@@ -1,8 +1,8 @@
-error[E0178]: expected a path on the left-hand side of `+`, not `&for<'a> Trait<'a>`
+error[E0178]: expected a path on the left-hand side of `+`
   --> $DIR/trait-object-polytrait-priority.rs:6:12
    |
 LL |     let _: &for<'a> Trait<'a> + 'static;
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |            ^^^^^^^^^^^^^^^^^^
    |
 help: try adding parentheses
    |