about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs2
-rw-r--r--compiler/rustc_hir/src/hir.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/errors.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/generics.rs12
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs13
-rw-r--r--compiler/rustc_hir_typeck/src/mem_categorization.rs1
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs15
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs2
-rw-r--r--compiler/rustc_interface/src/util.rs2
-rw-r--r--compiler/rustc_lint/src/context.rs2
-rw-r--r--compiler/rustc_parse/src/parser/item.rs5
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs2
-rw-r--r--compiler/rustc_resolve/src/imports.rs2
-rw-r--r--compiler/rustc_resolve/src/late.rs6
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs6
-rw-r--r--compiler/rustc_serialize/src/serialize.rs5
-rw-r--r--compiler/rustc_span/src/edit_distance.rs229
-rw-r--r--compiler/rustc_span/src/edit_distance/tests.rs80
-rw-r--r--compiler/rustc_span/src/lev_distance.rs177
-rw-r--r--compiler/rustc_span/src/lev_distance/tests.rs70
-rw-r--r--compiler/rustc_span/src/lib.rs3
-rw-r--r--library/core/src/ffi/c_str.rs5
-rw-r--r--src/bootstrap/compile.rs111
-rw-r--r--src/bootstrap/tool.rs60
-rw-r--r--src/librustdoc/passes/strip_hidden.rs18
-rw-r--r--tests/rustdoc/issue-108231.rs23
-rw-r--r--tests/rustdoc/reexport-hidden-macro.rs22
-rw-r--r--tests/ui/check-cfg/invalid-cfg-value.stderr4
-rw-r--r--tests/ui/const-generics/early/invalid-const-arguments.stderr9
-rw-r--r--tests/ui/did_you_mean/println-typo.rs6
-rw-r--r--tests/ui/did_you_mean/println-typo.stderr11
-rw-r--r--tests/ui/errors/traits/blame-trait-error-spans-on-exprs.rs6
-rw-r--r--tests/ui/errors/traits/blame-trait-error-spans-on-exprs.stderr60
-rw-r--r--tests/ui/traits/suggest-deferences/issue-39029.stderr4
37 files changed, 631 insertions, 354 deletions
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 6bafbfbc14c..4a0e005b8b9 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -13,7 +13,7 @@ use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
 use rustc_hir::PredicateOrigin;
 use rustc_index::vec::{Idx, IndexVec};
 use rustc_middle::ty::{DefIdTree, ResolverAstLowering, TyCtxt};
-use rustc_span::lev_distance::find_best_match_for_name;
+use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::source_map::DesugaringKind;
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::{Span, Symbol};
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 80ec1caf521..3f52f174cdf 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -987,7 +987,6 @@ pub struct Pat<'hir> {
 }
 
 impl<'hir> Pat<'hir> {
-    // FIXME(#19596) this is a workaround, but there should be a better way
     fn walk_short_(&self, it: &mut impl FnMut(&Pat<'hir>) -> bool) -> bool {
         if !it(self) {
             return false;
@@ -1015,7 +1014,6 @@ impl<'hir> Pat<'hir> {
         self.walk_short_(&mut it)
     }
 
-    // FIXME(#19596) this is a workaround, but there should be a better way
     fn walk_(&self, it: &mut impl FnMut(&Pat<'hir>) -> bool) {
         if !it(self) {
             return;
diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs
index a9c2886b414..a68e0e0ac5b 100644
--- a/compiler/rustc_hir_analysis/src/astconv/errors.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs
@@ -7,7 +7,7 @@ use rustc_hir::def_id::DefId;
 use rustc_infer::traits::FulfillmentError;
 use rustc_middle::ty::{self, Ty};
 use rustc_session::parse::feature_err;
-use rustc_span::lev_distance::find_best_match_for_name;
+use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::symbol::{sym, Ident};
 use rustc_span::{Span, Symbol, DUMMY_SP};
 
diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs
index e5e20aa78d9..630becc09d2 100644
--- a/compiler/rustc_hir_analysis/src/astconv/generics.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs
@@ -6,7 +6,7 @@ use crate::astconv::{
 use crate::errors::AssocTypeBindingNotAllowed;
 use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs};
 use rustc_ast::ast::ParamKindOrd;
-use rustc_errors::{struct_span_err, Applicability, Diagnostic, MultiSpan};
+use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
@@ -26,7 +26,7 @@ fn generic_arg_mismatch_err(
     param: &GenericParamDef,
     possible_ordering_error: bool,
     help: Option<&str>,
-) {
+) -> ErrorGuaranteed {
     let sess = tcx.sess;
     let mut err = struct_span_err!(
         sess,
@@ -70,9 +70,9 @@ fn generic_arg_mismatch_err(
         ) => match path.res {
             Res::Err => {
                 add_braces_suggestion(arg, &mut err);
-                err.set_primary_message("unresolved item provided when a constant was expected")
+                return err
+                    .set_primary_message("unresolved item provided when a constant was expected")
                     .emit();
-                return;
             }
             Res::Def(DefKind::TyParam, src_def_id) => {
                 if let Some(param_local_id) = param.def_id.as_local() {
@@ -81,7 +81,7 @@ fn generic_arg_mismatch_err(
                     if param_type.is_suggestable(tcx, false) {
                         err.span_suggestion(
                             tcx.def_span(src_def_id),
-                            "consider changing this type parameter to be a `const` generic",
+                            "consider changing this type parameter to a const parameter",
                             format!("const {}: {}", param_name, param_type),
                             Applicability::MaybeIncorrect,
                         );
@@ -137,7 +137,7 @@ fn generic_arg_mismatch_err(
         }
     }
 
-    err.emit();
+    err.emit()
 }
 
 /// Creates the relevant generic argument substitutions
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index 716b4fc6ae3..c0cd54cc916 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -37,8 +37,8 @@ use rustc_middle::ty::DynKind;
 use rustc_middle::ty::GenericParamDefKind;
 use rustc_middle::ty::{self, Const, DefIdTree, IsSuggestable, Ty, TyCtxt, TypeVisitable};
 use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECTS};
+use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::edition::Edition;
-use rustc_span::lev_distance::find_best_match_for_name;
 use rustc_span::symbol::{kw, Ident, Symbol};
 use rustc_span::{sym, Span, DUMMY_SP};
 use rustc_target::spec::abi;
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 2f79071f6dc..08cbfffdd17 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -45,8 +45,8 @@ use rustc_middle::ty::subst::SubstsRef;
 use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitable};
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_session::parse::feature_err;
+use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::hygiene::DesugaringKind;
-use rustc_span::lev_distance::find_best_match_for_name;
 use rustc_span::source_map::{Span, Spanned};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_target::spec::abi::Abi::RustIntrinsic;
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
index 06d6a375697..39e0ea98f96 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
@@ -549,6 +549,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return Err(expr);
         };
 
+        if let (
+            hir::ExprKind::AddrOf(_borrow_kind, _borrow_mutability, borrowed_expr),
+            ty::Ref(_ty_region, ty_ref_type, _ty_mutability),
+        ) = (&expr.kind, in_ty.kind())
+        {
+            // We can "drill into" the borrowed expression.
+            return self.blame_specific_part_of_expr_corresponding_to_generic_param(
+                param,
+                borrowed_expr,
+                (*ty_ref_type).into(),
+            );
+        }
+
         if let (hir::ExprKind::Tup(expr_elements), ty::Tuple(in_ty_elements)) =
             (&expr.kind, in_ty.kind())
         {
diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs
index 60e0ce3494d..4b08832eddc 100644
--- a/compiler/rustc_hir_typeck/src/mem_categorization.rs
+++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs
@@ -601,7 +601,6 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
         }
     }
 
-    // FIXME(#19596) This is a workaround, but there should be a better way to do this
     fn cat_pattern_<F>(
         &self,
         mut place_with_id: PlaceWithHirId<'tcx>,
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index eb6c0b7686b..88af554483b 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -24,8 +24,8 @@ use rustc_middle::ty::{InternalSubsts, SubstsRef};
 use rustc_session::lint;
 use rustc_span::def_id::DefId;
 use rustc_span::def_id::LocalDefId;
-use rustc_span::lev_distance::{
-    find_best_match_for_name_with_substrings, lev_distance_with_substrings,
+use rustc_span::edit_distance::{
+    edit_distance_with_substrings, find_best_match_for_name_with_substrings,
 };
 use rustc_span::symbol::sym;
 use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP};
@@ -69,7 +69,7 @@ struct ProbeContext<'a, 'tcx> {
     impl_dups: FxHashSet<DefId>,
 
     /// When probing for names, include names that are close to the
-    /// requested name (by Levenshtein distance)
+    /// requested name (by edit distance)
     allow_similar_names: bool,
 
     /// Some(candidate) if there is a private candidate
@@ -1793,7 +1793,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
 
     /// Similarly to `probe_for_return_type`, this method attempts to find the best matching
     /// candidate method where the method name may have been misspelled. Similarly to other
-    /// Levenshtein based suggestions, we provide at most one such suggestion.
+    /// edit distance based suggestions, we provide at most one such suggestion.
     fn probe_for_similar_candidate(&mut self) -> Result<Option<ty::AssocItem>, MethodError<'tcx>> {
         debug!("probing for method names similar to {:?}", self.method_name);
 
@@ -2024,8 +2024,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                         if self.matches_by_doc_alias(x.def_id) {
                             return true;
                         }
-                        match lev_distance_with_substrings(name.as_str(), x.name.as_str(), max_dist)
-                        {
+                        match edit_distance_with_substrings(
+                            name.as_str(),
+                            x.name.as_str(),
+                            max_dist,
+                        ) {
                             Some(d) => d > 0,
                             None => false,
                         }
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 6a7b1f6646a..4f3dbe03c05 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -31,7 +31,7 @@ use rustc_middle::ty::{self, DefIdTree, GenericArgKind, Ty, TyCtxt, TypeVisitabl
 use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef};
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::Symbol;
-use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span};
+use rustc_span::{edit_distance, source_map, ExpnKind, FileName, MacroKind, Span};
 use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedNote;
 use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
@@ -1014,7 +1014,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // that had unsatisfied trait bounds
         if unsatisfied_predicates.is_empty() && rcvr_ty.is_enum() {
             let adt_def = rcvr_ty.ty_adt_def().expect("enum is not an ADT");
-            if let Some(suggestion) = lev_distance::find_best_match_for_name(
+            if let Some(suggestion) = edit_distance::find_best_match_for_name(
                 &adt_def.variants().iter().map(|s| s.name).collect::<Vec<_>>(),
                 item_name.name,
                 None,
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 3881efe87db..ab6e76ef8aa 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -14,8 +14,8 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi
 use rustc_middle::middle::stability::EvalResult;
 use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeVisitable};
 use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
+use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::hygiene::DesugaringKind;
-use rustc_span::lev_distance::find_best_match_for_name;
 use rustc_span::source_map::{Span, Spanned};
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::{BytePos, DUMMY_SP};
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index e4b4d5375e6..475d3601b52 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -14,8 +14,8 @@ use rustc_session::filesearch::sysroot_candidates;
 use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer};
 use rustc_session::parse::CrateConfig;
 use rustc_session::{early_error, filesearch, output, Session};
+use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::edition::Edition;
-use rustc_span::lev_distance::find_best_match_for_name;
 use rustc_span::source_map::FileLoader;
 use rustc_span::symbol::{sym, Symbol};
 use session::CompilerIO;
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 9a9e2de7b5c..aace4974cc9 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -39,7 +39,7 @@ use rustc_middle::ty::{self, print::Printer, subst::GenericArg, RegisteredTools,
 use rustc_session::lint::{BuiltinLintDiagnostics, LintExpectationId};
 use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId};
 use rustc_session::Session;
-use rustc_span::lev_distance::find_best_match_for_name;
+use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::{BytePos, Span};
 use rustc_target::abi;
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index fd46a1292a8..f164bb330f3 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -19,8 +19,8 @@ use rustc_errors::{
     struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, PResult,
     StashKey,
 };
+use rustc_span::edit_distance::edit_distance;
 use rustc_span::edition::Edition;
-use rustc_span::lev_distance::lev_distance;
 use rustc_span::source_map::{self, Span};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::DUMMY_SP;
@@ -459,7 +459,8 @@ impl<'a> Parser<'a> {
                 // Maybe the user misspelled `macro_rules` (issue #91227)
                 if self.token.is_ident()
                     && path.segments.len() == 1
-                    && lev_distance("macro_rules", &path.segments[0].ident.to_string(), 3).is_some()
+                    && edit_distance("macro_rules", &path.segments[0].ident.to_string(), 2)
+                        .is_some()
                 {
                     err.span_suggestion(
                         path.span,
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 934d60589d4..cd26dbd6190 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -21,9 +21,9 @@ use rustc_session::lint::builtin::ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE;
 use rustc_session::lint::builtin::MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS;
 use rustc_session::lint::BuiltinLintDiagnostics;
 use rustc_session::Session;
+use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::edition::Edition;
 use rustc_span::hygiene::MacroKind;
-use rustc_span::lev_distance::find_best_match_for_name;
 use rustc_span::source_map::SourceMap;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{BytePos, Span, SyntaxContext};
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index da3e5095e53..48188b4ba35 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -21,8 +21,8 @@ use rustc_middle::span_bug;
 use rustc_middle::ty;
 use rustc_session::lint::builtin::{PUB_USE_OF_PRIVATE_EXTERN_CRATE, UNUSED_IMPORTS};
 use rustc_session::lint::BuiltinLintDiagnostics;
+use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::hygiene::LocalExpnId;
-use rustc_span::lev_distance::find_best_match_for_name;
 use rustc_span::symbol::{kw, Ident, Symbol};
 use rustc_span::Span;
 
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 162f91e05f8..f8bd289d72f 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -4135,9 +4135,9 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
     fn record_candidate_traits_for_expr_if_necessary(&mut self, expr: &'ast Expr) {
         match expr.kind {
             ExprKind::Field(_, ident) => {
-                // FIXME(#6890): Even though you can't treat a method like a
-                // field, we need to add any trait methods we find that match
-                // the field name so that we can do some nice error reporting
+                // #6890: Even though you can't treat a method like a field,
+                // we need to add any trait methods we find that match the
+                // field name so that we can do some nice error reporting
                 // later on in typeck.
                 let traits = self.traits_in_scope(ident, ValueNS);
                 self.r.trait_map.insert(expr.id, traits);
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 5205d055cf9..174a543fe46 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -25,9 +25,9 @@ use rustc_middle::ty::DefIdTree;
 use rustc_session::lint;
 use rustc_session::parse::feature_err;
 use rustc_session::Session;
+use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::edition::Edition;
 use rustc_span::hygiene::MacroKind;
-use rustc_span::lev_distance::find_best_match_for_name;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{BytePos, Span};
 
@@ -542,7 +542,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
             }
         }
 
-        // Try Levenshtein algorithm.
+        // Try finding a suitable replacement.
         let typo_sugg =
             self.lookup_typo_candidate(path, source.namespace(), is_expected).to_opt_suggestion();
         if path.len() == 1 && self.self_type_is_available() {
@@ -770,7 +770,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                 _ => {}
             }
 
-            // If the trait has a single item (which wasn't matched by Levenshtein), suggest it
+            // If the trait has a single item (which wasn't matched by the algorithm), suggest it
             let suggestion = self.get_single_associated_item(&path, &source, is_expected);
             if !self.r.add_typo_suggestion(err, suggestion, ident_span) {
                 fallback = !self.let_binding_suggestion(err, ident_span);
diff --git a/compiler/rustc_serialize/src/serialize.rs b/compiler/rustc_serialize/src/serialize.rs
index 751b209f11a..377c364961b 100644
--- a/compiler/rustc_serialize/src/serialize.rs
+++ b/compiler/rustc_serialize/src/serialize.rs
@@ -430,11 +430,6 @@ impl<D: Decoder, T: Decodable<D> + Copy> Decodable<D> for Cell<T> {
     }
 }
 
-// FIXME: #15036
-// Should use `try_borrow`, returning an
-// `encoder.error("attempting to Encode borrowed RefCell")`
-// from `encode` when `try_borrow` returns `None`.
-
 impl<S: Encoder, T: Encodable<S>> Encodable<S> for RefCell<T> {
     fn encode(&self, s: &mut S) {
         self.borrow().encode(s);
diff --git a/compiler/rustc_span/src/edit_distance.rs b/compiler/rustc_span/src/edit_distance.rs
new file mode 100644
index 00000000000..89f0386e3e9
--- /dev/null
+++ b/compiler/rustc_span/src/edit_distance.rs
@@ -0,0 +1,229 @@
+//! Edit distances.
+//!
+//! The [edit distance] is a metric for measuring the difference between two strings.
+//!
+//! [edit distance]: https://en.wikipedia.org/wiki/Edit_distance
+
+// The current implementation is the restricted Damerau-Levenshtein algorithm. It is restricted
+// because it does not permit modifying characters that have already been transposed. The specific
+// algorithm should not matter to the caller of the methods, which is why it is not noted in the
+// documentation.
+
+use crate::symbol::Symbol;
+use std::{cmp, mem};
+
+#[cfg(test)]
+mod tests;
+
+/// Finds the [edit distance] between two strings.
+///
+/// Returns `None` if the distance exceeds the limit.
+///
+/// [edit distance]: https://en.wikipedia.org/wiki/Edit_distance
+pub fn edit_distance(a: &str, b: &str, limit: usize) -> Option<usize> {
+    let mut a = &a.chars().collect::<Vec<_>>()[..];
+    let mut b = &b.chars().collect::<Vec<_>>()[..];
+
+    // Ensure that `b` is the shorter string, minimizing memory use.
+    if a.len() < b.len() {
+        mem::swap(&mut a, &mut b);
+    }
+
+    let min_dist = a.len() - b.len();
+    // If we know the limit will be exceeded, we can return early.
+    if min_dist > limit {
+        return None;
+    }
+
+    // Strip common prefix.
+    while let Some(((b_char, b_rest), (a_char, a_rest))) = b.split_first().zip(a.split_first())
+        && a_char == b_char
+    {
+        a = a_rest;
+        b = b_rest;
+    }
+    // Strip common suffix.
+    while let Some(((b_char, b_rest), (a_char, a_rest))) = b.split_last().zip(a.split_last())
+        && a_char == b_char
+    {
+        a = a_rest;
+        b = b_rest;
+    }
+
+    // If either string is empty, the distance is the length of the other.
+    // We know that `b` is the shorter string, so we don't need to check `a`.
+    if b.len() == 0 {
+        return Some(min_dist);
+    }
+
+    let mut prev_prev = vec![usize::MAX; b.len() + 1];
+    let mut prev = (0..=b.len()).collect::<Vec<_>>();
+    let mut current = vec![0; b.len() + 1];
+
+    // row by row
+    for i in 1..=a.len() {
+        current[0] = i;
+        let a_idx = i - 1;
+
+        // column by column
+        for j in 1..=b.len() {
+            let b_idx = j - 1;
+
+            // There is no cost to substitute a character with itself.
+            let substitution_cost = if a[a_idx] == b[b_idx] { 0 } else { 1 };
+
+            current[j] = cmp::min(
+                // deletion
+                prev[j] + 1,
+                cmp::min(
+                    // insertion
+                    current[j - 1] + 1,
+                    // substitution
+                    prev[j - 1] + substitution_cost,
+                ),
+            );
+
+            if (i > 1) && (j > 1) && (a[a_idx] == b[b_idx - 1]) && (a[a_idx - 1] == b[b_idx]) {
+                // transposition
+                current[j] = cmp::min(current[j], prev_prev[j - 2] + 1);
+            }
+        }
+
+        // Rotate the buffers, reusing the memory.
+        [prev_prev, prev, current] = [prev, current, prev_prev];
+    }
+
+    // `prev` because we already rotated the buffers.
+    let distance = prev[b.len()];
+    (distance <= limit).then_some(distance)
+}
+
+/// Provides a word similarity score between two words that accounts for substrings being more
+/// meaningful than a typical edit distance. The lower the score, the closer the match. 0 is an
+/// identical match.
+///
+/// Uses the edit distance between the two strings and removes the cost of the length difference.
+/// If this is 0 then it is either a substring match or a full word match, in the substring match
+/// case we detect this and return `1`. To prevent finding meaningless substrings, eg. "in" in
+/// "shrink", we only perform this subtraction of length difference if one of the words is not
+/// greater than twice the length of the other. For cases where the words are close in size but not
+/// an exact substring then the cost of the length difference is discounted by half.
+///
+/// Returns `None` if the distance exceeds the limit.
+pub fn edit_distance_with_substrings(a: &str, b: &str, limit: usize) -> Option<usize> {
+    let n = a.chars().count();
+    let m = b.chars().count();
+
+    // Check one isn't less than half the length of the other. If this is true then there is a
+    // big difference in length.
+    let big_len_diff = (n * 2) < m || (m * 2) < n;
+    let len_diff = if n < m { m - n } else { n - m };
+    let distance = edit_distance(a, b, limit + len_diff)?;
+
+    // This is the crux, subtracting length difference means exact substring matches will now be 0
+    let score = distance - len_diff;
+
+    // If the score is 0 but the words have different lengths then it's a substring match not a full
+    // word match
+    let score = if score == 0 && len_diff > 0 && !big_len_diff {
+        1 // Exact substring match, but not a total word match so return non-zero
+    } else if !big_len_diff {
+        // Not a big difference in length, discount cost of length difference
+        score + (len_diff + 1) / 2
+    } else {
+        // A big difference in length, add back the difference in length to the score
+        score + len_diff
+    };
+
+    (score <= limit).then_some(score)
+}
+
+/// Finds the best match for given word in the given iterator where substrings are meaningful.
+///
+/// A version of [`find_best_match_for_name`] that uses [`edit_distance_with_substrings`] as the
+/// score for word similarity. This takes an optional distance limit which defaults to one-third of
+/// the given word.
+///
+/// We use case insensitive comparison to improve accuracy on an edge case with a lower(upper)case
+/// letters mismatch.
+pub fn find_best_match_for_name_with_substrings(
+    candidates: &[Symbol],
+    lookup: Symbol,
+    dist: Option<usize>,
+) -> Option<Symbol> {
+    find_best_match_for_name_impl(true, candidates, lookup, dist)
+}
+
+/// Finds the best match for a given word in the given iterator.
+///
+/// As a loose rule to avoid the obviously incorrect suggestions, it takes
+/// an optional limit for the maximum allowable edit distance, which defaults
+/// to one-third of the given word.
+///
+/// We use case insensitive comparison to improve accuracy on an edge case with a lower(upper)case
+/// letters mismatch.
+pub fn find_best_match_for_name(
+    candidates: &[Symbol],
+    lookup: Symbol,
+    dist: Option<usize>,
+) -> Option<Symbol> {
+    find_best_match_for_name_impl(false, candidates, lookup, dist)
+}
+
+#[cold]
+fn find_best_match_for_name_impl(
+    use_substring_score: bool,
+    candidates: &[Symbol],
+    lookup: Symbol,
+    dist: Option<usize>,
+) -> Option<Symbol> {
+    let lookup = lookup.as_str();
+    let lookup_uppercase = lookup.to_uppercase();
+
+    // Priority of matches:
+    // 1. Exact case insensitive match
+    // 2. Edit distance match
+    // 3. Sorted word match
+    if let Some(c) = candidates.iter().find(|c| c.as_str().to_uppercase() == lookup_uppercase) {
+        return Some(*c);
+    }
+
+    let mut dist = dist.unwrap_or_else(|| cmp::max(lookup.len(), 3) / 3);
+    let mut best = None;
+    for c in candidates {
+        match if use_substring_score {
+            edit_distance_with_substrings(lookup, c.as_str(), dist)
+        } else {
+            edit_distance(lookup, c.as_str(), dist)
+        } {
+            Some(0) => return Some(*c),
+            Some(d) => {
+                dist = d - 1;
+                best = Some(*c);
+            }
+            None => {}
+        }
+    }
+    if best.is_some() {
+        return best;
+    }
+
+    find_match_by_sorted_words(candidates, lookup)
+}
+
+fn find_match_by_sorted_words(iter_names: &[Symbol], lookup: &str) -> Option<Symbol> {
+    iter_names.iter().fold(None, |result, candidate| {
+        if sort_by_words(candidate.as_str()) == sort_by_words(lookup) {
+            Some(*candidate)
+        } else {
+            result
+        }
+    })
+}
+
+fn sort_by_words(name: &str) -> String {
+    let mut split_words: Vec<&str> = name.split('_').collect();
+    // We are sorting primitive &strs and can use unstable sort here.
+    split_words.sort_unstable();
+    split_words.join("_")
+}
diff --git a/compiler/rustc_span/src/edit_distance/tests.rs b/compiler/rustc_span/src/edit_distance/tests.rs
new file mode 100644
index 00000000000..c9c7a1f1bf2
--- /dev/null
+++ b/compiler/rustc_span/src/edit_distance/tests.rs
@@ -0,0 +1,80 @@
+use super::*;
+
+#[test]
+fn test_edit_distance() {
+    // Test bytelength agnosticity
+    for c in (0..char::MAX as u32).filter_map(char::from_u32).map(|i| i.to_string()) {
+        assert_eq!(edit_distance(&c[..], &c[..], usize::MAX), Some(0));
+    }
+
+    let a = "\nMäry häd ä little lämb\n\nLittle lämb\n";
+    let b = "\nMary häd ä little lämb\n\nLittle lämb\n";
+    let c = "Mary häd ä little lämb\n\nLittle lämb\n";
+    assert_eq!(edit_distance(a, b, usize::MAX), Some(1));
+    assert_eq!(edit_distance(b, a, usize::MAX), Some(1));
+    assert_eq!(edit_distance(a, c, usize::MAX), Some(2));
+    assert_eq!(edit_distance(c, a, usize::MAX), Some(2));
+    assert_eq!(edit_distance(b, c, usize::MAX), Some(1));
+    assert_eq!(edit_distance(c, b, usize::MAX), Some(1));
+}
+
+#[test]
+fn test_edit_distance_limit() {
+    assert_eq!(edit_distance("abc", "abcd", 1), Some(1));
+    assert_eq!(edit_distance("abc", "abcd", 0), None);
+    assert_eq!(edit_distance("abc", "xyz", 3), Some(3));
+    assert_eq!(edit_distance("abc", "xyz", 2), None);
+}
+
+#[test]
+fn test_method_name_similarity_score() {
+    assert_eq!(edit_distance_with_substrings("empty", "is_empty", 1), Some(1));
+    assert_eq!(edit_distance_with_substrings("shrunk", "rchunks", 2), None);
+    assert_eq!(edit_distance_with_substrings("abc", "abcd", 1), Some(1));
+    assert_eq!(edit_distance_with_substrings("a", "abcd", 1), None);
+    assert_eq!(edit_distance_with_substrings("edf", "eq", 1), None);
+    assert_eq!(edit_distance_with_substrings("abc", "xyz", 3), Some(3));
+    assert_eq!(edit_distance_with_substrings("abcdef", "abcdef", 2), Some(0));
+}
+
+#[test]
+fn test_find_best_match_for_name() {
+    use crate::create_default_session_globals_then;
+    create_default_session_globals_then(|| {
+        let input = vec![Symbol::intern("aaab"), Symbol::intern("aaabc")];
+        assert_eq!(
+            find_best_match_for_name(&input, Symbol::intern("aaaa"), None),
+            Some(Symbol::intern("aaab"))
+        );
+
+        assert_eq!(find_best_match_for_name(&input, Symbol::intern("1111111111"), None), None);
+
+        let input = vec![Symbol::intern("AAAA")];
+        assert_eq!(
+            find_best_match_for_name(&input, Symbol::intern("aaaa"), None),
+            Some(Symbol::intern("AAAA"))
+        );
+
+        let input = vec![Symbol::intern("AAAA")];
+        assert_eq!(
+            find_best_match_for_name(&input, Symbol::intern("aaaa"), Some(4)),
+            Some(Symbol::intern("AAAA"))
+        );
+
+        let input = vec![Symbol::intern("a_longer_variable_name")];
+        assert_eq!(
+            find_best_match_for_name(&input, Symbol::intern("a_variable_longer_name"), None),
+            Some(Symbol::intern("a_longer_variable_name"))
+        );
+    })
+}
+
+#[test]
+fn test_precise_algorithm() {
+    // Not Levenshtein distance.
+    assert_ne!(edit_distance("ab", "ba", usize::MAX), Some(2));
+    // Not unrestricted Damerau-Levenshtein distance.
+    assert_ne!(edit_distance("abde", "bcaed", usize::MAX), Some(3));
+    // The current implementation is a restricted Damerau-Levenshtein distance.
+    assert_eq!(edit_distance("abde", "bcaed", usize::MAX), Some(4));
+}
diff --git a/compiler/rustc_span/src/lev_distance.rs b/compiler/rustc_span/src/lev_distance.rs
deleted file mode 100644
index 61e4b98a8d2..00000000000
--- a/compiler/rustc_span/src/lev_distance.rs
+++ /dev/null
@@ -1,177 +0,0 @@
-//! Levenshtein distances.
-//!
-//! The [Levenshtein distance] is a metric for measuring the difference between two strings.
-//!
-//! [Levenshtein distance]: https://en.wikipedia.org/wiki/Levenshtein_distance
-
-use crate::symbol::Symbol;
-use std::cmp;
-
-#[cfg(test)]
-mod tests;
-
-/// Finds the Levenshtein distance between two strings.
-///
-/// Returns None if the distance exceeds the limit.
-pub fn lev_distance(a: &str, b: &str, limit: usize) -> Option<usize> {
-    let n = a.chars().count();
-    let m = b.chars().count();
-    let min_dist = if n < m { m - n } else { n - m };
-
-    if min_dist > limit {
-        return None;
-    }
-    if n == 0 || m == 0 {
-        return (min_dist <= limit).then_some(min_dist);
-    }
-
-    let mut dcol: Vec<_> = (0..=m).collect();
-
-    for (i, sc) in a.chars().enumerate() {
-        let mut current = i;
-        dcol[0] = current + 1;
-
-        for (j, tc) in b.chars().enumerate() {
-            let next = dcol[j + 1];
-            if sc == tc {
-                dcol[j + 1] = current;
-            } else {
-                dcol[j + 1] = cmp::min(current, next);
-                dcol[j + 1] = cmp::min(dcol[j + 1], dcol[j]) + 1;
-            }
-            current = next;
-        }
-    }
-
-    (dcol[m] <= limit).then_some(dcol[m])
-}
-
-/// Provides a word similarity score between two words that accounts for substrings being more
-/// meaningful than a typical Levenshtein distance. The lower the score, the closer the match.
-/// 0 is an identical match.
-///
-/// Uses the Levenshtein distance between the two strings and removes the cost of the length
-/// difference. If this is 0 then it is either a substring match or a full word match, in the
-/// substring match case we detect this and return `1`. To prevent finding meaningless substrings,
-/// eg. "in" in "shrink", we only perform this subtraction of length difference if one of the words
-/// is not greater than twice the length of the other. For cases where the words are close in size
-/// but not an exact substring then the cost of the length difference is discounted by half.
-///
-/// Returns `None` if the distance exceeds the limit.
-pub fn lev_distance_with_substrings(a: &str, b: &str, limit: usize) -> Option<usize> {
-    let n = a.chars().count();
-    let m = b.chars().count();
-
-    // Check one isn't less than half the length of the other. If this is true then there is a
-    // big difference in length.
-    let big_len_diff = (n * 2) < m || (m * 2) < n;
-    let len_diff = if n < m { m - n } else { n - m };
-    let lev = lev_distance(a, b, limit + len_diff)?;
-
-    // This is the crux, subtracting length difference means exact substring matches will now be 0
-    let score = lev - len_diff;
-
-    // If the score is 0 but the words have different lengths then it's a substring match not a full
-    // word match
-    let score = if score == 0 && len_diff > 0 && !big_len_diff {
-        1 // Exact substring match, but not a total word match so return non-zero
-    } else if !big_len_diff {
-        // Not a big difference in length, discount cost of length difference
-        score + (len_diff + 1) / 2
-    } else {
-        // A big difference in length, add back the difference in length to the score
-        score + len_diff
-    };
-
-    (score <= limit).then_some(score)
-}
-
-/// Finds the best match for given word in the given iterator where substrings are meaningful.
-///
-/// A version of [`find_best_match_for_name`] that uses [`lev_distance_with_substrings`] as the score
-/// for word similarity. This takes an optional distance limit which defaults to one-third of the
-/// given word.
-///
-/// Besides the modified Levenshtein, we use case insensitive comparison to improve accuracy
-/// on an edge case with a lower(upper)case letters mismatch.
-pub fn find_best_match_for_name_with_substrings(
-    candidates: &[Symbol],
-    lookup: Symbol,
-    dist: Option<usize>,
-) -> Option<Symbol> {
-    find_best_match_for_name_impl(true, candidates, lookup, dist)
-}
-
-/// Finds the best match for a given word in the given iterator.
-///
-/// As a loose rule to avoid the obviously incorrect suggestions, it takes
-/// an optional limit for the maximum allowable edit distance, which defaults
-/// to one-third of the given word.
-///
-/// Besides Levenshtein, we use case insensitive comparison to improve accuracy
-/// on an edge case with a lower(upper)case letters mismatch.
-pub fn find_best_match_for_name(
-    candidates: &[Symbol],
-    lookup: Symbol,
-    dist: Option<usize>,
-) -> Option<Symbol> {
-    find_best_match_for_name_impl(false, candidates, lookup, dist)
-}
-
-#[cold]
-fn find_best_match_for_name_impl(
-    use_substring_score: bool,
-    candidates: &[Symbol],
-    lookup: Symbol,
-    dist: Option<usize>,
-) -> Option<Symbol> {
-    let lookup = lookup.as_str();
-    let lookup_uppercase = lookup.to_uppercase();
-
-    // Priority of matches:
-    // 1. Exact case insensitive match
-    // 2. Levenshtein distance match
-    // 3. Sorted word match
-    if let Some(c) = candidates.iter().find(|c| c.as_str().to_uppercase() == lookup_uppercase) {
-        return Some(*c);
-    }
-
-    let mut dist = dist.unwrap_or_else(|| cmp::max(lookup.len(), 3) / 3);
-    let mut best = None;
-    for c in candidates {
-        match if use_substring_score {
-            lev_distance_with_substrings(lookup, c.as_str(), dist)
-        } else {
-            lev_distance(lookup, c.as_str(), dist)
-        } {
-            Some(0) => return Some(*c),
-            Some(d) => {
-                dist = d - 1;
-                best = Some(*c);
-            }
-            None => {}
-        }
-    }
-    if best.is_some() {
-        return best;
-    }
-
-    find_match_by_sorted_words(candidates, lookup)
-}
-
-fn find_match_by_sorted_words(iter_names: &[Symbol], lookup: &str) -> Option<Symbol> {
-    iter_names.iter().fold(None, |result, candidate| {
-        if sort_by_words(candidate.as_str()) == sort_by_words(lookup) {
-            Some(*candidate)
-        } else {
-            result
-        }
-    })
-}
-
-fn sort_by_words(name: &str) -> String {
-    let mut split_words: Vec<&str> = name.split('_').collect();
-    // We are sorting primitive &strs and can use unstable sort here.
-    split_words.sort_unstable();
-    split_words.join("_")
-}
diff --git a/compiler/rustc_span/src/lev_distance/tests.rs b/compiler/rustc_span/src/lev_distance/tests.rs
deleted file mode 100644
index ed03b22c61f..00000000000
--- a/compiler/rustc_span/src/lev_distance/tests.rs
+++ /dev/null
@@ -1,70 +0,0 @@
-use super::*;
-
-#[test]
-fn test_lev_distance() {
-    // Test bytelength agnosticity
-    for c in (0..char::MAX as u32).filter_map(char::from_u32).map(|i| i.to_string()) {
-        assert_eq!(lev_distance(&c[..], &c[..], usize::MAX), Some(0));
-    }
-
-    let a = "\nMäry häd ä little lämb\n\nLittle lämb\n";
-    let b = "\nMary häd ä little lämb\n\nLittle lämb\n";
-    let c = "Mary häd ä little lämb\n\nLittle lämb\n";
-    assert_eq!(lev_distance(a, b, usize::MAX), Some(1));
-    assert_eq!(lev_distance(b, a, usize::MAX), Some(1));
-    assert_eq!(lev_distance(a, c, usize::MAX), Some(2));
-    assert_eq!(lev_distance(c, a, usize::MAX), Some(2));
-    assert_eq!(lev_distance(b, c, usize::MAX), Some(1));
-    assert_eq!(lev_distance(c, b, usize::MAX), Some(1));
-}
-
-#[test]
-fn test_lev_distance_limit() {
-    assert_eq!(lev_distance("abc", "abcd", 1), Some(1));
-    assert_eq!(lev_distance("abc", "abcd", 0), None);
-    assert_eq!(lev_distance("abc", "xyz", 3), Some(3));
-    assert_eq!(lev_distance("abc", "xyz", 2), None);
-}
-
-#[test]
-fn test_method_name_similarity_score() {
-    assert_eq!(lev_distance_with_substrings("empty", "is_empty", 1), Some(1));
-    assert_eq!(lev_distance_with_substrings("shrunk", "rchunks", 2), None);
-    assert_eq!(lev_distance_with_substrings("abc", "abcd", 1), Some(1));
-    assert_eq!(lev_distance_with_substrings("a", "abcd", 1), None);
-    assert_eq!(lev_distance_with_substrings("edf", "eq", 1), None);
-    assert_eq!(lev_distance_with_substrings("abc", "xyz", 3), Some(3));
-    assert_eq!(lev_distance_with_substrings("abcdef", "abcdef", 2), Some(0));
-}
-
-#[test]
-fn test_find_best_match_for_name() {
-    use crate::create_default_session_globals_then;
-    create_default_session_globals_then(|| {
-        let input = vec![Symbol::intern("aaab"), Symbol::intern("aaabc")];
-        assert_eq!(
-            find_best_match_for_name(&input, Symbol::intern("aaaa"), None),
-            Some(Symbol::intern("aaab"))
-        );
-
-        assert_eq!(find_best_match_for_name(&input, Symbol::intern("1111111111"), None), None);
-
-        let input = vec![Symbol::intern("AAAA")];
-        assert_eq!(
-            find_best_match_for_name(&input, Symbol::intern("aaaa"), None),
-            Some(Symbol::intern("AAAA"))
-        );
-
-        let input = vec![Symbol::intern("AAAA")];
-        assert_eq!(
-            find_best_match_for_name(&input, Symbol::intern("aaaa"), Some(4)),
-            Some(Symbol::intern("AAAA"))
-        );
-
-        let input = vec![Symbol::intern("a_longer_variable_name")];
-        assert_eq!(
-            find_best_match_for_name(&input, Symbol::intern("a_variable_longer_name"), None),
-            Some(Symbol::intern("a_longer_variable_name"))
-        );
-    })
-}
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index e095cf3fda2..e112100aa5f 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -19,6 +19,7 @@
 #![feature(negative_impls)]
 #![feature(min_specialization)]
 #![feature(rustc_attrs)]
+#![feature(let_chains)]
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
 
@@ -46,7 +47,7 @@ pub use hygiene::{ExpnData, ExpnHash, ExpnId, LocalExpnId, SyntaxContext};
 use rustc_data_structures::stable_hasher::HashingControls;
 pub mod def_id;
 use def_id::{CrateNum, DefId, DefPathHash, LocalDefId, LOCAL_CRATE};
-pub mod lev_distance;
+pub mod edit_distance;
 mod span_encoding;
 pub use span_encoding::{Span, DUMMY_SP};
 
diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs
index cd00fd0daf9..87f077325f8 100644
--- a/library/core/src/ffi/c_str.rs
+++ b/library/core/src/ffi/c_str.rs
@@ -457,6 +457,10 @@ impl CStr {
     /// to a contiguous region of memory terminated with a 0 byte to represent
     /// the end of the string.
     ///
+    /// The type of the returned pointer is
+    /// [`*const c_char`][crate::ffi::c_char], and whether it's
+    /// an alias for `*const i8` or `*const u8` is platform-specific.
+    ///
     /// **WARNING**
     ///
     /// The returned pointer is read-only; writing to it (including passing it
@@ -470,6 +474,7 @@ impl CStr {
     /// # #![allow(unused_must_use)] #![allow(temporary_cstring_as_ptr)]
     /// use std::ffi::CString;
     ///
+    /// // Do not do this:
     /// let ptr = CString::new("Hello").expect("CString::new failed").as_ptr();
     /// unsafe {
     ///     // `ptr` is dangling
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index e7d7215166b..f0fcdf0d5a0 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -111,10 +111,18 @@ impl Step for Std {
         let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target);
         if compiler_to_use != compiler {
             builder.ensure(Std::new(compiler_to_use, target));
-            builder.info(&format!(
-                "Uplifting stage1 library ({} -> {})",
-                compiler_to_use.host, target
-            ));
+            let msg = if compiler_to_use.host == target {
+                format!(
+                    "Uplifting library (stage{} -> stage{})",
+                    compiler_to_use.stage, compiler.stage
+                )
+            } else {
+                format!(
+                    "Uplifting library (stage{}:{} -> stage{}:{})",
+                    compiler_to_use.stage, compiler_to_use.host, compiler.stage, target
+                )
+            };
+            builder.info(&msg);
 
             // Even if we're not building std this stage, the new sysroot must
             // still contain the third party objects needed by various targets.
@@ -134,13 +142,23 @@ impl Step for Std {
             cargo.arg("-p").arg(krate);
         }
 
-        builder.info(&format!(
-            "Building{} stage{} library artifacts ({} -> {})",
-            crate_description(&self.crates),
-            compiler.stage,
-            &compiler.host,
-            target,
-        ));
+        let msg = if compiler.host == target {
+            format!(
+                "Building{} stage{} library artifacts ({}) ",
+                crate_description(&self.crates),
+                compiler.stage,
+                compiler.host
+            )
+        } else {
+            format!(
+                "Building{} stage{} library artifacts ({} -> {})",
+                crate_description(&self.crates),
+                compiler.stage,
+                compiler.host,
+                target,
+            )
+        };
+        builder.info(&msg);
         run_cargo(
             builder,
             cargo,
@@ -438,10 +456,6 @@ impl Step for StdLink {
         let compiler = self.compiler;
         let target_compiler = self.target_compiler;
         let target = self.target;
-        builder.info(&format!(
-            "Copying stage{} library from stage{} ({} -> {} / {})",
-            target_compiler.stage, compiler.stage, &compiler.host, target_compiler.host, target
-        ));
         let libdir = builder.sysroot_libdir(target_compiler, target);
         let hostdir = builder.sysroot_libdir(target_compiler, compiler.host);
         add_to_sysroot(builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target));
@@ -715,8 +729,22 @@ impl Step for Rustc {
         let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target);
         if compiler_to_use != compiler {
             builder.ensure(Rustc::new(compiler_to_use, target));
-            builder
-                .info(&format!("Uplifting stage1 rustc ({} -> {})", builder.config.build, target));
+            let msg = if compiler_to_use.host == target {
+                format!(
+                    "Uplifting rustc (stage{} -> stage{})",
+                    compiler_to_use.stage,
+                    compiler.stage + 1
+                )
+            } else {
+                format!(
+                    "Uplifting rustc (stage{}:{} -> stage{}:{})",
+                    compiler_to_use.stage,
+                    compiler_to_use.host,
+                    compiler.stage + 1,
+                    target
+                )
+            };
+            builder.info(&msg);
             builder.ensure(RustcLink::from_rustc(self, compiler_to_use));
             return;
         }
@@ -810,13 +838,24 @@ impl Step for Rustc {
             cargo.arg("-p").arg(krate);
         }
 
-        builder.info(&format!(
-            "Building{} stage{} compiler artifacts ({} -> {})",
-            crate_description(&self.crates),
-            compiler.stage,
-            &compiler.host,
-            target,
-        ));
+        let msg = if compiler.host == target {
+            format!(
+                "Building{} compiler artifacts (stage{} -> stage{})",
+                crate_description(&self.crates),
+                compiler.stage,
+                compiler.stage + 1
+            )
+        } else {
+            format!(
+                "Building{} compiler artifacts (stage{}:{} -> stage{}:{})",
+                crate_description(&self.crates),
+                compiler.stage,
+                compiler.host,
+                compiler.stage + 1,
+                target,
+            )
+        };
+        builder.info(&msg);
         run_cargo(
             builder,
             cargo,
@@ -1000,10 +1039,6 @@ impl Step for RustcLink {
         let compiler = self.compiler;
         let target_compiler = self.target_compiler;
         let target = self.target;
-        builder.info(&format!(
-            "Copying stage{} rustc from stage{} ({} -> {} / {})",
-            target_compiler.stage, compiler.stage, &compiler.host, target_compiler.host, target
-        ));
         add_to_sysroot(
             builder,
             &builder.sysroot_libdir(target_compiler, target),
@@ -1077,10 +1112,15 @@ impl Step for CodegenBackend {
 
         let tmp_stamp = out_dir.join(".tmp.stamp");
 
-        builder.info(&format!(
-            "Building stage{} codegen backend {} ({} -> {})",
-            compiler.stage, backend, &compiler.host, target
-        ));
+        let msg = if compiler.host == target {
+            format!("Building stage{} codegen backend {}", compiler.stage, backend)
+        } else {
+            format!(
+                "Building stage{} codegen backend {} ({} -> {})",
+                compiler.stage, backend, compiler.host, target
+            )
+        };
+        builder.info(&msg);
         let files = run_cargo(builder, cargo, vec![], &tmp_stamp, vec![], false, false);
         if builder.config.dry_run() {
             return;
@@ -1386,7 +1426,12 @@ impl Step for Assemble {
 
         let stage = target_compiler.stage;
         let host = target_compiler.host;
-        builder.info(&format!("Assembling stage{} compiler ({})", stage, host));
+        let msg = if build_compiler.host == host {
+            format!("Assembling stage{} compiler", stage)
+        } else {
+            format!("Assembling stage{} compiler ({})", stage, host)
+        };
+        builder.info(&msg);
 
         // Link in all dylibs to the libdir
         let stamp = librustc_stamp(builder, build_compiler, target_compiler.host);
diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs
index ca5f500f93b..d30532ef3c6 100644
--- a/src/bootstrap/tool.rs
+++ b/src/bootstrap/tool.rs
@@ -33,6 +33,44 @@ struct ToolBuild {
     allow_features: &'static str,
 }
 
+fn tooling_output(
+    mode: Mode,
+    tool: &str,
+    build_stage: u32,
+    host: &TargetSelection,
+    target: &TargetSelection,
+) -> String {
+    match mode {
+        // depends on compiler stage, different to host compiler
+        Mode::ToolRustc => {
+            if host == target {
+                format!("Building tool {} (stage{} -> stage{})", tool, build_stage, build_stage + 1)
+            } else {
+                format!(
+                    "Building tool {} (stage{}:{} -> stage{}:{})",
+                    tool,
+                    build_stage,
+                    host,
+                    build_stage + 1,
+                    target
+                )
+            }
+        }
+        // doesn't depend on compiler, same as host compiler
+        Mode::ToolStd => {
+            if host == target {
+                format!("Building tool {} (stage{})", tool, build_stage)
+            } else {
+                format!(
+                    "Building tool {} (stage{}:{} -> stage{}:{})",
+                    tool, build_stage, host, build_stage, target
+                )
+            }
+        }
+        _ => format!("Building tool {} (stage{})", tool, build_stage),
+    }
+}
+
 impl Step for ToolBuild {
     type Output = Option<PathBuf>;
 
@@ -74,8 +112,14 @@ impl Step for ToolBuild {
         if !self.allow_features.is_empty() {
             cargo.allow_features(self.allow_features);
         }
-
-        builder.info(&format!("Building stage{} tool {} ({})", compiler.stage, tool, target));
+        let msg = tooling_output(
+            self.mode,
+            self.tool,
+            self.compiler.stage,
+            &self.compiler.host,
+            &self.target,
+        );
+        builder.info(&msg);
         let mut duplicates = Vec::new();
         let is_expected = compile::stream_cargo(builder, cargo, vec![], &mut |msg| {
             // Only care about big things like the RLS/Cargo for now
@@ -562,10 +606,14 @@ impl Step for Rustdoc {
             features.as_slice(),
         );
 
-        builder.info(&format!(
-            "Building rustdoc for stage{} ({})",
-            target_compiler.stage, target_compiler.host
-        ));
+        let msg = tooling_output(
+            Mode::ToolRustc,
+            "rustdoc",
+            build_compiler.stage,
+            &self.compiler.host,
+            &target,
+        );
+        builder.info(&msg);
         builder.run(&mut cargo.into());
 
         // Cargo adds a number of paths to the dylib search path on windows, which results in
diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs
index 8c733ddefc0..890b3e8d67f 100644
--- a/src/librustdoc/passes/strip_hidden.rs
+++ b/src/librustdoc/passes/strip_hidden.rs
@@ -62,7 +62,7 @@ impl<'a, 'tcx> Stripper<'a, 'tcx> {
 
     /// In case `i` is a non-hidden impl block, then we special-case it by changing the value
     /// of `is_in_hidden_item` to `true` because the impl children inherit its visibility.
-    fn recurse_in_impl(&mut self, i: Item) -> Item {
+    fn recurse_in_impl_or_exported_macro(&mut self, i: Item) -> Item {
         let prev = mem::replace(&mut self.is_in_hidden_item, false);
         let ret = self.fold_item_recur(i);
         self.is_in_hidden_item = prev;
@@ -73,9 +73,17 @@ impl<'a, 'tcx> Stripper<'a, 'tcx> {
 impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> {
     fn fold_item(&mut self, i: Item) -> Option<Item> {
         let has_doc_hidden = i.attrs.lists(sym::doc).has_word(sym::hidden);
-        let is_impl = matches!(*i.kind, clean::ImplItem(..));
+        let is_impl_or_exported_macro = match *i.kind {
+            clean::ImplItem(..) => true,
+            // If the macro has the `#[macro_export]` attribute, it means it's accessible at the
+            // crate level so it should be handled differently.
+            clean::MacroItem(..) => {
+                i.attrs.other_attrs.iter().any(|attr| attr.has_name(sym::macro_export))
+            }
+            _ => false,
+        };
         let mut is_hidden = has_doc_hidden;
-        if !is_impl {
+        if !is_impl_or_exported_macro {
             is_hidden = self.is_in_hidden_item || has_doc_hidden;
             if !is_hidden && i.inline_stmt_id.is_none() {
                 // We don't need to check if it's coming from a reexport since the reexport itself was
@@ -92,8 +100,8 @@ impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> {
             if self.update_retained {
                 self.retained.insert(i.item_id);
             }
-            return Some(if is_impl {
-                self.recurse_in_impl(i)
+            return Some(if is_impl_or_exported_macro {
+                self.recurse_in_impl_or_exported_macro(i)
             } else {
                 self.set_is_in_hidden_item_and_fold(false, i)
             });
diff --git a/tests/rustdoc/issue-108231.rs b/tests/rustdoc/issue-108231.rs
new file mode 100644
index 00000000000..684f0494fd5
--- /dev/null
+++ b/tests/rustdoc/issue-108231.rs
@@ -0,0 +1,23 @@
+// Regression test for <https://github.com/rust-lang/rust/issues/108231>.
+// Macros with `#[macro_export]` attribute should be visible at the top level
+// even if they are inside a doc hidden item.
+
+#![crate_name = "foo"]
+
+// @has 'foo/index.html'
+// @count - '//*[@id="main-content"]//a[@class="macro"]' 1
+// @has - '//*[@id="main-content"]//a[@class="macro"]' 'foo'
+
+#[doc(hidden)]
+pub mod __internal {
+    /// This one should be visible.
+    #[macro_export]
+    macro_rules! foo {
+        () => {};
+    }
+
+    /// This one should be hidden.
+    macro_rules! bar {
+        () => {};
+    }
+}
diff --git a/tests/rustdoc/reexport-hidden-macro.rs b/tests/rustdoc/reexport-hidden-macro.rs
new file mode 100644
index 00000000000..afcfa979616
--- /dev/null
+++ b/tests/rustdoc/reexport-hidden-macro.rs
@@ -0,0 +1,22 @@
+// Ensure that inlined reexport of hidden macros is working as expected.
+// Part of <https://github.com/rust-lang/rust/issues/59368>.
+
+#![crate_name = "foo"]
+
+// @has 'foo/index.html'
+// @has - '//*[@id="main-content"]//a[@href="macro.Macro2.html"]' 'Macro2'
+
+// @has 'foo/macro.Macro2.html'
+// @has - '//*[@class="docblock"]' 'Displayed'
+
+#[macro_export]
+#[doc(hidden)]
+macro_rules! foo {
+    () => {};
+}
+
+/// not displayed
+pub use crate::foo as Macro;
+/// Displayed
+#[doc(inline)]
+pub use crate::foo as Macro2;
diff --git a/tests/ui/check-cfg/invalid-cfg-value.stderr b/tests/ui/check-cfg/invalid-cfg-value.stderr
index 60abcb18824..83383ea61a4 100644
--- a/tests/ui/check-cfg/invalid-cfg-value.stderr
+++ b/tests/ui/check-cfg/invalid-cfg-value.stderr
@@ -2,7 +2,9 @@ warning: unexpected `cfg` condition value
   --> $DIR/invalid-cfg-value.rs:7:7
    |
 LL | #[cfg(feature = "sedre")]
-   |       ^^^^^^^^^^^^^^^^^
+   |       ^^^^^^^^^^-------
+   |                 |
+   |                 help: did you mean: `"serde"`
    |
    = note: expected values for `feature` are: full, serde
    = note: `#[warn(unexpected_cfgs)]` on by default
diff --git a/tests/ui/const-generics/early/invalid-const-arguments.stderr b/tests/ui/const-generics/early/invalid-const-arguments.stderr
index b46e7e24f49..cee34e3b715 100644
--- a/tests/ui/const-generics/early/invalid-const-arguments.stderr
+++ b/tests/ui/const-generics/early/invalid-const-arguments.stderr
@@ -49,12 +49,9 @@ error[E0747]: type provided when a constant was expected
   --> $DIR/invalid-const-arguments.rs:10:19
    |
 LL | impl<N> Foo for B<N> {}
-   |                   ^
-   |
-help: consider changing this type parameter to be a `const` generic
-   |
-LL | impl<const N: u8> Foo for B<N> {}
-   |      ~~~~~~~~~~~
+   |      -            ^
+   |      |
+   |      help: consider changing this type parameter to a const parameter: `const N: u8`
 
 error[E0747]: unresolved item provided when a constant was expected
   --> $DIR/invalid-const-arguments.rs:14:32
diff --git a/tests/ui/did_you_mean/println-typo.rs b/tests/ui/did_you_mean/println-typo.rs
new file mode 100644
index 00000000000..685b5e1f284
--- /dev/null
+++ b/tests/ui/did_you_mean/println-typo.rs
@@ -0,0 +1,6 @@
+// https://internals.rust-lang.org/t/18227
+
+fn main() {
+    prinltn!(); //~ ERROR cannot find macro `prinltn` in this scope
+    //^ a macro with a similar name exists: `println`
+}
diff --git a/tests/ui/did_you_mean/println-typo.stderr b/tests/ui/did_you_mean/println-typo.stderr
new file mode 100644
index 00000000000..43b7b1894e2
--- /dev/null
+++ b/tests/ui/did_you_mean/println-typo.stderr
@@ -0,0 +1,11 @@
+error: cannot find macro `prinltn` in this scope
+  --> $DIR/println-typo.rs:4:5
+   |
+LL |     prinltn!();
+   |     ^^^^^^^ help: a macro with a similar name exists: `println`
+  --> $SRC_DIR/std/src/macros.rs:LL:COL
+   |
+   = note: similarly named macro `println` defined here
+
+error: aborting due to previous error
+
diff --git a/tests/ui/errors/traits/blame-trait-error-spans-on-exprs.rs b/tests/ui/errors/traits/blame-trait-error-spans-on-exprs.rs
index 2b75f432412..6fea409ed47 100644
--- a/tests/ui/errors/traits/blame-trait-error-spans-on-exprs.rs
+++ b/tests/ui/errors/traits/blame-trait-error-spans-on-exprs.rs
@@ -71,6 +71,8 @@ struct DoubleWrapper<T> {
 
 impl<T: T1> T1 for DoubleWrapper<T> {}
 
+impl<'a, T: T2> T1 for &'a T {}
+
 fn example<Q>(q: Q) {
     // In each of the following examples, we expect the error span to point at the 'q' variable,
     // since the missing constraint is `Q: T3`.
@@ -126,6 +128,10 @@ fn example<Q>(q: Q) {
         Two { a: Two { a: (), b: Two { a: Two { a: (), b: q }, b: () } }, b: () },
         //~^ ERROR the trait bound `Q: T1` is not satisfied [E0277]
     );
+
+    // Verifies for reference:
+    want(&Burrito { spicy: false, filling: q });
+    //~^ ERROR the trait bound `Q: T3` is not satisfied [E0277]
 }
 
 fn main() {}
diff --git a/tests/ui/errors/traits/blame-trait-error-spans-on-exprs.stderr b/tests/ui/errors/traits/blame-trait-error-spans-on-exprs.stderr
index 5f87c670d8a..6913771f288 100644
--- a/tests/ui/errors/traits/blame-trait-error-spans-on-exprs.stderr
+++ b/tests/ui/errors/traits/blame-trait-error-spans-on-exprs.stderr
@@ -1,5 +1,5 @@
 error[E0277]: the trait bound `Q: T3` is not satisfied
-  --> $DIR/blame-trait-error-spans-on-exprs.rs:79:60
+  --> $DIR/blame-trait-error-spans-on-exprs.rs:81:60
    |
 LL |     want(Wrapper { value: Burrito { spicy: false, filling: q } });
    |     ---- required by a bound introduced by this call       ^ the trait `T3` is not implemented for `Q`
@@ -29,7 +29,7 @@ LL | fn example<Q: T3>(q: Q) {
    |             ++++
 
 error[E0277]: the trait bound `Q: T3` is not satisfied
-  --> $DIR/blame-trait-error-spans-on-exprs.rs:83:84
+  --> $DIR/blame-trait-error-spans-on-exprs.rs:85:84
    |
 LL |     want(Wrapper { value: BurritoKinds::SmallBurrito { spicy: true, small_filling: q } });
    |     ---- required by a bound introduced by this call                               ^ the trait `T3` is not implemented for `Q`
@@ -59,7 +59,7 @@ LL | fn example<Q: T3>(q: Q) {
    |             ++++
 
 error[E0277]: the trait bound `Q: T3` is not satisfied
-  --> $DIR/blame-trait-error-spans-on-exprs.rs:87:39
+  --> $DIR/blame-trait-error-spans-on-exprs.rs:89:39
    |
 LL |     want(Wrapper { value: Taco(false, q) });
    |     ----                              ^ the trait `T3` is not implemented for `Q`
@@ -91,7 +91,7 @@ LL | fn example<Q: T3>(q: Q) {
    |             ++++
 
 error[E0277]: the trait bound `Q: T3` is not satisfied
-  --> $DIR/blame-trait-error-spans-on-exprs.rs:91:27
+  --> $DIR/blame-trait-error-spans-on-exprs.rs:93:27
    |
 LL |     want(Wrapper { value: TacoKinds::OneTaco(false, q) });
    |     ----                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `T3` is not implemented for `Q`
@@ -123,7 +123,7 @@ LL | fn example<Q: T3>(q: Q) {
    |             ++++
 
 error[E0277]: the trait bound `Q: T3` is not satisfied
-  --> $DIR/blame-trait-error-spans-on-exprs.rs:95:74
+  --> $DIR/blame-trait-error-spans-on-exprs.rs:97:74
    |
 LL |     want(Wrapper { value: GenericBurrito { spiciness: NotSpicy, filling: q } });
    |     ---- required by a bound introduced by this call                     ^ the trait `T3` is not implemented for `Q`
@@ -153,7 +153,7 @@ LL | fn example<Q: T3>(q: Q) {
    |             ++++
 
 error[E0277]: the trait bound `Q: T2` is not satisfied
-  --> $DIR/blame-trait-error-spans-on-exprs.rs:99:14
+  --> $DIR/blame-trait-error-spans-on-exprs.rs:101:14
    |
 LL |     want((3, q));
    |     ----     ^ the trait `T2` is not implemented for `Q`
@@ -178,7 +178,7 @@ LL | fn example<Q: T2>(q: Q) {
    |             ++++
 
 error[E0277]: the trait bound `Q: T3` is not satisfied
-  --> $DIR/blame-trait-error-spans-on-exprs.rs:103:31
+  --> $DIR/blame-trait-error-spans-on-exprs.rs:105:31
    |
 LL |     want(Wrapper { value: (3, q) });
    |     ----                      ^ the trait `T3` is not implemented for `Q`
@@ -210,7 +210,7 @@ LL | fn example<Q: T3>(q: Q) {
    |             ++++
 
 error[E0277]: the trait bound `Q: T3` is not satisfied
-  --> $DIR/blame-trait-error-spans-on-exprs.rs:107:15
+  --> $DIR/blame-trait-error-spans-on-exprs.rs:109:15
    |
 LL |     want(((3, q), 5));
    |     ----      ^ the trait `T3` is not implemented for `Q`
@@ -242,7 +242,7 @@ LL | fn example<Q: T3>(q: Q) {
    |             ++++
 
 error[E0277]: the trait bound `Q: T1` is not satisfied
-  --> $DIR/blame-trait-error-spans-on-exprs.rs:110:49
+  --> $DIR/blame-trait-error-spans-on-exprs.rs:112:49
    |
 LL |     want(DoubleWrapper { item: Wrapper { value: q } });
    |     ----                                        ^ the trait `T1` is not implemented for `Q`
@@ -267,7 +267,7 @@ LL | fn example<Q: T1>(q: Q) {
    |             ++++
 
 error[E0277]: the trait bound `Q: T1` is not satisfied
-  --> $DIR/blame-trait-error-spans-on-exprs.rs:113:88
+  --> $DIR/blame-trait-error-spans-on-exprs.rs:115:88
    |
 LL |     want(DoubleWrapper { item: Wrapper { value: DoubleWrapper { item: Wrapper { value: q } } } });
    |     ---- required by a bound introduced by this call                                   ^ the trait `T1` is not implemented for `Q`
@@ -292,7 +292,7 @@ LL | fn example<Q: T1>(q: Q) {
    |             ++++
 
 error[E0277]: the trait bound `Q: T3` is not satisfied
-  --> $DIR/blame-trait-error-spans-on-exprs.rs:117:27
+  --> $DIR/blame-trait-error-spans-on-exprs.rs:119:27
    |
 LL |     want(Wrapper { value: AliasBurrito { spiciness: q, filling: q } });
    |     ----                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `T3` is not implemented for `Q`
@@ -324,7 +324,7 @@ LL | fn example<Q: T3>(q: Q) {
    |             ++++
 
 error[E0277]: the trait bound `Q: T1` is not satisfied
-  --> $DIR/blame-trait-error-spans-on-exprs.rs:120:35
+  --> $DIR/blame-trait-error-spans-on-exprs.rs:122:35
    |
 LL |     want(Two { a: Two { a: (), b: q }, b: () });
    |     ----                          ^ the trait `T1` is not implemented for `Q`
@@ -349,7 +349,7 @@ LL | fn example<Q: T1>(q: Q) {
    |             ++++
 
 error[E0277]: the trait bound `Q: T1` is not satisfied
-  --> $DIR/blame-trait-error-spans-on-exprs.rs:126:59
+  --> $DIR/blame-trait-error-spans-on-exprs.rs:128:59
    |
 LL |     want(
    |     ---- required by a bound introduced by this call
@@ -375,6 +375,38 @@ help: consider restricting type parameter `Q`
 LL | fn example<Q: T1>(q: Q) {
    |             ++++
 
-error: aborting due to 13 previous errors
+error[E0277]: the trait bound `Q: T3` is not satisfied
+  --> $DIR/blame-trait-error-spans-on-exprs.rs:133:44
+   |
+LL |     want(&Burrito { spicy: false, filling: q });
+   |     ----                                   ^ the trait `T3` is not implemented for `Q`
+   |     |
+   |     required by a bound introduced by this call
+   |
+note: required for `Burrito<Q>` to implement `T2`
+  --> $DIR/blame-trait-error-spans-on-exprs.rs:22:13
+   |
+LL | impl<A: T3> T2 for Burrito<A> {}
+   |         --  ^^     ^^^^^^^^^^
+   |         |
+   |         unsatisfied trait bound introduced here
+note: required for `&Burrito<Q>` to implement `T1`
+  --> $DIR/blame-trait-error-spans-on-exprs.rs:74:17
+   |
+LL | impl<'a, T: T2> T1 for &'a T {}
+   |             --  ^^     ^^^^^
+   |             |
+   |             unsatisfied trait bound introduced here
+note: required by a bound in `want`
+  --> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
+   |
+LL | fn want<V: T1>(_x: V) {}
+   |            ^^ required by this bound in `want`
+help: consider restricting type parameter `Q`
+   |
+LL | fn example<Q: T3>(q: Q) {
+   |             ++++
+
+error: aborting due to 14 previous errors
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/suggest-deferences/issue-39029.stderr b/tests/ui/traits/suggest-deferences/issue-39029.stderr
index 49e20c6a76a..49105de3d69 100644
--- a/tests/ui/traits/suggest-deferences/issue-39029.stderr
+++ b/tests/ui/traits/suggest-deferences/issue-39029.stderr
@@ -1,8 +1,8 @@
 error[E0277]: the trait bound `NoToSocketAddrs: ToSocketAddrs` is not satisfied
-  --> $DIR/issue-39029.rs:16:37
+  --> $DIR/issue-39029.rs:16:38
    |
 LL |     let _errors = TcpListener::bind(&bad);
-   |                   ----------------- ^^^^ the trait `ToSocketAddrs` is not implemented for `NoToSocketAddrs`
+   |                   -----------------  ^^^ the trait `ToSocketAddrs` is not implemented for `NoToSocketAddrs`
    |                   |
    |                   required by a bound introduced by this call
    |