about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_ast/src/ast.rs13
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs7
-rw-r--r--compiler/rustc_ast_passes/messages.ftl4
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs9
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs29
-rw-r--r--compiler/rustc_codegen_llvm/src/back/lto.rs3
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs5
-rw-r--r--compiler/rustc_codegen_llvm/src/lib.rs1
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs67
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp4
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs10
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs60
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs271
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs36
-rw-r--r--src/librustdoc/html/static/js/search.js9
-rw-r--r--src/tools/tidy/src/ui_tests.rs2
-rw-r--r--tests/incremental/thinlto/cgu_invalidated_via_import.rs2
-rw-r--r--tests/incremental/thinlto/cgu_keeps_identical_fn.rs2
-rw-r--r--tests/incremental/thinlto/independent_cgus_dont_affect_each_other.rs2
-rw-r--r--tests/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr7
-rw-r--r--tests/ui/associated-types/bound-lifetime-in-return-only.elision.stderr7
-rw-r--r--tests/ui/attributes/issue-90873.rs4
-rw-r--r--tests/ui/attributes/issue-90873.stderr9
-rw-r--r--tests/ui/attributes/key-value-expansion-on-mac.rs4
-rw-r--r--tests/ui/attributes/key-value-expansion-on-mac.stderr2
-rw-r--r--tests/ui/attributes/key-value-expansion.rs6
-rw-r--r--tests/ui/attributes/key-value-expansion.stderr6
-rw-r--r--tests/ui/attributes/unused-item-in-attr.rs2
-rw-r--r--tests/ui/attributes/unused-item-in-attr.stderr4
-rw-r--r--tests/ui/c-variadic/variadic-ffi-6.stderr11
-rw-r--r--tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.rs25
-rw-r--r--tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.stderr64
-rw-r--r--tests/ui/consts/issue-90878-2.rs2
-rw-r--r--tests/ui/consts/issue-90878-2.stderr2
-rw-r--r--tests/ui/feature-gates/feature-gate-never_patterns.rs61
-rw-r--r--tests/ui/feature-gates/feature-gate-never_patterns.stderr82
-rw-r--r--tests/ui/foreign-fn-return-lifetime.fixed8
-rw-r--r--tests/ui/foreign-fn-return-lifetime.rs2
-rw-r--r--tests/ui/foreign-fn-return-lifetime.stderr9
-rw-r--r--tests/ui/generic-associated-types/issue-70304.stderr2
-rw-r--r--tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr2
-rw-r--r--tests/ui/issues/issue-13497.stderr6
-rw-r--r--tests/ui/lifetimes/issue-26638.stderr16
-rw-r--r--tests/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr29
-rw-r--r--tests/ui/malformed/malformed-interpolated.rs4
-rw-r--r--tests/ui/malformed/malformed-interpolated.stderr4
-rw-r--r--tests/ui/parser/bad-lit-suffixes.rs5
-rw-r--r--tests/ui/parser/bad-lit-suffixes.stderr27
-rw-r--r--tests/ui/parser/issues/issue-104620.rs2
-rw-r--r--tests/ui/parser/issues/issue-104620.stderr6
-rw-r--r--tests/ui/parser/match-arm-without-body.rs2
-rw-r--r--tests/ui/parser/match-arm-without-body.stderr16
-rw-r--r--tests/ui/self/elision/nested-item.stderr11
-rw-r--r--tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr66
-rw-r--r--tests/ui/suggestions/impl-trait-missing-lifetime.stderr22
-rw-r--r--tests/ui/suggestions/missing-lifetime-specifier.stderr10
-rw-r--r--tests/ui/suggestions/return-elided-lifetime.stderr22
-rw-r--r--tests/ui/underscore-lifetime/underscore-lifetime-binders.stderr2
58 files changed, 939 insertions, 168 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 190fae95652..5755ae8a8bc 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -676,6 +676,19 @@ impl Pat {
         });
         could_be_never_pattern
     }
+
+    /// Whether this contains a `!` pattern. This in particular means that a feature gate error will
+    /// be raised if the feature is off. Used to avoid gating the feature twice.
+    pub fn contains_never_pattern(&self) -> bool {
+        let mut contains_never_pattern = false;
+        self.walk(&mut |pat| {
+            if matches!(pat.kind, PatKind::Never) {
+                contains_never_pattern = true;
+            }
+            true
+        });
+        contains_never_pattern
+    }
 }
 
 /// A single field in a struct pattern.
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index e9f88d50937..a44b408feec 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -581,8 +581,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
         } else {
             // Either `body.is_none()` or `is_never_pattern` here.
             if !is_never_pattern {
-                let suggestion = span.shrink_to_hi();
-                self.tcx.sess.emit_err(MatchArmWithNoBody { span, suggestion });
+                if self.tcx.features().never_patterns {
+                    // If the feature is off we already emitted the error after parsing.
+                    let suggestion = span.shrink_to_hi();
+                    self.tcx.sess.emit_err(MatchArmWithNoBody { span, suggestion });
+                }
             } else if let Some(body) = &arm.body {
                 self.tcx.sess.emit_err(NeverPatternWithBody { span: body.span });
                 guard = None;
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index 876126b02ea..28bd6c2111b 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -174,6 +174,10 @@ ast_passes_item_underscore = `{$kind}` items in this context need a name
 ast_passes_keyword_lifetime =
     lifetimes cannot use keyword names
 
+ast_passes_match_arm_with_no_body =
+    `match` arm with no body
+    .suggestion = add a body after the pattern
+
 ast_passes_module_nonascii = trying to load file for module `{$name}` with non-ascii identifier name
     .help = consider using the `#[path]` attribute to specify filesystem path
 
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 7f6fcb49317..928bf19759a 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -758,3 +758,12 @@ pub struct AnonStructOrUnionNotAllowed {
     pub span: Span,
     pub struct_or_union: &'static str,
 }
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_match_arm_with_no_body)]
+pub struct MatchArmWithNoBody {
+    #[primary_span]
+    pub span: Span,
+    #[suggestion(code = " => todo!(),", applicability = "has-placeholders")]
+    pub suggestion: Span,
+}
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 897f35a5c4a..ac55c6cabd0 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -554,7 +554,34 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
     gate_all!(explicit_tail_calls, "`become` expression is experimental");
     gate_all!(generic_const_items, "generic const items are experimental");
     gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented");
-    gate_all!(never_patterns, "`!` patterns are experimental");
+
+    if !visitor.features.never_patterns {
+        if let Some(spans) = spans.get(&sym::never_patterns) {
+            for &span in spans {
+                if span.allows_unstable(sym::never_patterns) {
+                    continue;
+                }
+                let sm = sess.source_map();
+                // We gate two types of spans: the span of a `!` pattern, and the span of a
+                // match arm without a body. For the latter we want to give the user a normal
+                // error.
+                if let Ok(snippet) = sm.span_to_snippet(span)
+                    && snippet == "!"
+                {
+                    feature_err(
+                        &sess.parse_sess,
+                        sym::never_patterns,
+                        span,
+                        "`!` patterns are experimental",
+                    )
+                    .emit();
+                } else {
+                    let suggestion = span.shrink_to_hi();
+                    sess.emit_err(errors::MatchArmWithNoBody { span, suggestion });
+                }
+            }
+        }
+    }
 
     if !visitor.features.negative_bounds {
         for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {
diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs
index abc33a04598..97444f1a5bd 100644
--- a/compiler/rustc_codegen_llvm/src/back/lto.rs
+++ b/compiler/rustc_codegen_llvm/src/back/lto.rs
@@ -816,6 +816,9 @@ impl ThinLTOKeysMap {
         use std::io::Write;
         let file = File::create(path)?;
         let mut writer = io::BufWriter::new(file);
+        // The entries are loaded back into a hash map in `load_from_file()`, so
+        // the order in which we write them to file here does not matter.
+        #[allow(rustc::potential_query_instability)]
         for (module, key) in &self.keys {
             writeln!(writer, "{module} {key}")?;
         }
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
index 51df14df644..33bfde03a31 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
@@ -58,6 +58,11 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
         return;
     }
 
+    // The entries of the map are only used to get a list of all files with
+    // coverage info. In the end the list of files is passed into
+    // `GlobalFileTable::new()` which internally do `.sort_unstable_by()`, so
+    // the iteration order here does not matter.
+    #[allow(rustc::potential_query_instability)]
     let function_coverage_entries = function_coverage_map
         .into_iter()
         .map(|(instance, function_coverage)| (instance, function_coverage.into_finished()))
diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs
index 33b19ab362a..116108ae5a9 100644
--- a/compiler/rustc_codegen_llvm/src/lib.rs
+++ b/compiler/rustc_codegen_llvm/src/lib.rs
@@ -17,7 +17,6 @@
 #![feature(never_type)]
 #![feature(impl_trait_in_assoc_type)]
 #![recursion_limit = "256"]
-#![allow(rustc::potential_query_instability)]
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
 
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index de0abe04611..a25cfe68e0d 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -345,12 +345,34 @@ pub struct FutureIncompatibleInfo {
 }
 
 /// The reason for future incompatibility
+///
+/// Future-incompatible lints come in roughly two categories:
+///
+/// 1. There was a mistake in the compiler (such as a soundness issue), and
+///    we're trying to fix it, but it may be a breaking change.
+/// 2. A change across an Edition boundary, typically used for the
+///    introduction of new language features that can't otherwise be
+///    introduced in a backwards-compatible way.
+///
+/// See <https://rustc-dev-guide.rust-lang.org/bug-fix-procedure.html> and
+/// <https://rustc-dev-guide.rust-lang.org/diagnostics.html#future-incompatible-lints>
+/// for more information.
 #[derive(Copy, Clone, Debug)]
 pub enum FutureIncompatibilityReason {
     /// This will be an error in a future release for all editions
     ///
     /// This will *not* show up in cargo's future breakage report.
     /// The warning will hence only be seen in local crates, not in dependencies.
+    ///
+    /// Choose this variant when you are first introducing a "future
+    /// incompatible" warning that is intended to eventually be fixed in the
+    /// future. This allows crate developers an opportunity to fix the warning
+    /// before blasting all dependents with a warning they can't fix
+    /// (dependents have to wait for a new release of the affected crate to be
+    /// published).
+    ///
+    /// After a lint has been in this state for a while, consider graduating
+    /// it to [`FutureIncompatibilityReason::FutureReleaseErrorReportInDeps`].
     FutureReleaseErrorDontReportInDeps,
     /// This will be an error in a future release, and
     /// Cargo should create a report even for dependencies
@@ -358,17 +380,62 @@ pub enum FutureIncompatibilityReason {
     /// This is the *only* reason that will make future incompatibility warnings show up in cargo's
     /// reports. All other future incompatibility warnings are not visible when they occur in a
     /// dependency.
+    ///
+    /// Choose this variant after the lint has been sitting in the
+    /// [`FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps`]
+    /// state for a while, and you feel like it is ready to graduate to
+    /// warning everyone. It is a good signal that it is ready if you can
+    /// determine that all or most affected crates on crates.io have been
+    /// updated.
+    ///
+    /// After some period of time, lints with this variant can be turned into
+    /// hard errors (and the lint removed). Preferably when there is some
+    /// confidence that the number of impacted projects is very small (few
+    /// should have a broken dependency in their dependency tree).
     FutureReleaseErrorReportInDeps,
     /// Code that changes meaning in some way in a
     /// future release.
+    ///
+    /// Choose this variant when the semantics of existing code is changing,
+    /// (as opposed to
+    /// [`FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps`],
+    /// which is for when code is going to be rejected in the future).
     FutureReleaseSemanticsChange,
     /// Previously accepted code that will become an
     /// error in the provided edition
+    ///
+    /// Choose this variant for code that you want to start rejecting across
+    /// an edition boundary. This will automatically include the lint in the
+    /// `rust-20xx-compatibility` lint group, which is used by `cargo fix
+    /// --edition` to do migrations. The lint *should* be auto-fixable with
+    /// [`Applicability::MachineApplicable`].
+    ///
+    /// The lint can either be `Allow` or `Warn` by default. If it is `Allow`,
+    /// users usually won't see this warning unless they are doing an edition
+    /// migration manually or there is a problem during the migration (cargo's
+    /// automatic migrations will force the level to `Warn`). If it is `Warn`
+    /// by default, users on all editions will see this warning (only do this
+    /// if you think it is important for everyone to be aware of the change,
+    /// and to encourage people to update their code on all editions).
+    ///
+    /// See also [`FutureIncompatibilityReason::EditionSemanticsChange`] if
+    /// you have code that is changing semantics across the edition (as
+    /// opposed to being rejected).
     EditionError(Edition),
     /// Code that changes meaning in some way in
     /// the provided edition
+    ///
+    /// This is the same as [`FutureIncompatibilityReason::EditionError`],
+    /// except for situations where the semantics change across an edition. It
+    /// slightly changes the text of the diagnostic, but is otherwise the
+    /// same.
     EditionSemanticsChange(Edition),
     /// A custom reason.
+    ///
+    /// Choose this variant if the built-in text of the diagnostic of the
+    /// other variants doesn't match your situation. This is behaviorally
+    /// equivalent to
+    /// [`FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps`].
     Custom(&'static str),
 }
 
diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
index 2601d96b8c8..556dc890a84 100644
--- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
@@ -37,7 +37,9 @@
 #include "llvm/Transforms/Utils/FunctionImportUtils.h"
 #include "llvm/LTO/LTO.h"
 #include "llvm/Bitcode/BitcodeWriter.h"
-
+#if LLVM_VERSION_GE(18, 0)
+#include "llvm/TargetParser/Host.h"
+#endif
 #include "llvm/Transforms/Instrumentation.h"
 #include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
 #include "llvm/Support/TimeProfiler.h"
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 3c0627526be..5b0011e9f70 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -2918,7 +2918,15 @@ impl<'a> Parser<'a> {
             let mut result = if !is_fat_arrow && !is_almost_fat_arrow {
                 // A pattern without a body, allowed for never patterns.
                 arm_body = None;
-                this.expect_one_of(&[token::Comma], &[token::CloseDelim(Delimiter::Brace)])
+                this.expect_one_of(&[token::Comma], &[token::CloseDelim(Delimiter::Brace)]).map(
+                    |x| {
+                        // Don't gate twice
+                        if !pat.contains_never_pattern() {
+                            this.sess.gated_spans.gate(sym::never_patterns, pat.span);
+                        }
+                        x
+                    },
+                )
             } else {
                 if let Err(mut err) = this.expect(&token::FatArrow) {
                     // We might have a `=>` -> `=` or `->` typo (issue #89396).
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index cbe75b3dab6..81055431f64 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -6,9 +6,9 @@ use rustc_ast::token::Delimiter;
 use rustc_ast::tokenstream::DelimSpan;
 use rustc_ast::MetaItemKind;
 use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, Attribute, DelimArgs, MetaItem};
-use rustc_ast_pretty::pprust;
 use rustc_errors::{Applicability, FatalError, PResult};
 use rustc_feature::{AttributeTemplate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
+use rustc_session::errors::report_lit_error;
 use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
 use rustc_session::parse::ParseSess;
 use rustc_span::{sym, Span, Symbol};
@@ -51,28 +51,44 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta
                 MetaItemKind::List(nmis)
             }
             AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => {
-                if let ast::ExprKind::Lit(token_lit) = expr.kind
-                    && let Ok(lit) = ast::MetaItemLit::from_token_lit(token_lit, expr.span)
-                {
-                    if token_lit.suffix.is_some() {
-                        let mut err = sess.span_diagnostic.struct_span_err(
-                            expr.span,
-                            "suffixed literals are not allowed in attributes",
-                        );
-                        err.help(
-                            "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \
-                            use an unsuffixed version (`1`, `1.0`, etc.)",
-                        );
-                        return Err(err);
-                    } else {
-                        MetaItemKind::NameValue(lit)
-                    }
+                if let ast::ExprKind::Lit(token_lit) = expr.kind {
+                    let res = ast::MetaItemLit::from_token_lit(token_lit, expr.span);
+                    let res = match res {
+                        Ok(lit) => {
+                            if token_lit.suffix.is_some() {
+                                let mut err = sess.span_diagnostic.struct_span_err(
+                                    expr.span,
+                                    "suffixed literals are not allowed in attributes",
+                                );
+                                err.help(
+                                    "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \
+                                    use an unsuffixed version (`1`, `1.0`, etc.)",
+                                );
+                                return Err(err);
+                            } else {
+                                MetaItemKind::NameValue(lit)
+                            }
+                        }
+                        Err(err) => {
+                            report_lit_error(sess, err, token_lit, expr.span);
+                            let lit = ast::MetaItemLit {
+                                symbol: token_lit.symbol,
+                                suffix: token_lit.suffix,
+                                kind: ast::LitKind::Err,
+                                span: expr.span,
+                            };
+                            MetaItemKind::NameValue(lit)
+                        }
+                    };
+                    res
                 } else {
-                    // The non-error case can happen with e.g. `#[foo = 1+1]`. The error case can
-                    // happen with e.g. `#[foo = include_str!("nonexistent-file.rs")]`; in that
-                    // case we delay the error because an earlier error will have already been
-                    // reported.
-                    let msg = format!("unexpected expression: `{}`", pprust::expr_to_string(expr));
+                    // Example cases:
+                    // - `#[foo = 1+1]`: results in `ast::ExprKind::BinOp`.
+                    // - `#[foo = include_str!("nonexistent-file.rs")]`:
+                    //   results in `ast::ExprKind::Err`. In that case we delay
+                    //   the error because an earlier error will have already
+                    //   been reported.
+                    let msg = format!("attribute value must be a literal");
                     let mut err = sess.span_diagnostic.struct_span_err(expr.span, msg);
                     if let ast::ExprKind::Err = expr.kind {
                         err.downgrade_to_delayed_bug();
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index df30c185c60..61c7846ea61 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -8,7 +8,7 @@ use crate::{PathResult, PathSource, Segment};
 use rustc_hir::def::Namespace::{self, *};
 
 use rustc_ast::ptr::P;
-use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt};
+use rustc_ast::visit::{walk_ty, FnCtxt, FnKind, LifetimeCtxt, Visitor};
 use rustc_ast::{
     self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
     MethodCall, NodeId, Path, Ty, TyKind, DUMMY_NODE_ID,
@@ -2830,6 +2830,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
             .collect();
         debug!(?in_scope_lifetimes);
 
+        let mut maybe_static = false;
         debug!(?function_param_lifetimes);
         if let Some((param_lifetimes, params)) = &function_param_lifetimes {
             let elided_len = param_lifetimes.len();
@@ -2868,10 +2869,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
 
             if num_params == 0 {
                 err.help(
-                    "this function's return type contains a borrowed value, \
-                 but there is no value for it to be borrowed from",
+                    "this function's return type contains a borrowed value, but there is no value \
+                     for it to be borrowed from",
                 );
                 if in_scope_lifetimes.is_empty() {
+                    maybe_static = true;
                     in_scope_lifetimes = vec![(
                         Ident::with_dummy_span(kw::StaticLifetime),
                         (DUMMY_NODE_ID, LifetimeRes::Static),
@@ -2879,11 +2881,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                 }
             } else if elided_len == 0 {
                 err.help(
-                    "this function's return type contains a borrowed value with \
-                 an elided lifetime, but the lifetime cannot be derived from \
-                 the arguments",
+                    "this function's return type contains a borrowed value with an elided \
+                     lifetime, but the lifetime cannot be derived from the arguments",
                 );
                 if in_scope_lifetimes.is_empty() {
+                    maybe_static = true;
                     in_scope_lifetimes = vec![(
                         Ident::with_dummy_span(kw::StaticLifetime),
                         (DUMMY_NODE_ID, LifetimeRes::Static),
@@ -2891,13 +2893,13 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                 }
             } else if num_params == 1 {
                 err.help(format!(
-                    "this function's return type contains a borrowed value, \
-                 but the signature does not say which {m} it is borrowed from"
+                    "this function's return type contains a borrowed value, but the signature does \
+                     not say which {m} it is borrowed from",
                 ));
             } else {
                 err.help(format!(
-                    "this function's return type contains a borrowed value, \
-                 but the signature does not say whether it is borrowed from {m}"
+                    "this function's return type contains a borrowed value, but the signature does \
+                     not say whether it is borrowed from {m}",
                 ));
             }
         }
@@ -2962,11 +2964,238 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                 );
             }
             1 => {
+                let post = if maybe_static {
+                    let owned = if let [lt] = &lifetime_refs[..]
+                        && lt.kind != MissingLifetimeKind::Ampersand
+                    {
+                        ", or if you will only have owned values"
+                    } else {
+                        ""
+                    };
+                    format!(
+                        ", but this is uncommon unless you're returning a borrowed value from a \
+                         `const` or a `static`{owned}",
+                    )
+                } else {
+                    String::new()
+                };
                 err.multipart_suggestion_verbose(
-                    format!("consider using the `{existing_name}` lifetime"),
+                    format!("consider using the `{existing_name}` lifetime{post}"),
                     spans_suggs,
                     Applicability::MaybeIncorrect,
                 );
+                if maybe_static {
+                    // FIXME: what follows are general suggestions, but we'd want to perform some
+                    // minimal flow analysis to provide more accurate suggestions. For example, if
+                    // we identified that the return expression references only one argument, we
+                    // would suggest borrowing only that argument, and we'd skip the prior
+                    // "use `'static`" suggestion entirely.
+                    if let [lt] = &lifetime_refs[..]
+                        && (lt.kind == MissingLifetimeKind::Ampersand
+                            || lt.kind == MissingLifetimeKind::Underscore)
+                    {
+                        let pre = if lt.kind == MissingLifetimeKind::Ampersand
+                            && let Some((kind, _span)) = self.diagnostic_metadata.current_function
+                            && let FnKind::Fn(_, _, sig, _, _, _) = kind
+                            && !sig.decl.inputs.is_empty()
+                            && let sugg = sig
+                                .decl
+                                .inputs
+                                .iter()
+                                .filter_map(|param| {
+                                    if param.ty.span.contains(lt.span) {
+                                        // We don't want to suggest `fn elision(_: &fn() -> &i32)`
+                                        // when we have `fn elision(_: fn() -> &i32)`
+                                        None
+                                    } else if let TyKind::CVarArgs = param.ty.kind {
+                                        // Don't suggest `&...` for ffi fn with varargs
+                                        None
+                                    } else if let TyKind::ImplTrait(..) = &param.ty.kind {
+                                        // We handle these in the next `else if` branch.
+                                        None
+                                    } else {
+                                        Some((param.ty.span.shrink_to_lo(), "&".to_string()))
+                                    }
+                                })
+                                .collect::<Vec<_>>()
+                            && !sugg.is_empty()
+                        {
+                            let (the, s) = if sig.decl.inputs.len() == 1 {
+                                ("the", "")
+                            } else {
+                                ("one of the", "s")
+                            };
+                            err.multipart_suggestion_verbose(
+                                format!(
+                                    "instead, you are more likely to want to change {the} \
+                                     argument{s} to be borrowed...",
+                                ),
+                                sugg,
+                                Applicability::MaybeIncorrect,
+                            );
+                            "...or alternatively, you might want"
+                        } else if (lt.kind == MissingLifetimeKind::Ampersand
+                            || lt.kind == MissingLifetimeKind::Underscore)
+                            && let Some((kind, _span)) = self.diagnostic_metadata.current_function
+                            && let FnKind::Fn(_, _, sig, _, _, _) = kind
+                            && let ast::FnRetTy::Ty(ret_ty) = &sig.decl.output
+                            && !sig.decl.inputs.is_empty()
+                            && let arg_refs = sig
+                                .decl
+                                .inputs
+                                .iter()
+                                .filter_map(|param| match &param.ty.kind {
+                                    TyKind::ImplTrait(_, bounds) => Some(bounds),
+                                    _ => None,
+                                })
+                                .flat_map(|bounds| bounds.into_iter())
+                                .collect::<Vec<_>>()
+                            && !arg_refs.is_empty()
+                        {
+                            // We have a situation like
+                            // fn g(mut x: impl Iterator<Item = &()>) -> Option<&()>
+                            // So we look at every ref in the trait bound. If there's any, we
+                            // suggest
+                            // fn g<'a>(mut x: impl Iterator<Item = &'a ()>) -> Option<&'a ()>
+                            let mut lt_finder =
+                                LifetimeFinder { lifetime: lt.span, found: None, seen: vec![] };
+                            for bound in arg_refs {
+                                if let ast::GenericBound::Trait(trait_ref, _) = bound {
+                                    lt_finder.visit_trait_ref(&trait_ref.trait_ref);
+                                }
+                            }
+                            lt_finder.visit_ty(ret_ty);
+                            let spans_suggs: Vec<_> = lt_finder
+                                .seen
+                                .iter()
+                                .filter_map(|ty| match &ty.kind {
+                                    TyKind::Ref(_, mut_ty) => {
+                                        let span = ty.span.with_hi(mut_ty.ty.span.lo());
+                                        Some((span, "&'a ".to_string()))
+                                    }
+                                    _ => None,
+                                })
+                                .collect();
+                            self.suggest_introducing_lifetime(
+                                err,
+                                None,
+                                |err, higher_ranked, span, message, intro_sugg| {
+                                    err.multipart_suggestion_verbose(
+                                        message,
+                                        std::iter::once((span, intro_sugg))
+                                            .chain(spans_suggs.iter().cloned())
+                                            .collect(),
+                                        Applicability::MaybeIncorrect,
+                                    );
+                                    higher_ranked
+                                },
+                            );
+                            "alternatively, you might want"
+                        } else {
+                            "instead, you are more likely to want"
+                        };
+                        let mut owned_sugg = lt.kind == MissingLifetimeKind::Ampersand;
+                        let mut sugg = vec![(lt.span, String::new())];
+                        if let Some((kind, _span)) = self.diagnostic_metadata.current_function
+                            && let FnKind::Fn(_, _, sig, _, _, _) = kind
+                            && let ast::FnRetTy::Ty(ty) = &sig.decl.output
+                        {
+                            let mut lt_finder =
+                                LifetimeFinder { lifetime: lt.span, found: None, seen: vec![] };
+                            lt_finder.visit_ty(&ty);
+
+                            if let [Ty { span, kind: TyKind::Ref(_, mut_ty), .. }] =
+                                &lt_finder.seen[..]
+                            {
+                                // We might have a situation like
+                                // fn g(mut x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()>
+                                // but `lt.span` only points at `'_`, so to suggest `-> Option<()>`
+                                // we need to find a more accurate span to end up with
+                                // fn g<'a>(mut x: impl Iterator<Item = &'_ ()>) -> Option<()>
+                                sugg = vec![(span.with_hi(mut_ty.ty.span.lo()), String::new())];
+                                owned_sugg = true;
+                            }
+                            if let Some(ty) = lt_finder.found {
+                                if let TyKind::Path(None, path) = &ty.kind {
+                                    // Check if the path being borrowed is likely to be owned.
+                                    let path: Vec<_> = Segment::from_path(path);
+                                    match self.resolve_path(&path, Some(TypeNS), None) {
+                                        PathResult::Module(ModuleOrUniformRoot::Module(module)) => {
+                                            match module.res() {
+                                                Some(Res::PrimTy(PrimTy::Str)) => {
+                                                    // Don't suggest `-> str`, suggest `-> String`.
+                                                    sugg = vec![(
+                                                        lt.span.with_hi(ty.span.hi()),
+                                                        "String".to_string(),
+                                                    )];
+                                                }
+                                                Some(Res::PrimTy(..)) => {}
+                                                Some(Res::Def(
+                                                    DefKind::Struct
+                                                    | DefKind::Union
+                                                    | DefKind::Enum
+                                                    | DefKind::ForeignTy
+                                                    | DefKind::AssocTy
+                                                    | DefKind::OpaqueTy
+                                                    | DefKind::TyParam,
+                                                    _,
+                                                )) => {}
+                                                _ => {
+                                                    // Do not suggest in all other cases.
+                                                    owned_sugg = false;
+                                                }
+                                            }
+                                        }
+                                        PathResult::NonModule(res) => {
+                                            match res.base_res() {
+                                                Res::PrimTy(PrimTy::Str) => {
+                                                    // Don't suggest `-> str`, suggest `-> String`.
+                                                    sugg = vec![(
+                                                        lt.span.with_hi(ty.span.hi()),
+                                                        "String".to_string(),
+                                                    )];
+                                                }
+                                                Res::PrimTy(..) => {}
+                                                Res::Def(
+                                                    DefKind::Struct
+                                                    | DefKind::Union
+                                                    | DefKind::Enum
+                                                    | DefKind::ForeignTy
+                                                    | DefKind::AssocTy
+                                                    | DefKind::OpaqueTy
+                                                    | DefKind::TyParam,
+                                                    _,
+                                                ) => {}
+                                                _ => {
+                                                    // Do not suggest in all other cases.
+                                                    owned_sugg = false;
+                                                }
+                                            }
+                                        }
+                                        _ => {
+                                            // Do not suggest in all other cases.
+                                            owned_sugg = false;
+                                        }
+                                    }
+                                }
+                                if let TyKind::Slice(inner_ty) = &ty.kind {
+                                    // Don't suggest `-> [T]`, suggest `-> Vec<T>`.
+                                    sugg = vec![
+                                        (lt.span.with_hi(inner_ty.span.lo()), "Vec<".to_string()),
+                                        (ty.span.with_lo(inner_ty.span.hi()), ">".to_string()),
+                                    ];
+                                }
+                            }
+                        }
+                        if owned_sugg {
+                            err.multipart_suggestion_verbose(
+                                format!("{pre} to return an owned value"),
+                                sugg,
+                                Applicability::MaybeIncorrect,
+                            );
+                        }
+                    }
+                }
 
                 // Record as using the suggested resolution.
                 let (_, (_, res)) = in_scope_lifetimes[0];
@@ -2996,7 +3225,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
 fn mk_where_bound_predicate(
     path: &Path,
     poly_trait_ref: &ast::PolyTraitRef,
-    ty: &ast::Ty,
+    ty: &Ty,
 ) -> Option<ast::WhereBoundPredicate> {
     use rustc_span::DUMMY_SP;
     let modified_segments = {
@@ -3073,6 +3302,24 @@ pub(super) fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: I
     err.emit();
 }
 
+struct LifetimeFinder<'ast> {
+    lifetime: Span,
+    found: Option<&'ast Ty>,
+    seen: Vec<&'ast Ty>,
+}
+
+impl<'ast> Visitor<'ast> for LifetimeFinder<'ast> {
+    fn visit_ty(&mut self, t: &'ast Ty) {
+        if let TyKind::Ref(_, mut_ty) = &t.kind {
+            self.seen.push(t);
+            if t.span.lo() == self.lifetime.lo() {
+                self.found = Some(&mut_ty.ty);
+            }
+        }
+        walk_ty(self, t)
+    }
+}
+
 /// Shadowing involving a label is only a warning for historical reasons.
 //FIXME: make this a proper lint.
 pub(super) fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident) {
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
index 8fa0dceda87..4988222c135 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
@@ -3525,20 +3525,30 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         }
 
         match obligation.predicate.kind().skip_binder() {
-            ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => {
-                let ty::ConstKind::Unevaluated(uv) = ct.kind() else {
+            ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => match ct.kind() {
+                ty::ConstKind::Unevaluated(uv) => {
+                    let mut err =
+                        self.tcx.sess.struct_span_err(span, "unconstrained generic constant");
+                    let const_span = self.tcx.def_span(uv.def);
+                    match self.tcx.sess.source_map().span_to_snippet(const_span) {
+                            Ok(snippet) => err.help(format!(
+                                "try adding a `where` bound using this expression: `where [(); {snippet}]:`"
+                            )),
+                            _ => err.help("consider adding a `where` bound using this expression"),
+                        };
+                    Some(err)
+                }
+                ty::ConstKind::Expr(_) => {
+                    let err = self
+                        .tcx
+                        .sess
+                        .struct_span_err(span, format!("unconstrained generic constant `{ct}`"));
+                    Some(err)
+                }
+                _ => {
                     bug!("const evaluatable failed for non-unevaluated const `{ct:?}`");
-                };
-                let mut err = self.tcx.sess.struct_span_err(span, "unconstrained generic constant");
-                let const_span = self.tcx.def_span(uv.def);
-                match self.tcx.sess.source_map().span_to_snippet(const_span) {
-                    Ok(snippet) => err.help(format!(
-                        "try adding a `where` bound using this expression: `where [(); {snippet}]:`"
-                    )),
-                    _ => err.help("consider adding a `where` bound using this expression"),
-                };
-                Some(err)
-            }
+                }
+            },
             _ => {
                 span_bug!(
                     span,
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index a521bf66bed..f2875b7f01e 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -298,10 +298,6 @@ function initSearch(rawSearchIndex) {
         return "=,>-]".indexOf(c) !== -1;
     }
 
-    function isStopCharacter(c) {
-        return isEndCharacter(c);
-    }
-
     function isErrorCharacter(c) {
         return "()".indexOf(c) !== -1;
     }
@@ -617,8 +613,7 @@ function initSearch(rawSearchIndex) {
                     }
                 } else if (
                     c === "[" ||
-                    c === "=" ||
-                    isStopCharacter(c) ||
+                    isEndCharacter(c) ||
                     isSpecialStartCharacter(c) ||
                     isSeparatorCharacter(c)
                 ) {
@@ -917,7 +912,7 @@ function initSearch(rawSearchIndex) {
 
         while (parserState.pos < parserState.length) {
             const c = parserState.userQuery[parserState.pos];
-            if (isStopCharacter(c)) {
+            if (isEndCharacter(c)) {
                 foundStopChar = true;
                 if (isSeparatorCharacter(c)) {
                     parserState.pos += 1;
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index dfa386b49de..40149f8f1c3 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -11,7 +11,7 @@ use std::path::{Path, PathBuf};
 const ENTRY_LIMIT: usize = 900;
 // FIXME: The following limits should be reduced eventually.
 const ISSUES_ENTRY_LIMIT: usize = 1852;
-const ROOT_ENTRY_LIMIT: usize = 867;
+const ROOT_ENTRY_LIMIT: usize = 866;
 
 const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
     "rs",     // test source files
diff --git a/tests/incremental/thinlto/cgu_invalidated_via_import.rs b/tests/incremental/thinlto/cgu_invalidated_via_import.rs
index 5fe435d796f..e0cd385eff3 100644
--- a/tests/incremental/thinlto/cgu_invalidated_via_import.rs
+++ b/tests/incremental/thinlto/cgu_invalidated_via_import.rs
@@ -4,7 +4,7 @@
 
 // revisions: cfail1 cfail2 cfail3
 // compile-flags: -Z query-dep-graph -O
-// build-pass (FIXME(62277): could be check-pass?)
+// build-pass
 
 #![feature(rustc_attrs)]
 #![crate_type="rlib"]
diff --git a/tests/incremental/thinlto/cgu_keeps_identical_fn.rs b/tests/incremental/thinlto/cgu_keeps_identical_fn.rs
index 368a726ea90..781aae578d4 100644
--- a/tests/incremental/thinlto/cgu_keeps_identical_fn.rs
+++ b/tests/incremental/thinlto/cgu_keeps_identical_fn.rs
@@ -5,7 +5,7 @@
 
 // revisions: cfail1 cfail2 cfail3
 // compile-flags: -Z query-dep-graph -O
-// build-pass (FIXME(62277): could be check-pass?)
+// build-pass
 
 #![feature(rustc_attrs)]
 #![crate_type = "rlib"]
diff --git a/tests/incremental/thinlto/independent_cgus_dont_affect_each_other.rs b/tests/incremental/thinlto/independent_cgus_dont_affect_each_other.rs
index 045f2011958..8aa036ec978 100644
--- a/tests/incremental/thinlto/independent_cgus_dont_affect_each_other.rs
+++ b/tests/incremental/thinlto/independent_cgus_dont_affect_each_other.rs
@@ -3,7 +3,7 @@
 
 // revisions: cfail1 cfail2 cfail3
 // compile-flags: -Z query-dep-graph -O
-// build-pass (FIXME(62277): could be check-pass?)
+// build-pass
 
 #![feature(rustc_attrs)]
 #![crate_type="rlib"]
diff --git a/tests/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr b/tests/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr
index 8ccfb212216..24fd38f31ad 100644
--- a/tests/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr
+++ b/tests/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr
@@ -5,10 +5,15 @@ LL | fn elision<T: Fn() -> &i32>() {
    |                       ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
    |
 LL | fn elision<T: Fn() -> &'static i32>() {
    |                        +++++++
+help: instead, you are more likely to want to return an owned value
+   |
+LL - fn elision<T: Fn() -> &i32>() {
+LL + fn elision<T: Fn() -> i32>() {
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/associated-types/bound-lifetime-in-return-only.elision.stderr b/tests/ui/associated-types/bound-lifetime-in-return-only.elision.stderr
index 0593a62a750..d24378487e7 100644
--- a/tests/ui/associated-types/bound-lifetime-in-return-only.elision.stderr
+++ b/tests/ui/associated-types/bound-lifetime-in-return-only.elision.stderr
@@ -5,10 +5,15 @@ LL | fn elision(_: fn() -> &i32) {
    |                       ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
    |
 LL | fn elision(_: fn() -> &'static i32) {
    |                        +++++++
+help: instead, you are more likely to want to return an owned value
+   |
+LL - fn elision(_: fn() -> &i32) {
+LL + fn elision(_: fn() -> i32) {
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/attributes/issue-90873.rs b/tests/ui/attributes/issue-90873.rs
index 1411f61744d..53339ce7e28 100644
--- a/tests/ui/attributes/issue-90873.rs
+++ b/tests/ui/attributes/issue-90873.rs
@@ -1,9 +1,9 @@
 #![u=||{static d=||1;}]
-//~^ unexpected expression
+//~^ attribute value must be a literal
 //~| cannot find attribute `u` in this scope
 //~| missing type for `static` item
 
 #![a={impl std::ops::Neg for i8 {}}]
-//~^ ERROR unexpected expression
+//~^ ERROR attribute value must be a literal
 //~| ERROR cannot find attribute `a` in this scope
 //~| ERROR `main` function not found in crate `issue_90873`
diff --git a/tests/ui/attributes/issue-90873.stderr b/tests/ui/attributes/issue-90873.stderr
index 894ec8341f8..5a8bbaf8ec1 100644
--- a/tests/ui/attributes/issue-90873.stderr
+++ b/tests/ui/attributes/issue-90873.stderr
@@ -1,15 +1,10 @@
-error: unexpected expression: `||
-           {
-               static d: _ = || 1;
-           }`
+error: attribute value must be a literal
   --> $DIR/issue-90873.rs:1:6
    |
 LL | #![u=||{static d=||1;}]
    |      ^^^^^^^^^^^^^^^^^
 
-error: unexpected expression: `{
-           impl std::ops::Neg for i8 {}
-       }`
+error: attribute value must be a literal
   --> $DIR/issue-90873.rs:6:6
    |
 LL | #![a={impl std::ops::Neg for i8 {}}]
diff --git a/tests/ui/attributes/key-value-expansion-on-mac.rs b/tests/ui/attributes/key-value-expansion-on-mac.rs
index c1d68d8cda9..ea7cf7c4f64 100644
--- a/tests/ui/attributes/key-value-expansion-on-mac.rs
+++ b/tests/ui/attributes/key-value-expansion-on-mac.rs
@@ -7,8 +7,8 @@ macro_rules! bar {
 
 // FIXME?: `bar` here expands before `stringify` has a chance to expand.
 // `#[rustc_dummy = ...]` is validated and dropped during expansion of `bar`,
-// the "unexpected expression" errors comes from the validation.
-#[rustc_dummy = stringify!(b)] //~ ERROR unexpected expression: `stringify!(b)`
+// the "attribute value must be a literal" error comes from the validation.
+#[rustc_dummy = stringify!(b)] //~ ERROR attribute value must be a literal
 bar!();
 
 fn main() {}
diff --git a/tests/ui/attributes/key-value-expansion-on-mac.stderr b/tests/ui/attributes/key-value-expansion-on-mac.stderr
index 7d817da1362..260462cfeef 100644
--- a/tests/ui/attributes/key-value-expansion-on-mac.stderr
+++ b/tests/ui/attributes/key-value-expansion-on-mac.stderr
@@ -1,4 +1,4 @@
-error: unexpected expression: `stringify!(b)`
+error: attribute value must be a literal
   --> $DIR/key-value-expansion-on-mac.rs:11:17
    |
 LL | #[rustc_dummy = stringify!(b)]
diff --git a/tests/ui/attributes/key-value-expansion.rs b/tests/ui/attributes/key-value-expansion.rs
index 83d601e5e3a..3065c12749c 100644
--- a/tests/ui/attributes/key-value-expansion.rs
+++ b/tests/ui/attributes/key-value-expansion.rs
@@ -18,13 +18,13 @@ macro_rules! bug {
 
 // Any expressions containing macro call `X` that's more complex than `X` itself.
 // Parentheses will work.
-bug!((column!())); //~ ERROR unexpected expression: `(7u32)`
+bug!((column!())); //~ ERROR attribute value must be a literal
 
 // Original test case.
 
 macro_rules! bug {
     () => {
-        bug!("bug" + stringify!(found)); //~ ERROR unexpected expression: `"bug" + "found"`
+        bug!("bug" + stringify!(found)); //~ ERROR attribute value must be a literal
     };
     ($test:expr) => {
         #[doc = $test]
@@ -46,7 +46,7 @@ macro_rules! doc_comment {
 macro_rules! some_macro {
     ($t1: ty) => {
         doc_comment! {format!("{coor}", coor = stringify!($t1)).as_str()}
-        //~^ ERROR unexpected expression: `{
+        //~^ ERROR attribute value must be a literal
     };
 }
 
diff --git a/tests/ui/attributes/key-value-expansion.stderr b/tests/ui/attributes/key-value-expansion.stderr
index aaa8b169583..54d79c5bebb 100644
--- a/tests/ui/attributes/key-value-expansion.stderr
+++ b/tests/ui/attributes/key-value-expansion.stderr
@@ -1,10 +1,10 @@
-error: unexpected expression: `(7u32)`
+error: attribute value must be a literal
   --> $DIR/key-value-expansion.rs:21:6
    |
 LL | bug!((column!()));
    |      ^^^^^^^^^^^
 
-error: unexpected expression: `"bug" + "found"`
+error: attribute value must be a literal
   --> $DIR/key-value-expansion.rs:27:14
    |
 LL |         bug!("bug" + stringify!(found));
@@ -15,7 +15,7 @@ LL | bug!();
    |
    = note: this error originates in the macro `bug` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: unexpected expression: `{ let res = ::alloc::fmt::format(format_args!("{0}", "u8")); res }.as_str()`
+error: attribute value must be a literal
   --> $DIR/key-value-expansion.rs:48:23
    |
 LL |         doc_comment! {format!("{coor}", coor = stringify!($t1)).as_str()}
diff --git a/tests/ui/attributes/unused-item-in-attr.rs b/tests/ui/attributes/unused-item-in-attr.rs
index 70dcd5413f1..fda0a5d6a3f 100644
--- a/tests/ui/attributes/unused-item-in-attr.rs
+++ b/tests/ui/attributes/unused-item-in-attr.rs
@@ -1,5 +1,5 @@
 #[w = { extern crate alloc; }]
-//~^ ERROR unexpected expression: `{
+//~^ ERROR attribute value must be a literal
 //~| ERROR cannot find attribute `w` in this scope
 fn f() {}
 
diff --git a/tests/ui/attributes/unused-item-in-attr.stderr b/tests/ui/attributes/unused-item-in-attr.stderr
index 92a8f585821..84130965d31 100644
--- a/tests/ui/attributes/unused-item-in-attr.stderr
+++ b/tests/ui/attributes/unused-item-in-attr.stderr
@@ -1,6 +1,4 @@
-error: unexpected expression: `{
-           extern crate alloc;
-       }`
+error: attribute value must be a literal
   --> $DIR/unused-item-in-attr.rs:1:7
    |
 LL | #[w = { extern crate alloc; }]
diff --git a/tests/ui/c-variadic/variadic-ffi-6.stderr b/tests/ui/c-variadic/variadic-ffi-6.stderr
index 1ceff570478..344bfed4b42 100644
--- a/tests/ui/c-variadic/variadic-ffi-6.stderr
+++ b/tests/ui/c-variadic/variadic-ffi-6.stderr
@@ -5,10 +5,19 @@ LL | ) -> &usize {
    |      ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
    |
 LL | ) -> &'static usize {
    |       +++++++
+help: instead, you are more likely to want to change one of the arguments to be borrowed...
+   |
+LL |     x: &usize,
+   |        +
+help: ...or alternatively, you might want to return an owned value
+   |
+LL - ) -> &usize {
+LL + ) -> usize {
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.rs b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.rs
new file mode 100644
index 00000000000..6256000b491
--- /dev/null
+++ b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.rs
@@ -0,0 +1,25 @@
+#![feature(generic_const_exprs)]
+#![allow(incomplete_features)]
+
+fn foo<const N: usize>(
+    _: [u8; {
+        {
+            N
+        }
+    }],
+) {
+}
+
+fn ice<const L: usize>()
+where
+    [(); (L - 1) + 1 + L]:,
+{
+    foo::<_, L>([(); L + 1 + L]);
+    //~^ ERROR: mismatched types
+    //~^^ ERROR: unconstrained generic constant
+    //~^^^ ERROR: function takes 1 generic argument but 2 generic arguments were supplied
+    //~^^^^ ERROR: unconstrained generic constant
+    //~^^^^^ ERROR: unconstrained generic constant `{const expr}`
+}
+
+fn main() {}
diff --git a/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.stderr b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.stderr
new file mode 100644
index 00000000000..6001d824787
--- /dev/null
+++ b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.stderr
@@ -0,0 +1,64 @@
+error[E0107]: function takes 1 generic argument but 2 generic arguments were supplied
+  --> $DIR/issue_114151.rs:17:5
+   |
+LL |     foo::<_, L>([(); L + 1 + L]);
+   |     ^^^      - help: remove this generic argument
+   |     |
+   |     expected 1 generic argument
+   |
+note: function defined here, with 1 generic parameter: `N`
+  --> $DIR/issue_114151.rs:4:4
+   |
+LL | fn foo<const N: usize>(
+   |    ^^^ --------------
+
+error[E0308]: mismatched types
+  --> $DIR/issue_114151.rs:17:18
+   |
+LL |     foo::<_, L>([(); L + 1 + L]);
+   |                  ^^ expected `u8`, found `()`
+
+error: unconstrained generic constant
+  --> $DIR/issue_114151.rs:17:22
+   |
+LL |     foo::<_, L>([(); L + 1 + L]);
+   |                      ^^^^^^^^^
+   |
+   = help: try adding a `where` bound using this expression: `where [(); L + 1 + L]:`
+
+error: unconstrained generic constant
+  --> $DIR/issue_114151.rs:17:17
+   |
+LL |     foo::<_, L>([(); L + 1 + L]);
+   |     ----------- ^^^^^^^^^^^^^^^
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: try adding a `where` bound using this expression: `where [(); {
+                   {
+                       N
+                   }
+               }]:`
+note: required by a bound in `foo`
+  --> $DIR/issue_114151.rs:5:13
+   |
+LL |   fn foo<const N: usize>(
+   |      --- required by a bound in this function
+LL |       _: [u8; {
+   |  _____________^
+LL | |         {
+LL | |             N
+LL | |         }
+LL | |     }],
+   | |_____^ required by this bound in `foo`
+
+error: unconstrained generic constant `{const expr}`
+  --> $DIR/issue_114151.rs:17:5
+   |
+LL |     foo::<_, L>([(); L + 1 + L]);
+   |     ^^^^^^^^^^^
+
+error: aborting due to 5 previous errors
+
+Some errors have detailed explanations: E0107, E0308.
+For more information about an error, try `rustc --explain E0107`.
diff --git a/tests/ui/consts/issue-90878-2.rs b/tests/ui/consts/issue-90878-2.rs
index e5bcecce6ee..0e61c65305f 100644
--- a/tests/ui/consts/issue-90878-2.rs
+++ b/tests/ui/consts/issue-90878-2.rs
@@ -1,4 +1,4 @@
- #![l=|x|[b;x ]] //~ ERROR unexpected expression: `|x| [b; x]`
+ #![l=|x|[b;x ]] //~ ERROR attribute value must be a literal
 //~^ ERROR cannot find attribute `l` in this scope
 
 // notice the space at the start,
diff --git a/tests/ui/consts/issue-90878-2.stderr b/tests/ui/consts/issue-90878-2.stderr
index 71b8d21fb4d..0b332840042 100644
--- a/tests/ui/consts/issue-90878-2.stderr
+++ b/tests/ui/consts/issue-90878-2.stderr
@@ -1,4 +1,4 @@
-error: unexpected expression: `|x| [b; x]`
+error: attribute value must be a literal
   --> $DIR/issue-90878-2.rs:1:7
    |
 LL |  #![l=|x|[b;x ]]
diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.rs b/tests/ui/feature-gates/feature-gate-never_patterns.rs
index ca5ce3b9489..f3910622313 100644
--- a/tests/ui/feature-gates/feature-gate-never_patterns.rs
+++ b/tests/ui/feature-gates/feature-gate-never_patterns.rs
@@ -12,16 +12,63 @@ fn main() {
     unsafe {
         let ptr: *const Void = NonNull::dangling().as_ptr();
         match *ptr {
-            ! //~ ERROR `!` patterns are experimental
+            !
+            //~^ ERROR `!` patterns are experimental
+        }
+        // Check that the gate operates even behind `cfg`.
+        #[cfg(FALSE)]
+        match *ptr {
+            !
+            //~^ ERROR `!` patterns are experimental
+        }
+        #[cfg(FALSE)]
+        match *ptr {
+            ! => {}
+            //~^ ERROR `!` patterns are experimental
         }
     }
 
+    // Correctly gate match arms with no body.
+    match Some(0) {
+        None => {}
+        Some(_),
+        //~^ ERROR unexpected `,` in pattern
+    }
+    match Some(0) {
+        None => {}
+        Some(_)
+        //~^ ERROR `match` arm with no body
+    }
+    match Some(0) {
+        _ => {}
+        Some(_) if false,
+        //~^ ERROR `match` arm with no body
+        Some(_) if false
+        //~^ ERROR `match` arm with no body
+    }
+    match res {
+        Ok(_) => {}
+        Err(!),
+        //~^ ERROR `!` patterns are experimental
+    }
+    match res {
+        Err(!) if false,
+        //~^ ERROR `!` patterns are experimental
+        //~| ERROR a guard on a never pattern will never be run
+        _ => {}
+    }
+
     // Check that the gate operates even behind `cfg`.
-    #[cfg(FALSE)]
-    unsafe {
-        let ptr: *const Void = NonNull::dangling().as_ptr();
-        match *ptr {
-            ! => {} //~ ERROR `!` patterns are experimental
-        }
+    match Some(0) {
+        None => {}
+        #[cfg(FALSE)]
+        Some(_)
+        //~^ ERROR `match` arm with no body
+    }
+    match Some(0) {
+        _ => {}
+        #[cfg(FALSE)]
+        Some(_) if false
+        //~^ ERROR `match` arm with no body
     }
 }
diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.stderr b/tests/ui/feature-gates/feature-gate-never_patterns.stderr
index 2354a3b0476..dd10829d495 100644
--- a/tests/ui/feature-gates/feature-gate-never_patterns.stderr
+++ b/tests/ui/feature-gates/feature-gate-never_patterns.stderr
@@ -1,3 +1,18 @@
+error: unexpected `,` in pattern
+  --> $DIR/feature-gate-never_patterns.rs:34:16
+   |
+LL |         Some(_),
+   |                ^
+   |
+help: try adding parentheses to match on a tuple...
+   |
+LL |         (Some(_),)
+   |         +        +
+help: ...or a vertical bar to match on multiple alternatives
+   |
+LL |         Some(_) |
+   |
+
 error[E0408]: variable `_x` is not bound in all patterns
   --> $DIR/feature-gate-never_patterns.rs:8:19
    |
@@ -25,7 +40,16 @@ LL |             !
    = help: add `#![feature(never_patterns)]` to the crate attributes to enable
 
 error[E0658]: `!` patterns are experimental
-  --> $DIR/feature-gate-never_patterns.rs:24:13
+  --> $DIR/feature-gate-never_patterns.rs:21:13
+   |
+LL |             !
+   |             ^
+   |
+   = note: see issue #118155 <https://github.com/rust-lang/rust/issues/118155> for more information
+   = help: add `#![feature(never_patterns)]` to the crate attributes to enable
+
+error[E0658]: `!` patterns are experimental
+  --> $DIR/feature-gate-never_patterns.rs:26:13
    |
 LL |             ! => {}
    |             ^
@@ -33,7 +57,61 @@ LL |             ! => {}
    = note: see issue #118155 <https://github.com/rust-lang/rust/issues/118155> for more information
    = help: add `#![feature(never_patterns)]` to the crate attributes to enable
 
-error: aborting due to 4 previous errors
+error: `match` arm with no body
+  --> $DIR/feature-gate-never_patterns.rs:39:9
+   |
+LL |         Some(_)
+   |         ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
+
+error: `match` arm with no body
+  --> $DIR/feature-gate-never_patterns.rs:44:9
+   |
+LL |         Some(_) if false,
+   |         ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
+
+error: `match` arm with no body
+  --> $DIR/feature-gate-never_patterns.rs:46:9
+   |
+LL |         Some(_) if false
+   |         ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
+
+error[E0658]: `!` patterns are experimental
+  --> $DIR/feature-gate-never_patterns.rs:51:13
+   |
+LL |         Err(!),
+   |             ^
+   |
+   = note: see issue #118155 <https://github.com/rust-lang/rust/issues/118155> for more information
+   = help: add `#![feature(never_patterns)]` to the crate attributes to enable
+
+error[E0658]: `!` patterns are experimental
+  --> $DIR/feature-gate-never_patterns.rs:55:13
+   |
+LL |         Err(!) if false,
+   |             ^
+   |
+   = note: see issue #118155 <https://github.com/rust-lang/rust/issues/118155> for more information
+   = help: add `#![feature(never_patterns)]` to the crate attributes to enable
+
+error: `match` arm with no body
+  --> $DIR/feature-gate-never_patterns.rs:65:9
+   |
+LL |         Some(_)
+   |         ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
+
+error: `match` arm with no body
+  --> $DIR/feature-gate-never_patterns.rs:71:9
+   |
+LL |         Some(_) if false
+   |         ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
+
+error: a guard on a never pattern will never be run
+  --> $DIR/feature-gate-never_patterns.rs:55:19
+   |
+LL |         Err(!) if false,
+   |                   ^^^^^ help: remove this guard
+
+error: aborting due to 14 previous errors
 
 Some errors have detailed explanations: E0408, E0658.
 For more information about an error, try `rustc --explain E0408`.
diff --git a/tests/ui/foreign-fn-return-lifetime.fixed b/tests/ui/foreign-fn-return-lifetime.fixed
deleted file mode 100644
index 143d6343d26..00000000000
--- a/tests/ui/foreign-fn-return-lifetime.fixed
+++ /dev/null
@@ -1,8 +0,0 @@
-// run-rustfix
-
-extern "C" {
-    pub fn g(_: &u8) -> &u8; // OK
-    pub fn f() -> &'static u8; //~ ERROR missing lifetime specifier
-}
-
-fn main() {}
diff --git a/tests/ui/foreign-fn-return-lifetime.rs b/tests/ui/foreign-fn-return-lifetime.rs
index 76fe50a340a..35595bab36d 100644
--- a/tests/ui/foreign-fn-return-lifetime.rs
+++ b/tests/ui/foreign-fn-return-lifetime.rs
@@ -1,5 +1,3 @@
-// run-rustfix
-
 extern "C" {
     pub fn g(_: &u8) -> &u8; // OK
     pub fn f() -> &u8; //~ ERROR missing lifetime specifier
diff --git a/tests/ui/foreign-fn-return-lifetime.stderr b/tests/ui/foreign-fn-return-lifetime.stderr
index 43edcbde35c..e24c0f23c35 100644
--- a/tests/ui/foreign-fn-return-lifetime.stderr
+++ b/tests/ui/foreign-fn-return-lifetime.stderr
@@ -1,14 +1,19 @@
 error[E0106]: missing lifetime specifier
-  --> $DIR/foreign-fn-return-lifetime.rs:5:19
+  --> $DIR/foreign-fn-return-lifetime.rs:3:19
    |
 LL |     pub fn f() -> &u8;
    |                   ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
    |
 LL |     pub fn f() -> &'static u8;
    |                    +++++++
+help: instead, you are more likely to want to return an owned value
+   |
+LL -     pub fn f() -> &u8;
+LL +     pub fn f() -> u8;
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/generic-associated-types/issue-70304.stderr b/tests/ui/generic-associated-types/issue-70304.stderr
index 99339e96859..9b02c1b0768 100644
--- a/tests/ui/generic-associated-types/issue-70304.stderr
+++ b/tests/ui/generic-associated-types/issue-70304.stderr
@@ -11,7 +11,7 @@ LL | fn create_doc() -> impl Document<Cursor<'_> = DocCursorImpl<'_>> {
    |                                                             ^^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values
    |
 LL | fn create_doc() -> impl Document<Cursor<'_> = DocCursorImpl<'static>> {
    |                                                             ~~~~~~~
diff --git a/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr b/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr
index 443ffeb55cd..a5982a5542a 100644
--- a/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr
+++ b/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr
@@ -5,7 +5,7 @@ LL | fn d() -> impl Fn() -> (impl Debug + '_) {
    |                                      ^^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values
    |
 LL | fn d() -> impl Fn() -> (impl Debug + 'static) {
    |                                      ~~~~~~~
diff --git a/tests/ui/issues/issue-13497.stderr b/tests/ui/issues/issue-13497.stderr
index 236e6b48607..fb3de637a79 100644
--- a/tests/ui/issues/issue-13497.stderr
+++ b/tests/ui/issues/issue-13497.stderr
@@ -5,10 +5,14 @@ LL |     &str
    |     ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
    |
 LL |     &'static str
    |      +++++++
+help: instead, you are more likely to want to return an owned value
+   |
+LL |     String
+   |     ~~~~~~
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/lifetimes/issue-26638.stderr b/tests/ui/lifetimes/issue-26638.stderr
index e61158a5d4d..ee958686259 100644
--- a/tests/ui/lifetimes/issue-26638.stderr
+++ b/tests/ui/lifetimes/issue-26638.stderr
@@ -17,10 +17,18 @@ LL | fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter() }
    |                                        ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
    |
 LL | fn parse_type_2(iter: fn(&u8)->&u8) -> &'static str { iter() }
    |                                         +++++++
+help: instead, you are more likely to want to change the argument to be borrowed...
+   |
+LL | fn parse_type_2(iter: &fn(&u8)->&u8) -> &str { iter() }
+   |                       +
+help: ...or alternatively, you might want to return an owned value
+   |
+LL | fn parse_type_2(iter: fn(&u8)->&u8) -> String { iter() }
+   |                                        ~~~~~~
 
 error[E0106]: missing lifetime specifier
   --> $DIR/issue-26638.rs:10:22
@@ -29,10 +37,14 @@ LL | fn parse_type_3() -> &str { unimplemented!() }
    |                      ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
    |
 LL | fn parse_type_3() -> &'static str { unimplemented!() }
    |                       +++++++
+help: instead, you are more likely to want to return an owned value
+   |
+LL | fn parse_type_3() -> String { unimplemented!() }
+   |                      ~~~~~~
 
 error[E0308]: mismatched types
   --> $DIR/issue-26638.rs:1:69
diff --git a/tests/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr b/tests/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr
index 5eee953ef18..23ef36888f0 100644
--- a/tests/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr
+++ b/tests/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr
@@ -5,10 +5,15 @@ LL | fn f() -> &isize {
    |           ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
    |
 LL | fn f() -> &'static isize {
    |            +++++++
+help: instead, you are more likely to want to return an owned value
+   |
+LL - fn f() -> &isize {
+LL + fn f() -> isize {
+   |
 
 error[E0106]: missing lifetime specifier
   --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:7:33
@@ -41,10 +46,19 @@ LL | fn i(_x: isize) -> &isize {
    |                    ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
    |
 LL | fn i(_x: isize) -> &'static isize {
    |                     +++++++
+help: instead, you are more likely to want to change the argument to be borrowed...
+   |
+LL | fn i(_x: &isize) -> &isize {
+   |          +
+help: ...or alternatively, you might want to return an owned value
+   |
+LL - fn i(_x: isize) -> &isize {
+LL + fn i(_x: isize) -> isize {
+   |
 
 error[E0106]: missing lifetime specifier
   --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:34:24
@@ -53,10 +67,19 @@ LL | fn j(_x: StaticStr) -> &isize {
    |                        ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
    |
 LL | fn j(_x: StaticStr) -> &'static isize {
    |                         +++++++
+help: instead, you are more likely to want to change the argument to be borrowed...
+   |
+LL | fn j(_x: &StaticStr) -> &isize {
+   |          +
+help: ...or alternatively, you might want to return an owned value
+   |
+LL - fn j(_x: StaticStr) -> &isize {
+LL + fn j(_x: StaticStr) -> isize {
+   |
 
 error[E0106]: missing lifetime specifier
   --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:40:49
diff --git a/tests/ui/malformed/malformed-interpolated.rs b/tests/ui/malformed/malformed-interpolated.rs
index 0d84e723fc3..0d801ebd3ac 100644
--- a/tests/ui/malformed/malformed-interpolated.rs
+++ b/tests/ui/malformed/malformed-interpolated.rs
@@ -10,7 +10,7 @@ macro_rules! check {
 check!("0"); // OK
 check!(0); // OK
 check!(0u8); //~ ERROR suffixed literals are not allowed in attributes
-check!(-0); //~ ERROR unexpected expression: `-0`
-check!(0 + 0); //~ ERROR unexpected expression: `0 + 0`
+check!(-0); //~ ERROR attribute value must be a literal
+check!(0 + 0); //~ ERROR attribute value must be a literal
 
 fn main() {}
diff --git a/tests/ui/malformed/malformed-interpolated.stderr b/tests/ui/malformed/malformed-interpolated.stderr
index c24d9f15388..92e99b7a70b 100644
--- a/tests/ui/malformed/malformed-interpolated.stderr
+++ b/tests/ui/malformed/malformed-interpolated.stderr
@@ -6,13 +6,13 @@ LL | check!(0u8);
    |
    = help: instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)
 
-error: unexpected expression: `-0`
+error: attribute value must be a literal
   --> $DIR/malformed-interpolated.rs:13:8
    |
 LL | check!(-0);
    |        ^^
 
-error: unexpected expression: `0 + 0`
+error: attribute value must be a literal
   --> $DIR/malformed-interpolated.rs:14:8
    |
 LL | check!(0 + 0);
diff --git a/tests/ui/parser/bad-lit-suffixes.rs b/tests/ui/parser/bad-lit-suffixes.rs
index 8cb9ef7e0c9..c614f493885 100644
--- a/tests/ui/parser/bad-lit-suffixes.rs
+++ b/tests/ui/parser/bad-lit-suffixes.rs
@@ -28,11 +28,12 @@ fn main() {
 }
 
 #[rustc_dummy = "string"suffix]
-//~^ ERROR unexpected expression: `"string"suffix`
+//~^ ERROR suffixes on string literals are invalid
 fn f() {}
 
 #[must_use = "string"suffix]
-//~^ ERROR unexpected expression: `"string"suffix`
+//~^ ERROR suffixes on string literals are invalid
+//~| ERROR malformed `must_use` attribute input
 fn g() {}
 
 #[link(name = "string"suffix)]
diff --git a/tests/ui/parser/bad-lit-suffixes.stderr b/tests/ui/parser/bad-lit-suffixes.stderr
index 756f99ab12c..b5dacdf7d0d 100644
--- a/tests/ui/parser/bad-lit-suffixes.stderr
+++ b/tests/ui/parser/bad-lit-suffixes.stderr
@@ -10,26 +10,39 @@ error: suffixes on string literals are invalid
 LL |     "C"suffix
    |     ^^^^^^^^^ invalid suffix `suffix`
 
-error: unexpected expression: `"string"suffix`
+error: suffixes on string literals are invalid
   --> $DIR/bad-lit-suffixes.rs:30:17
    |
 LL | #[rustc_dummy = "string"suffix]
-   |                 ^^^^^^^^^^^^^^
+   |                 ^^^^^^^^^^^^^^ invalid suffix `suffix`
 
-error: unexpected expression: `"string"suffix`
+error: suffixes on string literals are invalid
   --> $DIR/bad-lit-suffixes.rs:34:14
    |
 LL | #[must_use = "string"suffix]
-   |              ^^^^^^^^^^^^^^
+   |              ^^^^^^^^^^^^^^ invalid suffix `suffix`
+
+error: malformed `must_use` attribute input
+  --> $DIR/bad-lit-suffixes.rs:34:1
+   |
+LL | #[must_use = "string"suffix]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL | #[must_use = "reason"]
+   |
+LL | #[must_use]
+   |
 
 error: suffixes on string literals are invalid
-  --> $DIR/bad-lit-suffixes.rs:38:15
+  --> $DIR/bad-lit-suffixes.rs:39:15
    |
 LL | #[link(name = "string"suffix)]
    |               ^^^^^^^^^^^^^^ invalid suffix `suffix`
 
 error: invalid suffix `suffix` for number literal
-  --> $DIR/bad-lit-suffixes.rs:42:41
+  --> $DIR/bad-lit-suffixes.rs:43:41
    |
 LL | #[rustc_layout_scalar_valid_range_start(0suffix)]
    |                                         ^^^^^^^ invalid suffix `suffix`
@@ -136,5 +149,5 @@ LL |     1.0e10suffix;
    |
    = help: valid suffixes are `f32` and `f64`
 
-error: aborting due to 20 previous errors
+error: aborting due to 21 previous errors
 
diff --git a/tests/ui/parser/issues/issue-104620.rs b/tests/ui/parser/issues/issue-104620.rs
index f49476c4408..fd0916b4411 100644
--- a/tests/ui/parser/issues/issue-104620.rs
+++ b/tests/ui/parser/issues/issue-104620.rs
@@ -1,4 +1,4 @@
 #![feature(rustc_attrs)]
 
-#![rustc_dummy=5z] //~ ERROR unexpected expression: `5z`
+#![rustc_dummy=5z] //~ ERROR invalid suffix `z` for number literal
 fn main() {}
diff --git a/tests/ui/parser/issues/issue-104620.stderr b/tests/ui/parser/issues/issue-104620.stderr
index fa20b5f8b16..040c63a5fbf 100644
--- a/tests/ui/parser/issues/issue-104620.stderr
+++ b/tests/ui/parser/issues/issue-104620.stderr
@@ -1,8 +1,10 @@
-error: unexpected expression: `5z`
+error: invalid suffix `z` for number literal
   --> $DIR/issue-104620.rs:3:16
    |
 LL | #![rustc_dummy=5z]
-   |                ^^
+   |                ^^ invalid suffix `z`
+   |
+   = help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/parser/match-arm-without-body.rs b/tests/ui/parser/match-arm-without-body.rs
index c3487c2c658..4723abff8b6 100644
--- a/tests/ui/parser/match-arm-without-body.rs
+++ b/tests/ui/parser/match-arm-without-body.rs
@@ -66,8 +66,6 @@ fn main() {
         pat!()
         //~^ ERROR expected `,` following `match` arm
         //~| HELP missing a comma here
-        //~| ERROR `match` arm with no body
-        //~| HELP add a body after the pattern
         _ => {}
     }
     match Some(false) {
diff --git a/tests/ui/parser/match-arm-without-body.stderr b/tests/ui/parser/match-arm-without-body.stderr
index 3a06ed050b5..d98c7ec2826 100644
--- a/tests/ui/parser/match-arm-without-body.stderr
+++ b/tests/ui/parser/match-arm-without-body.stderr
@@ -68,19 +68,19 @@ error: `match` arm with no body
   --> $DIR/match-arm-without-body.rs:30:9
    |
 LL |         Some(_) if true
-   |         ^^^^^^^^^^^^^^^- help: add a body after the pattern: `=> todo!(),`
+   |         ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
 
 error: `match` arm with no body
   --> $DIR/match-arm-without-body.rs:40:9
    |
 LL |         Some(_) if true,
-   |         ^^^^^^^^^^^^^^^- help: add a body after the pattern: `=> todo!(),`
+   |         ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
 
 error: `match` arm with no body
   --> $DIR/match-arm-without-body.rs:45:9
    |
 LL |         Some(_) if true,
-   |         ^^^^^^^^^^^^^^^- help: add a body after the pattern: `=> todo!(),`
+   |         ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
 
 error: `match` arm with no body
   --> $DIR/match-arm-without-body.rs:51:9
@@ -98,19 +98,13 @@ error: `match` arm with no body
   --> $DIR/match-arm-without-body.rs:61:9
    |
 LL |         pat!() if true,
-   |         ^^^^^^^^^^^^^^- help: add a body after the pattern: `=> todo!(),`
-
-error: `match` arm with no body
-  --> $DIR/match-arm-without-body.rs:66:9
-   |
-LL |         pat!()
    |         ^^^^^^- help: add a body after the pattern: `=> todo!(),`
 
 error: `match` arm with no body
-  --> $DIR/match-arm-without-body.rs:74:9
+  --> $DIR/match-arm-without-body.rs:72:9
    |
 LL |         pat!(),
    |         ^^^^^^- help: add a body after the pattern: `=> todo!(),`
 
-error: aborting due to 14 previous errors
+error: aborting due to 13 previous errors
 
diff --git a/tests/ui/self/elision/nested-item.stderr b/tests/ui/self/elision/nested-item.stderr
index 752fd82332c..7bad26fa133 100644
--- a/tests/ui/self/elision/nested-item.stderr
+++ b/tests/ui/self/elision/nested-item.stderr
@@ -21,10 +21,19 @@ LL | fn wrap(self: Wrap<{ fn bar(&self) {} }>) -> &() {
    |                                              ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
    |
 LL | fn wrap(self: Wrap<{ fn bar(&self) {} }>) -> &'static () {
    |                                               +++++++
+help: instead, you are more likely to want to change the argument to be borrowed...
+   |
+LL | fn wrap(self: &Wrap<{ fn bar(&self) {} }>) -> &() {
+   |               +
+help: ...or alternatively, you might want to return an owned value
+   |
+LL - fn wrap(self: Wrap<{ fn bar(&self) {} }>) -> &() {
+LL + fn wrap(self: Wrap<{ fn bar(&self) {} }>) -> () {
+   |
 
 error[E0412]: cannot find type `Wrap` in this scope
   --> $DIR/nested-item.rs:5:15
diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr
index 50806a67255..2dfaa731194 100644
--- a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr
+++ b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr
@@ -5,10 +5,19 @@ LL |     fn g(mut x: impl Iterator<Item = &()>) -> Option<&()> { x.next() }
    |                                                      ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
    |
 LL |     fn g(mut x: impl Iterator<Item = &()>) -> Option<&'static ()> { x.next() }
    |                                                       +++++++
+help: consider introducing a named lifetime parameter
+   |
+LL |     fn g<'a>(mut x: impl Iterator<Item = &'a ()>) -> Option<&'a ()> { x.next() }
+   |         ++++                             ~~~                ~~~
+help: alternatively, you might want to return an owned value
+   |
+LL -     fn g(mut x: impl Iterator<Item = &()>) -> Option<&()> { x.next() }
+LL +     fn g(mut x: impl Iterator<Item = &()>) -> Option<()> { x.next() }
+   |
 
 error[E0106]: missing lifetime specifier
   --> $DIR/impl-trait-missing-lifetime-gated.rs:19:60
@@ -17,10 +26,19 @@ LL |     async fn i(mut x: impl Iterator<Item = &()>) -> Option<&()> { x.next()
    |                                                            ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
    |
 LL |     async fn i(mut x: impl Iterator<Item = &()>) -> Option<&'static ()> { x.next() }
    |                                                             +++++++
+help: consider introducing a named lifetime parameter
+   |
+LL |     async fn i<'a>(mut x: impl Iterator<Item = &'a ()>) -> Option<&'a ()> { x.next() }
+   |               ++++                             ~~~                ~~~
+help: alternatively, you might want to return an owned value
+   |
+LL -     async fn i(mut x: impl Iterator<Item = &()>) -> Option<&()> { x.next() }
+LL +     async fn i(mut x: impl Iterator<Item = &()>) -> Option<()> { x.next() }
+   |
 
 error[E0106]: missing lifetime specifier
   --> $DIR/impl-trait-missing-lifetime-gated.rs:27:58
@@ -29,10 +47,19 @@ LL |     fn g(mut x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next()
    |                                                          ^^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values
    |
 LL |     fn g(mut x: impl Iterator<Item = &'_ ()>) -> Option<&'static ()> { x.next() }
    |                                                          ~~~~~~~
+help: consider introducing a named lifetime parameter
+   |
+LL |     fn g<'a>(mut x: impl Iterator<Item = &'a ()>) -> Option<&'a ()> { x.next() }
+   |         ++++                             ~~~                ~~~
+help: alternatively, you might want to return an owned value
+   |
+LL -     fn g(mut x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() }
+LL +     fn g(mut x: impl Iterator<Item = &'_ ()>) -> Option<()> { x.next() }
+   |
 
 error[E0106]: missing lifetime specifier
   --> $DIR/impl-trait-missing-lifetime-gated.rs:37:64
@@ -41,10 +68,19 @@ LL |     async fn i(mut x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.n
    |                                                                ^^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values
    |
 LL |     async fn i(mut x: impl Iterator<Item = &'_ ()>) -> Option<&'static ()> { x.next() }
    |                                                                ~~~~~~~
+help: consider introducing a named lifetime parameter
+   |
+LL |     async fn i<'a>(mut x: impl Iterator<Item = &'a ()>) -> Option<&'a ()> { x.next() }
+   |               ++++                             ~~~                ~~~
+help: alternatively, you might want to return an owned value
+   |
+LL -     async fn i(mut x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() }
+LL +     async fn i(mut x: impl Iterator<Item = &'_ ()>) -> Option<()> { x.next() }
+   |
 
 error[E0106]: missing lifetime specifier
   --> $DIR/impl-trait-missing-lifetime-gated.rs:47:37
@@ -53,10 +89,19 @@ LL |     fn g(mut x: impl Foo) -> Option<&()> { x.next() }
    |                                     ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
    |
 LL |     fn g(mut x: impl Foo) -> Option<&'static ()> { x.next() }
    |                                      +++++++
+help: consider introducing a named lifetime parameter
+   |
+LL |     fn g<'a>(mut x: impl Foo) -> Option<&'a ()> { x.next() }
+   |         ++++                            ~~~
+help: alternatively, you might want to return an owned value
+   |
+LL -     fn g(mut x: impl Foo) -> Option<&()> { x.next() }
+LL +     fn g(mut x: impl Foo) -> Option<()> { x.next() }
+   |
 
 error[E0106]: missing lifetime specifier
   --> $DIR/impl-trait-missing-lifetime-gated.rs:58:41
@@ -65,10 +110,19 @@ LL |     fn g(mut x: impl Foo<()>) -> Option<&()> { x.next() }
    |                                         ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
    |
 LL |     fn g(mut x: impl Foo<()>) -> Option<&'static ()> { x.next() }
    |                                          +++++++
+help: consider introducing a named lifetime parameter
+   |
+LL |     fn g<'a>(mut x: impl Foo<()>) -> Option<&'a ()> { x.next() }
+   |         ++++                                ~~~
+help: alternatively, you might want to return an owned value
+   |
+LL -     fn g(mut x: impl Foo<()>) -> Option<&()> { x.next() }
+LL +     fn g(mut x: impl Foo<()>) -> Option<()> { x.next() }
+   |
 
 error[E0658]: anonymous lifetimes in `impl Trait` are unstable
   --> $DIR/impl-trait-missing-lifetime-gated.rs:6:35
diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime.stderr b/tests/ui/suggestions/impl-trait-missing-lifetime.stderr
index 2c29cfa0b91..c1dbaae0649 100644
--- a/tests/ui/suggestions/impl-trait-missing-lifetime.stderr
+++ b/tests/ui/suggestions/impl-trait-missing-lifetime.stderr
@@ -5,10 +5,19 @@ LL | fn g(mut x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() }
    |                                                      ^^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values
    |
 LL | fn g(mut x: impl Iterator<Item = &'_ ()>) -> Option<&'static ()> { x.next() }
    |                                                      ~~~~~~~
+help: consider introducing a named lifetime parameter
+   |
+LL | fn g<'a>(mut x: impl Iterator<Item = &'a ()>) -> Option<&'a ()> { x.next() }
+   |     ++++                             ~~~                ~~~
+help: alternatively, you might want to return an owned value
+   |
+LL - fn g(mut x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() }
+LL + fn g(mut x: impl Iterator<Item = &'_ ()>) -> Option<()> { x.next() }
+   |
 
 error[E0106]: missing lifetime specifier
   --> $DIR/impl-trait-missing-lifetime.rs:16:60
@@ -17,10 +26,19 @@ LL | async fn i(mut x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next(
    |                                                            ^^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values
    |
 LL | async fn i(mut x: impl Iterator<Item = &'_ ()>) -> Option<&'static ()> { x.next() }
    |                                                            ~~~~~~~
+help: consider introducing a named lifetime parameter
+   |
+LL | async fn i<'a>(mut x: impl Iterator<Item = &'a ()>) -> Option<&'a ()> { x.next() }
+   |           ++++                             ~~~                ~~~
+help: alternatively, you might want to return an owned value
+   |
+LL - async fn i(mut x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() }
+LL + async fn i(mut x: impl Iterator<Item = &'_ ()>) -> Option<()> { x.next() }
+   |
 
 error: lifetime may not live long enough
   --> $DIR/impl-trait-missing-lifetime.rs:16:69
diff --git a/tests/ui/suggestions/missing-lifetime-specifier.stderr b/tests/ui/suggestions/missing-lifetime-specifier.stderr
index fa4bc2fa79d..e41f547ce9b 100644
--- a/tests/ui/suggestions/missing-lifetime-specifier.stderr
+++ b/tests/ui/suggestions/missing-lifetime-specifier.stderr
@@ -5,7 +5,7 @@ LL |     static a: RefCell<HashMap<i32, Vec<Vec<Foo>>>> = RefCell::new(HashMap::
    |                                            ^^^ expected 2 lifetime parameters
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values
    |
 LL |     static a: RefCell<HashMap<i32, Vec<Vec<Foo<'static, 'static>>>>> = RefCell::new(HashMap::new());
    |                                               ++++++++++++++++++
@@ -32,7 +32,7 @@ LL |     static b: RefCell<HashMap<i32, Vec<Vec<&Bar>>>> = RefCell::new(HashMap:
    |                                            expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
    |
 LL |     static b: RefCell<HashMap<i32, Vec<Vec<&'static Bar<'static, 'static>>>>> = RefCell::new(HashMap::new());
    |                                             +++++++    ++++++++++++++++++
@@ -59,7 +59,7 @@ LL |     static c: RefCell<HashMap<i32, Vec<Vec<Qux<i32>>>>> = RefCell::new(Hash
    |                                               ^ expected 2 lifetime parameters
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values
    |
 LL |     static c: RefCell<HashMap<i32, Vec<Vec<Qux<'static, 'static, i32>>>>> = RefCell::new(HashMap::new());
    |                                                +++++++++++++++++
@@ -86,7 +86,7 @@ LL |     static d: RefCell<HashMap<i32, Vec<Vec<&Tar<i32>>>>> = RefCell::new(Has
    |                                            expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
    |
 LL |     static d: RefCell<HashMap<i32, Vec<Vec<&'static Tar<'static, 'static, i32>>>>> = RefCell::new(HashMap::new());
    |                                             +++++++     +++++++++++++++++
@@ -113,7 +113,7 @@ LL |     static f: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, i32>>>>> = RefCell
    |                                            ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
    |
 LL |     static f: RefCell<HashMap<i32, Vec<Vec<&'static Tar<'static, i32>>>>> = RefCell::new(HashMap::new());
    |                                             +++++++
diff --git a/tests/ui/suggestions/return-elided-lifetime.stderr b/tests/ui/suggestions/return-elided-lifetime.stderr
index 273d95bc747..7bfffd30184 100644
--- a/tests/ui/suggestions/return-elided-lifetime.stderr
+++ b/tests/ui/suggestions/return-elided-lifetime.stderr
@@ -5,10 +5,15 @@ LL | fn f1() -> &i32 { loop {} }
    |            ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
    |
 LL | fn f1() -> &'static i32 { loop {} }
    |             +++++++
+help: instead, you are more likely to want to return an owned value
+   |
+LL - fn f1() -> &i32 { loop {} }
+LL + fn f1() -> i32 { loop {} }
+   |
 
 error[E0106]: missing lifetime specifiers
   --> $DIR/return-elided-lifetime.rs:8:14
@@ -19,7 +24,7 @@ LL | fn f1_() -> (&i32, &i32) { loop {} }
    |              expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
    |
 LL | fn f1_() -> (&'static i32, &'static i32) { loop {} }
    |               +++++++       +++++++
@@ -31,10 +36,19 @@ LL | fn f2(a: i32, b: i32) -> &i32 { loop {} }
    |                          ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
    |
 LL | fn f2(a: i32, b: i32) -> &'static i32 { loop {} }
    |                           +++++++
+help: instead, you are more likely to want to change one of the arguments to be borrowed...
+   |
+LL | fn f2(a: &i32, b: &i32) -> &i32 { loop {} }
+   |          +        +
+help: ...or alternatively, you might want to return an owned value
+   |
+LL - fn f2(a: i32, b: i32) -> &i32 { loop {} }
+LL + fn f2(a: i32, b: i32) -> i32 { loop {} }
+   |
 
 error[E0106]: missing lifetime specifiers
   --> $DIR/return-elided-lifetime.rs:13:28
@@ -45,7 +59,7 @@ LL | fn f2_(a: i32, b: i32) -> (&i32, &i32) { loop {} }
    |                            expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
    |
 LL | fn f2_(a: i32, b: i32) -> (&'static i32, &'static i32) { loop {} }
    |                             +++++++       +++++++
diff --git a/tests/ui/underscore-lifetime/underscore-lifetime-binders.stderr b/tests/ui/underscore-lifetime/underscore-lifetime-binders.stderr
index 50401791eff..cd74d27dcb5 100644
--- a/tests/ui/underscore-lifetime/underscore-lifetime-binders.stderr
+++ b/tests/ui/underscore-lifetime/underscore-lifetime-binders.stderr
@@ -28,7 +28,7 @@ LL | fn meh() -> Box<dyn for<'_> Meh<'_>>
    |                                 ^^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
-help: consider using the `'static` lifetime
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values
    |
 LL | fn meh() -> Box<dyn for<'_> Meh<'static>>
    |                                 ~~~~~~~