about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-11-14 08:03:01 +0000
committerbors <bors@rust-lang.org>2019-11-14 08:03:01 +0000
commita2491ee4e6c74451371aee8f4a2a5786d7a35406 (patch)
tree276dc5a1368ab04c84a6387bf15bffab129aa5ff /src
parent5e380b797b22e5361a43b2b82f6278df17d89f3e (diff)
parentd145d1e72c184177f40447c63b2813dcef729860 (diff)
downloadrust-a2491ee4e6c74451371aee8f4a2a5786d7a35406.tar.gz
rust-a2491ee4e6c74451371aee8f4a2a5786d7a35406.zip
Auto merge of #66403 - JohnTitor:rollup-7obuivl, r=JohnTitor
Rollup of 9 pull requests

Successful merges:

 - #66253 (Improve errors after re rebalance coherence)
 - #66264 (fix an ICE in macro's diagnostic message)
 - #66349 (expand source_util macros with def-site context)
 - #66351 (Tweak non-char/numeric in range pattern diagnostic)
 - #66360 (Fix link to Exten in Vec::set_len)
 - #66361 (parser: don't use `unreachable!()` in `fn unexpected`.)
 - #66363 (Improve error message in make_tests)
 - #66369 (compiletest: Obtain timestamps for common inputs only once)
 - #66372 (Fix broken links in Ipv4Addr::is_benchmarking docs)

Failed merges:

r? @ghost
Diffstat (limited to 'src')
-rw-r--r--src/liballoc/vec.rs2
-rw-r--r--src/librustc/traits/coherence.rs12
-rw-r--r--src/librustc_parse/parser/item.rs15
-rw-r--r--src/librustc_parse/parser/mod.rs4
-rw-r--r--src/librustc_typeck/check/pat.rs90
-rw-r--r--src/librustc_typeck/coherence/orphan.rs64
-rw-r--r--src/librustc_typeck/error_codes.rs23
-rw-r--r--src/libstd/net/ip.rs4
-rw-r--r--src/libsyntax_ext/source_util.rs8
-rw-r--r--src/test/ui/coherence/coherence-all-remote.stderr1
-rw-r--r--src/test/ui/coherence/coherence-bigint-param.stderr7
-rw-r--r--src/test/ui/coherence/coherence-cross-crate-conflict.stderr1
-rw-r--r--src/test/ui/coherence/coherence-lone-type-parameter.stderr1
-rw-r--r--src/test/ui/coherence/impl[t]-foreign-for-fundamental[t].stderr1
-rw-r--r--src/test/ui/coherence/impl[t]-foreign[foreign]-for-fundamental[t].stderr2
-rw-r--r--src/test/ui/coherence/impl[t]-foreign[foreign]-for-t.stderr1
-rw-r--r--src/test/ui/coherence/impl[t]-foreign[fundamental[t]]-for-foreign.stderr2
-rw-r--r--src/test/ui/coherence/impl[t]-foreign[fundamental[t]]-for-fundamental[t].stderr2
-rw-r--r--src/test/ui/coherence/impl[t]-foreign[fundamental[t]]-for-t.stderr2
-rw-r--r--src/test/ui/coherence/impl[t]-foreign[fundamental[t]_local]-for-foreign.rs4
-rw-r--r--src/test/ui/coherence/impl[t]-foreign[fundamental[t]_local]-for-foreign.stderr14
-rw-r--r--src/test/ui/coherence/impl[t]-foreign[local]-for-fundamental[t].rs4
-rw-r--r--src/test/ui/coherence/impl[t]-foreign[local]-for-fundamental[t].stderr14
-rw-r--r--src/test/ui/coherence/impl[t]-foreign[local]-for-t.rs2
-rw-r--r--src/test/ui/coherence/impl[t]-foreign[local]-for-t.stderr7
-rw-r--r--src/test/ui/coherence/impl[t]-foreign[t]-for-foreign.stderr1
-rw-r--r--src/test/ui/coherence/impl[t]-foreign[t]-for-fundamental.stderr2
-rw-r--r--src/test/ui/coherence/impl[t]-foreign[t]-for-t.stderr1
-rw-r--r--src/test/ui/error-codes/E0029-teach.stderr7
-rw-r--r--src/test/ui/error-codes/E0029.stderr8
-rw-r--r--src/test/ui/error-codes/e0119/issue-28981.stderr1
-rw-r--r--src/test/ui/issues/issue-41974.stderr1
-rw-r--r--src/test/ui/match/match-range-fail.rs9
-rw-r--r--src/test/ui/match/match-range-fail.stderr28
-rw-r--r--src/test/ui/orphan-check-diagnostics.stderr1
-rw-r--r--src/test/ui/parser/issue-66357-unexpected-unreachable.rs14
-rw-r--r--src/test/ui/parser/issue-66357-unexpected-unreachable.stderr16
-rw-r--r--src/test/ui/parser/macros-no-semicolon-items.stderr6
-rw-r--r--src/test/ui/parser/mbe_missing_right_paren.rs3
-rw-r--r--src/test/ui/parser/mbe_missing_right_paren.stderr31
-rw-r--r--src/test/ui/parser/recover-range-pats.stderr72
-rw-r--r--src/test/ui/pattern/patkind-litrange-no-expr.stderr8
-rw-r--r--src/test/ui/qualified/qualified-path-params.stderr7
-rw-r--r--src/tools/compiletest/src/main.rs164
44 files changed, 430 insertions, 237 deletions
diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs
index 5b53a6a2899..b1741f00507 100644
--- a/src/liballoc/vec.rs
+++ b/src/liballoc/vec.rs
@@ -861,7 +861,7 @@ impl<T> Vec<T> {
     ///
     /// [`truncate`]: #method.truncate
     /// [`resize`]: #method.resize
-    /// [`extend`]: #method.extend-1
+    /// [`extend`]: ../../std/iter/trait.Extend.html#tymethod.extend
     /// [`clear`]: #method.clear
     ///
     /// # Safety
diff --git a/src/librustc/traits/coherence.rs b/src/librustc/traits/coherence.rs
index 1645f94535f..778bba1eef6 100644
--- a/src/librustc/traits/coherence.rs
+++ b/src/librustc/traits/coherence.rs
@@ -238,7 +238,7 @@ pub fn trait_ref_is_local_or_fundamental<'tcx>(
 
 pub enum OrphanCheckErr<'tcx> {
     NonLocalInputType(Vec<(Ty<'tcx>, bool /* Is this the first input type? */)>),
-    UncoveredTy(Ty<'tcx>),
+    UncoveredTy(Ty<'tcx>, Option<Ty<'tcx>>),
 }
 
 /// Checks the coherence orphan rules. `impl_def_id` should be the
@@ -402,7 +402,15 @@ fn orphan_check_trait_ref<'tcx>(
             return Ok(());
         } else if let ty::Param(_) = input_ty.kind {
             debug!("orphan_check_trait_ref: uncovered ty: `{:?}`", input_ty);
-            return Err(OrphanCheckErr::UncoveredTy(input_ty))
+            let local_type = trait_ref
+                .input_types()
+                .flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate))
+                .filter(|ty| ty_is_non_local_constructor(tcx, ty, in_crate).is_none())
+                .next();
+
+            debug!("orphan_check_trait_ref: uncovered ty local_type: `{:?}`", local_type);
+
+            return Err(OrphanCheckErr::UncoveredTy(input_ty, local_type))
         }
         if let Some(non_local_tys) = non_local_tys {
             for input_ty in non_local_tys {
diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs
index 3e21436d313..1f386b8bbbf 100644
--- a/src/librustc_parse/parser/item.rs
+++ b/src/librustc_parse/parser/item.rs
@@ -1742,14 +1742,25 @@ impl<'a> Parser<'a> {
     }
 
     fn report_invalid_macro_expansion_item(&self) {
+        let has_close_delim = self.sess.source_map()
+            .span_to_snippet(self.prev_span)
+            .map(|s| s.ends_with(")") || s.ends_with("]"))
+            .unwrap_or(false);
+        let right_brace_span = if has_close_delim {
+            // it's safe to peel off one character only when it has the close delim
+            self.prev_span.with_lo(self.prev_span.hi() - BytePos(1))
+        } else {
+            self.sess.source_map().next_point(self.prev_span)
+        };
+
         self.struct_span_err(
             self.prev_span,
             "macros that expand to items must be delimited with braces or followed by a semicolon",
         ).multipart_suggestion(
             "change the delimiters to curly braces",
             vec![
-                (self.prev_span.with_hi(self.prev_span.lo() + BytePos(1)), String::from(" {")),
-                (self.prev_span.with_lo(self.prev_span.hi() - BytePos(1)), '}'.to_string()),
+                (self.prev_span.with_hi(self.prev_span.lo() + BytePos(1)), "{".to_string()),
+                (right_brace_span, '}'.to_string()),
             ],
             Applicability::MaybeIncorrect,
         ).span_suggestion(
diff --git a/src/librustc_parse/parser/mod.rs b/src/librustc_parse/parser/mod.rs
index a491d91e20f..d5d8604be85 100644
--- a/src/librustc_parse/parser/mod.rs
+++ b/src/librustc_parse/parser/mod.rs
@@ -443,7 +443,9 @@ impl<'a> Parser<'a> {
     crate fn unexpected<T>(&mut self) -> PResult<'a, T> {
         match self.expect_one_of(&[], &[]) {
             Err(e) => Err(e),
-            Ok(_) => unreachable!(),
+            // We can get `Ok(true)` from `recover_closing_delimiter`
+            // which is called in `expected_one_of_not_found`.
+            Ok(_) => FatalError.raise(),
         }
     }
 
diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs
index a4c9862bc74..9421dbc2b2c 100644
--- a/src/librustc_typeck/check/pat.rs
+++ b/src/librustc_typeck/check/pat.rs
@@ -362,37 +362,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             || ty.is_char()
             || ty.references_error()
         };
-        let lhs_compat = numeric_or_char(lhs_ty);
-        let rhs_compat = numeric_or_char(rhs_ty);
-
-        if !lhs_compat || !rhs_compat {
-            let span = if !lhs_compat && !rhs_compat {
-                span
-            } else if !lhs_compat {
-                begin.span
-            } else {
-                end.span
-            };
+        let lhs_fail = !numeric_or_char(lhs_ty);
+        let rhs_fail = !numeric_or_char(rhs_ty);
 
-            let mut err = struct_span_err!(
-                self.tcx.sess,
-                span,
-                E0029,
-                "only char and numeric types are allowed in range patterns"
+        if lhs_fail || rhs_fail {
+            self.emit_err_pat_range(
+                span, begin.span, end.span, lhs_fail, rhs_fail, lhs_ty, rhs_ty
             );
-            err.span_label(span, "ranges require char or numeric types");
-            err.note(&format!("start type: {}", self.ty_to_string(lhs_ty)));
-            err.note(&format!("end type: {}", self.ty_to_string(rhs_ty)));
-            if self.tcx.sess.teach(&err.get_code().unwrap()) {
-                err.note(
-                    "In a match expression, only numbers and characters can be matched \
-                        against a range. This is because the compiler checks that the range \
-                        is non-empty at compile-time, and is unable to evaluate arbitrary \
-                        comparison functions. If you want to capture values of an orderable \
-                        type between two end-points, you can use a guard."
-                    );
-            }
-            err.emit();
             return None;
         }
 
@@ -406,6 +382,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         Some(common_type)
     }
 
+    fn emit_err_pat_range(
+        &self,
+        span: Span,
+        begin_span: Span,
+        end_span: Span,
+        lhs_fail: bool,
+        rhs_fail: bool,
+        lhs_ty: Ty<'tcx>,
+        rhs_ty: Ty<'tcx>,
+    ) {
+        let span = if lhs_fail && rhs_fail {
+            span
+        } else if lhs_fail {
+            begin_span
+        } else {
+            end_span
+        };
+
+        let mut err = struct_span_err!(
+            self.tcx.sess,
+            span,
+            E0029,
+            "only char and numeric types are allowed in range patterns"
+        );
+        let msg = |ty| {
+            format!("this is of type `{}` but it should be `char` or numeric", ty)
+        };
+        let mut one_side_err = |first_span, first_ty, second_span, second_ty: Ty<'_>| {
+            err.span_label(first_span, &msg(first_ty));
+            if !second_ty.references_error() {
+                err.span_label(
+                    second_span,
+                    &format!("this is of type `{}`", second_ty)
+                );
+            }
+        };
+        if lhs_fail && rhs_fail {
+            err.span_label(begin_span, &msg(lhs_ty));
+            err.span_label(end_span, &msg(rhs_ty));
+        } else if lhs_fail {
+            one_side_err(begin_span, lhs_ty, end_span, rhs_ty);
+        } else {
+            one_side_err(end_span, rhs_ty, begin_span, lhs_ty);
+        }
+        if self.tcx.sess.teach(&err.get_code().unwrap()) {
+            err.note(
+                "In a match expression, only numbers and characters can be matched \
+                    against a range. This is because the compiler checks that the range \
+                    is non-empty at compile-time, and is unable to evaluate arbitrary \
+                    comparison functions. If you want to capture values of an orderable \
+                    type between two end-points, you can use a guard."
+                );
+        }
+        err.emit();
+    }
+
     fn check_pat_ident(
         &self,
         pat: &Pat,
diff --git a/src/librustc_typeck/coherence/orphan.rs b/src/librustc_typeck/coherence/orphan.rs
index f066ca762c0..88fa09cc936 100644
--- a/src/librustc_typeck/coherence/orphan.rs
+++ b/src/librustc_typeck/coherence/orphan.rs
@@ -77,30 +77,58 @@ impl ItemLikeVisitor<'v> for OrphanChecker<'tcx> {
                     err.emit();
                     return;
                 }
-                Err(traits::OrphanCheckErr::UncoveredTy(param_ty)) => {
+                Err(traits::OrphanCheckErr::UncoveredTy(param_ty, local_type)) => {
                     let mut sp = sp;
                     for param in &generics.params {
                         if param.name.ident().to_string() == param_ty.to_string() {
                             sp = param.span;
                         }
                     }
-                    let mut err = struct_span_err!(
-                        self.tcx.sess,
-                        sp,
-                        E0210,
-                        "type parameter `{}` must be used as the type parameter for some local \
-                         type (e.g., `MyStruct<{}>`)",
-                        param_ty,
-                        param_ty
-                    );
-                    err.span_label(sp, format!(
-                        "type parameter `{}` must be used as the type parameter for some local \
-                         type",
-                        param_ty,
-                    ));
-                    err.note("only traits defined in the current crate can be implemented for a \
-                              type parameter");
-                    err.emit();
+
+                    match local_type {
+                        Some(local_type) => {
+                            struct_span_err!(
+                                self.tcx.sess,
+                                sp,
+                                E0210,
+                                "type parameter `{}` must be covered by another type \
+                                when it appears before the first local type (`{}`)",
+                                param_ty,
+                                local_type
+                            ).span_label(sp, format!(
+                                "type parameter `{}` must be covered by another type \
+                                when it appears before the first local type (`{}`)",
+                                param_ty,
+                                local_type
+                            )).note("implementing a foreign trait is only possible if at \
+                                    least one of the types for which is it implemented is local, \
+                                    and no uncovered type parameters appear before that first \
+                                    local type"
+                            ).note("in this case, 'before' refers to the following order: \
+                                    `impl<..> ForeignTrait<T1, ..., Tn> for T0`, \
+                                    where `T0` is the first and `Tn` is the last"
+                            ).emit();
+                        }
+                        None => {
+                            struct_span_err!(
+                                self.tcx.sess,
+                                sp,
+                                E0210,
+                                "type parameter `{}` must be used as the type parameter for some \
+                                local type (e.g., `MyStruct<{}>`)",
+                                param_ty,
+                                param_ty
+                            ).span_label(sp, format!(
+                                "type parameter `{}` must be used as the type parameter for some \
+                                local type",
+                                param_ty,
+                            )).note("implementing a foreign trait is only possible if at \
+                                    least one of the types for which is it implemented is local"
+                            ).note("only traits defined in the current crate can be \
+                                    implemented for a type parameter"
+                            ).emit();
+                        }
+                    };
                     return;
                 }
             }
diff --git a/src/librustc_typeck/error_codes.rs b/src/librustc_typeck/error_codes.rs
index 560abeb7057..02783f65df5 100644
--- a/src/librustc_typeck/error_codes.rs
+++ b/src/librustc_typeck/error_codes.rs
@@ -2114,8 +2114,13 @@ E0210: r##"
 This error indicates a violation of one of Rust's orphan rules for trait
 implementations. The rule concerns the use of type parameters in an
 implementation of a foreign trait (a trait defined in another crate), and
-states that type parameters must be "covered" by a local type. To understand
-what this means, it is perhaps easiest to consider a few examples.
+states that type parameters must be "covered" by a local type.
+
+When implementing a foreign trait for a foreign type,
+the trait must have one or more type parameters.
+A type local to your crate must appear before any use of any type parameters.
+
+To understand what this means, it is perhaps easier to consider a few examples.
 
 If `ForeignTrait` is a trait defined in some external crate `foo`, then the
 following trait `impl` is an error:
@@ -2173,12 +2178,18 @@ impl<P1, ..., Pm> ForeignTrait<T1, ..., Tn> for T0 { ... }
 
 where `P1, ..., Pm` are the type parameters of the `impl` and `T0, ..., Tn`
 are types. One of the types `T0, ..., Tn` must be a local type (this is another
-orphan rule, see the explanation for E0117). Let `i` be the smallest integer
-such that `Ti` is a local type. Then no type parameter can appear in any of the
-`Tj` for `j < i`.
+orphan rule, see the explanation for E0117).
 
-For information on the design of the orphan rules, see [RFC 1023].
+Both of the following must be true:
+1. At least one of the types `T0..=Tn` must be a local type.
+Let `Ti` be the first such type.
+2. No uncovered type parameters `P1..=Pm` may appear in `T0..Ti`
+(excluding `Ti`).
+
+For information on the design of the orphan rules,
+see [RFC 2451] and [RFC 1023].
 
+[RFC 2451]: https://rust-lang.github.io/rfcs/2451-re-rebalancing-coherence.html
 [RFC 1023]: https://github.com/rust-lang/rfcs/blob/master/text/1023-rebalancing-coherence.md
 "##,
 
diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs
index 21f674af8cf..e51a9b62449 100644
--- a/src/libstd/net/ip.rs
+++ b/src/libstd/net/ip.rs
@@ -633,8 +633,8 @@ impl Ipv4Addr {
     /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0`
     /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`.
     ///
-    /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112
-    /// [errate 423]: https://www.rfc-editor.org/errata/eid423
+    /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544
+    /// [errata 423]: https://www.rfc-editor.org/errata/eid423
     /// [`true`]: ../../std/primitive.bool.html
     ///
     /// # Examples
diff --git a/src/libsyntax_ext/source_util.rs b/src/libsyntax_ext/source_util.rs
index 120b533a69b..3d27af2f210 100644
--- a/src/libsyntax_ext/source_util.rs
+++ b/src/libsyntax_ext/source_util.rs
@@ -21,6 +21,7 @@ use rustc_data_structures::sync::Lrc;
 /// line!(): expands to the current line number
 pub fn expand_line(cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream)
                    -> Box<dyn base::MacResult+'static> {
+    let sp = cx.with_def_site_ctxt(sp);
     base::check_zero_tts(cx, sp, tts, "line!");
 
     let topmost = cx.expansion_cause().unwrap_or(sp);
@@ -32,6 +33,7 @@ pub fn expand_line(cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream)
 /* column!(): expands to the current column number */
 pub fn expand_column(cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream)
                   -> Box<dyn base::MacResult+'static> {
+    let sp = cx.with_def_site_ctxt(sp);
     base::check_zero_tts(cx, sp, tts, "column!");
 
     let topmost = cx.expansion_cause().unwrap_or(sp);
@@ -45,6 +47,7 @@ pub fn expand_column(cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream)
 /// out if we wanted.
 pub fn expand_file(cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream)
                    -> Box<dyn base::MacResult+'static> {
+    let sp = cx.with_def_site_ctxt(sp);
     base::check_zero_tts(cx, sp, tts, "file!");
 
     let topmost = cx.expansion_cause().unwrap_or(sp);
@@ -54,12 +57,14 @@ pub fn expand_file(cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream)
 
 pub fn expand_stringify(cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream)
                         -> Box<dyn base::MacResult+'static> {
+    let sp = cx.with_def_site_ctxt(sp);
     let s = pprust::tts_to_string(tts);
     base::MacEager::expr(cx.expr_str(sp, Symbol::intern(&s)))
 }
 
 pub fn expand_mod(cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream)
                   -> Box<dyn base::MacResult+'static> {
+    let sp = cx.with_def_site_ctxt(sp);
     base::check_zero_tts(cx, sp, tts, "module_path!");
     let mod_path = &cx.current_expansion.module.mod_path;
     let string = mod_path.iter().map(|x| x.to_string()).collect::<Vec<String>>().join("::");
@@ -72,6 +77,7 @@ pub fn expand_mod(cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream)
 /// unhygienically.
 pub fn expand_include<'cx>(cx: &'cx mut ExtCtxt<'_>, sp: Span, tts: TokenStream)
                            -> Box<dyn base::MacResult+'cx> {
+    let sp = cx.with_def_site_ctxt(sp);
     let file = match get_single_str_from_tts(cx, sp, tts, "include!") {
         Some(f) => f,
         None => return DummyResult::any(sp),
@@ -125,6 +131,7 @@ pub fn expand_include<'cx>(cx: &'cx mut ExtCtxt<'_>, sp: Span, tts: TokenStream)
 // include_str! : read the given file, insert it as a literal string expr
 pub fn expand_include_str(cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream)
                           -> Box<dyn base::MacResult+'static> {
+    let sp = cx.with_def_site_ctxt(sp);
     let file = match get_single_str_from_tts(cx, sp, tts, "include_str!") {
         Some(f) => f,
         None => return DummyResult::any(sp)
@@ -156,6 +163,7 @@ pub fn expand_include_str(cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream)
 
 pub fn expand_include_bytes(cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream)
                             -> Box<dyn base::MacResult+'static> {
+    let sp = cx.with_def_site_ctxt(sp);
     let file = match get_single_str_from_tts(cx, sp, tts, "include_bytes!") {
         Some(f) => f,
         None => return DummyResult::any(sp)
diff --git a/src/test/ui/coherence/coherence-all-remote.stderr b/src/test/ui/coherence/coherence-all-remote.stderr
index b35ae46f888..6ecfb2c5eb0 100644
--- a/src/test/ui/coherence/coherence-all-remote.stderr
+++ b/src/test/ui/coherence/coherence-all-remote.stderr
@@ -4,6 +4,7 @@ error[E0210]: type parameter `T` must be used as the type parameter for some loc
 LL | impl<T> Remote1<T> for isize { }
    |      ^ type parameter `T` must be used as the type parameter for some local type
    |
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
    = note: only traits defined in the current crate can be implemented for a type parameter
 
 error: aborting due to previous error
diff --git a/src/test/ui/coherence/coherence-bigint-param.stderr b/src/test/ui/coherence/coherence-bigint-param.stderr
index bb81d7adea2..d431c5f4b52 100644
--- a/src/test/ui/coherence/coherence-bigint-param.stderr
+++ b/src/test/ui/coherence/coherence-bigint-param.stderr
@@ -1,10 +1,11 @@
-error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
+error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`BigInt`)
   --> $DIR/coherence-bigint-param.rs:8:6
    |
 LL | impl<T> Remote1<BigInt> for T { }
-   |      ^ type parameter `T` must be used as the type parameter for some local type
+   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`BigInt`)
    |
-   = note: only traits defined in the current crate can be implemented for a type parameter
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local, and no uncovered type parameters appear before that first local type
+   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/coherence/coherence-cross-crate-conflict.stderr b/src/test/ui/coherence/coherence-cross-crate-conflict.stderr
index cb66f9b0c7f..c00751a0f23 100644
--- a/src/test/ui/coherence/coherence-cross-crate-conflict.stderr
+++ b/src/test/ui/coherence/coherence-cross-crate-conflict.stderr
@@ -13,6 +13,7 @@ error[E0210]: type parameter `A` must be used as the type parameter for some loc
 LL | impl<A> Foo for A {
    |      ^ type parameter `A` must be used as the type parameter for some local type
    |
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
    = note: only traits defined in the current crate can be implemented for a type parameter
 
 error: aborting due to 2 previous errors
diff --git a/src/test/ui/coherence/coherence-lone-type-parameter.stderr b/src/test/ui/coherence/coherence-lone-type-parameter.stderr
index 3791d96302b..2c3b4fc3ad2 100644
--- a/src/test/ui/coherence/coherence-lone-type-parameter.stderr
+++ b/src/test/ui/coherence/coherence-lone-type-parameter.stderr
@@ -4,6 +4,7 @@ error[E0210]: type parameter `T` must be used as the type parameter for some loc
 LL | impl<T> Remote for T { }
    |      ^ type parameter `T` must be used as the type parameter for some local type
    |
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
    = note: only traits defined in the current crate can be implemented for a type parameter
 
 error: aborting due to previous error
diff --git a/src/test/ui/coherence/impl[t]-foreign-for-fundamental[t].stderr b/src/test/ui/coherence/impl[t]-foreign-for-fundamental[t].stderr
index 69194bdaf56..8a951d407ca 100644
--- a/src/test/ui/coherence/impl[t]-foreign-for-fundamental[t].stderr
+++ b/src/test/ui/coherence/impl[t]-foreign-for-fundamental[t].stderr
@@ -4,6 +4,7 @@ error[E0210]: type parameter `T` must be used as the type parameter for some loc
 LL | impl<T> Remote for Box<T> {
    |      ^ type parameter `T` must be used as the type parameter for some local type
    |
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
    = note: only traits defined in the current crate can be implemented for a type parameter
 
 error: aborting due to previous error
diff --git a/src/test/ui/coherence/impl[t]-foreign[foreign]-for-fundamental[t].stderr b/src/test/ui/coherence/impl[t]-foreign[foreign]-for-fundamental[t].stderr
index b0f34419bb3..c5759244eff 100644
--- a/src/test/ui/coherence/impl[t]-foreign[foreign]-for-fundamental[t].stderr
+++ b/src/test/ui/coherence/impl[t]-foreign[foreign]-for-fundamental[t].stderr
@@ -4,6 +4,7 @@ error[E0210]: type parameter `T` must be used as the type parameter for some loc
 LL | impl<T> Remote1<u32> for Box<T> {
    |      ^ type parameter `T` must be used as the type parameter for some local type
    |
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
    = note: only traits defined in the current crate can be implemented for a type parameter
 
 error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
@@ -12,6 +13,7 @@ error[E0210]: type parameter `T` must be used as the type parameter for some loc
 LL | impl<'a, T> Remote1<u32> for &'a T {
    |          ^ type parameter `T` must be used as the type parameter for some local type
    |
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
    = note: only traits defined in the current crate can be implemented for a type parameter
 
 error: aborting due to 2 previous errors
diff --git a/src/test/ui/coherence/impl[t]-foreign[foreign]-for-t.stderr b/src/test/ui/coherence/impl[t]-foreign[foreign]-for-t.stderr
index 002f8b7286a..e8663fd7d82 100644
--- a/src/test/ui/coherence/impl[t]-foreign[foreign]-for-t.stderr
+++ b/src/test/ui/coherence/impl[t]-foreign[foreign]-for-t.stderr
@@ -4,6 +4,7 @@ error[E0210]: type parameter `T` must be used as the type parameter for some loc
 LL | impl<T> Remote1<u32> for T {
    |      ^ type parameter `T` must be used as the type parameter for some local type
    |
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
    = note: only traits defined in the current crate can be implemented for a type parameter
 
 error: aborting due to previous error
diff --git a/src/test/ui/coherence/impl[t]-foreign[fundamental[t]]-for-foreign.stderr b/src/test/ui/coherence/impl[t]-foreign[fundamental[t]]-for-foreign.stderr
index 0c43936e6d4..639bee2b8ec 100644
--- a/src/test/ui/coherence/impl[t]-foreign[fundamental[t]]-for-foreign.stderr
+++ b/src/test/ui/coherence/impl[t]-foreign[fundamental[t]]-for-foreign.stderr
@@ -4,6 +4,7 @@ error[E0210]: type parameter `T` must be used as the type parameter for some loc
 LL | impl<T> Remote1<Box<T>> for u32 {
    |      ^ type parameter `T` must be used as the type parameter for some local type
    |
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
    = note: only traits defined in the current crate can be implemented for a type parameter
 
 error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
@@ -12,6 +13,7 @@ error[E0210]: type parameter `T` must be used as the type parameter for some loc
 LL | impl<'a, T> Remote1<&'a T> for u32 {
    |          ^ type parameter `T` must be used as the type parameter for some local type
    |
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
    = note: only traits defined in the current crate can be implemented for a type parameter
 
 error: aborting due to 2 previous errors
diff --git a/src/test/ui/coherence/impl[t]-foreign[fundamental[t]]-for-fundamental[t].stderr b/src/test/ui/coherence/impl[t]-foreign[fundamental[t]]-for-fundamental[t].stderr
index f81f15b204b..0b6c81b53cd 100644
--- a/src/test/ui/coherence/impl[t]-foreign[fundamental[t]]-for-fundamental[t].stderr
+++ b/src/test/ui/coherence/impl[t]-foreign[fundamental[t]]-for-fundamental[t].stderr
@@ -4,6 +4,7 @@ error[E0210]: type parameter `T` must be used as the type parameter for some loc
 LL | impl<'a, T> Remote1<Box<T>> for &'a T {
    |          ^ type parameter `T` must be used as the type parameter for some local type
    |
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
    = note: only traits defined in the current crate can be implemented for a type parameter
 
 error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
@@ -12,6 +13,7 @@ error[E0210]: type parameter `T` must be used as the type parameter for some loc
 LL | impl<'a, T> Remote1<&'a T> for Box<T> {
    |          ^ type parameter `T` must be used as the type parameter for some local type
    |
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
    = note: only traits defined in the current crate can be implemented for a type parameter
 
 error: aborting due to 2 previous errors
diff --git a/src/test/ui/coherence/impl[t]-foreign[fundamental[t]]-for-t.stderr b/src/test/ui/coherence/impl[t]-foreign[fundamental[t]]-for-t.stderr
index 24fd492c57c..fe40490822e 100644
--- a/src/test/ui/coherence/impl[t]-foreign[fundamental[t]]-for-t.stderr
+++ b/src/test/ui/coherence/impl[t]-foreign[fundamental[t]]-for-t.stderr
@@ -4,6 +4,7 @@ error[E0210]: type parameter `T` must be used as the type parameter for some loc
 LL | impl<T> Remote1<Box<T>> for T {
    |      ^ type parameter `T` must be used as the type parameter for some local type
    |
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
    = note: only traits defined in the current crate can be implemented for a type parameter
 
 error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
@@ -12,6 +13,7 @@ error[E0210]: type parameter `T` must be used as the type parameter for some loc
 LL | impl<'a, T> Remote1<&'a T> for T {
    |          ^ type parameter `T` must be used as the type parameter for some local type
    |
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
    = note: only traits defined in the current crate can be implemented for a type parameter
 
 error: aborting due to 2 previous errors
diff --git a/src/test/ui/coherence/impl[t]-foreign[fundamental[t]_local]-for-foreign.rs b/src/test/ui/coherence/impl[t]-foreign[fundamental[t]_local]-for-foreign.rs
index 4666e449ca9..c9e3594cd34 100644
--- a/src/test/ui/coherence/impl[t]-foreign[fundamental[t]_local]-for-foreign.rs
+++ b/src/test/ui/coherence/impl[t]-foreign[fundamental[t]_local]-for-foreign.rs
@@ -8,11 +8,11 @@ use std::rc::Rc;
 struct Local;
 
 impl<T> Remote2<Box<T>, Local> for u32 {
-    //~^ ERROR type parameter `T` must be used as the type parameter for some local type
+    //~^ ERROR type parameter `T` must be covered by another type
 }
 
 impl<'a, T> Remote2<&'a T, Local> for u32 {
-    //~^ ERROR type parameter `T` must be used as the type parameter for some local type
+    //~^ ERROR type parameter `T` must be covered by another type
 }
 
 fn main() {}
diff --git a/src/test/ui/coherence/impl[t]-foreign[fundamental[t]_local]-for-foreign.stderr b/src/test/ui/coherence/impl[t]-foreign[fundamental[t]_local]-for-foreign.stderr
index 6a1db165416..1eaef59b3f8 100644
--- a/src/test/ui/coherence/impl[t]-foreign[fundamental[t]_local]-for-foreign.stderr
+++ b/src/test/ui/coherence/impl[t]-foreign[fundamental[t]_local]-for-foreign.stderr
@@ -1,18 +1,20 @@
-error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
+error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
   --> $DIR/impl[t]-foreign[fundamental[t]_local]-for-foreign.rs:10:6
    |
 LL | impl<T> Remote2<Box<T>, Local> for u32 {
-   |      ^ type parameter `T` must be used as the type parameter for some local type
+   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
    |
-   = note: only traits defined in the current crate can be implemented for a type parameter
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local, and no uncovered type parameters appear before that first local type
+   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
 
-error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
+error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
   --> $DIR/impl[t]-foreign[fundamental[t]_local]-for-foreign.rs:14:10
    |
 LL | impl<'a, T> Remote2<&'a T, Local> for u32 {
-   |          ^ type parameter `T` must be used as the type parameter for some local type
+   |          ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
    |
-   = note: only traits defined in the current crate can be implemented for a type parameter
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local, and no uncovered type parameters appear before that first local type
+   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/coherence/impl[t]-foreign[local]-for-fundamental[t].rs b/src/test/ui/coherence/impl[t]-foreign[local]-for-fundamental[t].rs
index db88c330b39..7709bd9c89b 100644
--- a/src/test/ui/coherence/impl[t]-foreign[local]-for-fundamental[t].rs
+++ b/src/test/ui/coherence/impl[t]-foreign[local]-for-fundamental[t].rs
@@ -8,11 +8,11 @@ use std::rc::Rc;
 struct Local;
 
 impl<T> Remote1<Local> for Box<T> {
-    //~^ ERROR type parameter `T` must be used as the type parameter for some local type
+    //~^ ERROR type parameter `T` must be covered by another type
 }
 
 impl<T> Remote1<Local> for &T {
-    //~^ ERROR type parameter `T` must be used as the type parameter for some local type
+    //~^ ERROR type parameter `T` must be covered by another type
 }
 
 fn main() {}
diff --git a/src/test/ui/coherence/impl[t]-foreign[local]-for-fundamental[t].stderr b/src/test/ui/coherence/impl[t]-foreign[local]-for-fundamental[t].stderr
index b5fdb16c2f3..4d39186d494 100644
--- a/src/test/ui/coherence/impl[t]-foreign[local]-for-fundamental[t].stderr
+++ b/src/test/ui/coherence/impl[t]-foreign[local]-for-fundamental[t].stderr
@@ -1,18 +1,20 @@
-error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
+error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
   --> $DIR/impl[t]-foreign[local]-for-fundamental[t].rs:10:6
    |
 LL | impl<T> Remote1<Local> for Box<T> {
-   |      ^ type parameter `T` must be used as the type parameter for some local type
+   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
    |
-   = note: only traits defined in the current crate can be implemented for a type parameter
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local, and no uncovered type parameters appear before that first local type
+   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
 
-error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
+error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
   --> $DIR/impl[t]-foreign[local]-for-fundamental[t].rs:14:6
    |
 LL | impl<T> Remote1<Local> for &T {
-   |      ^ type parameter `T` must be used as the type parameter for some local type
+   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
    |
-   = note: only traits defined in the current crate can be implemented for a type parameter
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local, and no uncovered type parameters appear before that first local type
+   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/coherence/impl[t]-foreign[local]-for-t.rs b/src/test/ui/coherence/impl[t]-foreign[local]-for-t.rs
index dd4110d31e6..eed3a4b5c23 100644
--- a/src/test/ui/coherence/impl[t]-foreign[local]-for-t.rs
+++ b/src/test/ui/coherence/impl[t]-foreign[local]-for-t.rs
@@ -8,7 +8,7 @@ use std::rc::Rc;
 struct Local;
 
 impl<T> Remote1<Local> for T {
-    //~^ ERROR type parameter `T` must be used as the type parameter for some local type
+    //~^ ERROR type parameter `T` must be covered by another type
 }
 
 fn main() {}
diff --git a/src/test/ui/coherence/impl[t]-foreign[local]-for-t.stderr b/src/test/ui/coherence/impl[t]-foreign[local]-for-t.stderr
index 7b65212f62a..d74be4cec72 100644
--- a/src/test/ui/coherence/impl[t]-foreign[local]-for-t.stderr
+++ b/src/test/ui/coherence/impl[t]-foreign[local]-for-t.stderr
@@ -1,10 +1,11 @@
-error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
+error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
   --> $DIR/impl[t]-foreign[local]-for-t.rs:10:6
    |
 LL | impl<T> Remote1<Local> for T {
-   |      ^ type parameter `T` must be used as the type parameter for some local type
+   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`)
    |
-   = note: only traits defined in the current crate can be implemented for a type parameter
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local, and no uncovered type parameters appear before that first local type
+   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/coherence/impl[t]-foreign[t]-for-foreign.stderr b/src/test/ui/coherence/impl[t]-foreign[t]-for-foreign.stderr
index 2e5ae6a8eb3..b26feb4914c 100644
--- a/src/test/ui/coherence/impl[t]-foreign[t]-for-foreign.stderr
+++ b/src/test/ui/coherence/impl[t]-foreign[t]-for-foreign.stderr
@@ -4,6 +4,7 @@ error[E0210]: type parameter `T` must be used as the type parameter for some loc
 LL | impl<T> Remote1<T> for u32 {
    |      ^ type parameter `T` must be used as the type parameter for some local type
    |
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
    = note: only traits defined in the current crate can be implemented for a type parameter
 
 error: aborting due to previous error
diff --git a/src/test/ui/coherence/impl[t]-foreign[t]-for-fundamental.stderr b/src/test/ui/coherence/impl[t]-foreign[t]-for-fundamental.stderr
index 3976f06947f..5e8cc552c98 100644
--- a/src/test/ui/coherence/impl[t]-foreign[t]-for-fundamental.stderr
+++ b/src/test/ui/coherence/impl[t]-foreign[t]-for-fundamental.stderr
@@ -4,6 +4,7 @@ error[E0210]: type parameter `T` must be used as the type parameter for some loc
 LL | impl<T> Remote1<T> for Box<T> {
    |      ^ type parameter `T` must be used as the type parameter for some local type
    |
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
    = note: only traits defined in the current crate can be implemented for a type parameter
 
 error[E0210]: type parameter `B` must be used as the type parameter for some local type (e.g., `MyStruct<B>`)
@@ -12,6 +13,7 @@ error[E0210]: type parameter `B` must be used as the type parameter for some loc
 LL | impl<'a, A, B> Remote1<A> for &'a B {
    |             ^ type parameter `B` must be used as the type parameter for some local type
    |
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
    = note: only traits defined in the current crate can be implemented for a type parameter
 
 error: aborting due to 2 previous errors
diff --git a/src/test/ui/coherence/impl[t]-foreign[t]-for-t.stderr b/src/test/ui/coherence/impl[t]-foreign[t]-for-t.stderr
index 8d858b8abee..d3226d33bee 100644
--- a/src/test/ui/coherence/impl[t]-foreign[t]-for-t.stderr
+++ b/src/test/ui/coherence/impl[t]-foreign[t]-for-t.stderr
@@ -4,6 +4,7 @@ error[E0210]: type parameter `T` must be used as the type parameter for some loc
 LL | impl<T> Remote1<T> for T {
    |      ^ type parameter `T` must be used as the type parameter for some local type
    |
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
    = note: only traits defined in the current crate can be implemented for a type parameter
 
 error: aborting due to previous error
diff --git a/src/test/ui/error-codes/E0029-teach.stderr b/src/test/ui/error-codes/E0029-teach.stderr
index 998ef4bc1d7..ec146ca86f5 100644
--- a/src/test/ui/error-codes/E0029-teach.stderr
+++ b/src/test/ui/error-codes/E0029-teach.stderr
@@ -2,10 +2,11 @@ error[E0029]: only char and numeric types are allowed in range patterns
   --> $DIR/E0029-teach.rs:7:9
    |
 LL |         "hello" ..= "world" => {}
-   |         ^^^^^^^^^^^^^^^^^^^ ranges require char or numeric types
+   |         -------^^^^^-------
+   |         |           |
+   |         |           this is of type `&'static str` but it should be `char` or numeric
+   |         this is of type `&'static str` but it should be `char` or numeric
    |
-   = note: start type: &'static str
-   = note: end type: &'static str
    = note: In a match expression, only numbers and characters can be matched against a range. This is because the compiler checks that the range is non-empty at compile-time, and is unable to evaluate arbitrary comparison functions. If you want to capture values of an orderable type between two end-points, you can use a guard.
 
 error: aborting due to previous error
diff --git a/src/test/ui/error-codes/E0029.stderr b/src/test/ui/error-codes/E0029.stderr
index 209d219191c..e54722ae7b9 100644
--- a/src/test/ui/error-codes/E0029.stderr
+++ b/src/test/ui/error-codes/E0029.stderr
@@ -2,10 +2,10 @@ error[E0029]: only char and numeric types are allowed in range patterns
   --> $DIR/E0029.rs:5:9
    |
 LL |         "hello" ..= "world" => {}
-   |         ^^^^^^^^^^^^^^^^^^^ ranges require char or numeric types
-   |
-   = note: start type: &'static str
-   = note: end type: &'static str
+   |         -------^^^^^-------
+   |         |           |
+   |         |           this is of type `&'static str` but it should be `char` or numeric
+   |         this is of type `&'static str` but it should be `char` or numeric
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/error-codes/e0119/issue-28981.stderr b/src/test/ui/error-codes/e0119/issue-28981.stderr
index ec8e8144d42..2a78cc8b2db 100644
--- a/src/test/ui/error-codes/e0119/issue-28981.stderr
+++ b/src/test/ui/error-codes/e0119/issue-28981.stderr
@@ -14,6 +14,7 @@ error[E0210]: type parameter `Foo` must be used as the type parameter for some l
 LL | impl<Foo> Deref for Foo { }
    |      ^^^ type parameter `Foo` must be used as the type parameter for some local type
    |
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
    = note: only traits defined in the current crate can be implemented for a type parameter
 
 error: aborting due to 2 previous errors
diff --git a/src/test/ui/issues/issue-41974.stderr b/src/test/ui/issues/issue-41974.stderr
index 12d4da71599..9f164822dea 100644
--- a/src/test/ui/issues/issue-41974.stderr
+++ b/src/test/ui/issues/issue-41974.stderr
@@ -21,6 +21,7 @@ error[E0210]: type parameter `T` must be used as the type parameter for some loc
 LL | impl<T> Drop for T where T: A {
    |      ^ type parameter `T` must be used as the type parameter for some local type
    |
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
    = note: only traits defined in the current crate can be implemented for a type parameter
 
 error: aborting due to 3 previous errors
diff --git a/src/test/ui/match/match-range-fail.rs b/src/test/ui/match/match-range-fail.rs
index 8a3678577b4..252d4cbf162 100644
--- a/src/test/ui/match/match-range-fail.rs
+++ b/src/test/ui/match/match-range-fail.rs
@@ -3,15 +3,16 @@ fn main() {
         "bar" ..= "foo" => { }
     };
     //~^^ ERROR only char and numeric types are allowed in range
-    //~| start type: &'static str
-    //~| end type: &'static str
 
     match "wow" {
         10 ..= "what" => ()
     };
     //~^^ ERROR only char and numeric types are allowed in range
-    //~| start type: {integer}
-    //~| end type: &'static str
+
+    match "wow" {
+        true ..= "what" => {}
+    };
+    //~^^ ERROR only char and numeric types are allowed in range
 
     match 5 {
         'c' ..= 100 => { }
diff --git a/src/test/ui/match/match-range-fail.stderr b/src/test/ui/match/match-range-fail.stderr
index 3fd2a499e2b..25fa9c2f618 100644
--- a/src/test/ui/match/match-range-fail.stderr
+++ b/src/test/ui/match/match-range-fail.stderr
@@ -2,22 +2,30 @@ error[E0029]: only char and numeric types are allowed in range patterns
   --> $DIR/match-range-fail.rs:3:9
    |
 LL |         "bar" ..= "foo" => { }
-   |         ^^^^^^^^^^^^^^^ ranges require char or numeric types
-   |
-   = note: start type: &'static str
-   = note: end type: &'static str
+   |         -----^^^^^-----
+   |         |         |
+   |         |         this is of type `&'static str` but it should be `char` or numeric
+   |         this is of type `&'static str` but it should be `char` or numeric
 
 error[E0029]: only char and numeric types are allowed in range patterns
-  --> $DIR/match-range-fail.rs:10:16
+  --> $DIR/match-range-fail.rs:8:16
    |
 LL |         10 ..= "what" => ()
-   |                ^^^^^^ ranges require char or numeric types
+   |         --     ^^^^^^ this is of type `&'static str` but it should be `char` or numeric
+   |         |
+   |         this is of type `{integer}`
+
+error[E0029]: only char and numeric types are allowed in range patterns
+  --> $DIR/match-range-fail.rs:13:9
    |
-   = note: start type: {integer}
-   = note: end type: &'static str
+LL |         true ..= "what" => {}
+   |         ----^^^^^------
+   |         |        |
+   |         |        this is of type `&'static str` but it should be `char` or numeric
+   |         this is of type `bool` but it should be `char` or numeric
 
 error[E0308]: mismatched types
-  --> $DIR/match-range-fail.rs:17:9
+  --> $DIR/match-range-fail.rs:18:9
    |
 LL |         'c' ..= 100 => { }
    |         ^^^^^^^^^^^ expected integer, found char
@@ -25,7 +33,7 @@ LL |         'c' ..= 100 => { }
    = note: expected type `{integer}`
               found type `char`
 
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
 
 Some errors have detailed explanations: E0029, E0308.
 For more information about an error, try `rustc --explain E0029`.
diff --git a/src/test/ui/orphan-check-diagnostics.stderr b/src/test/ui/orphan-check-diagnostics.stderr
index cb21b26bba7..c84d401898c 100644
--- a/src/test/ui/orphan-check-diagnostics.stderr
+++ b/src/test/ui/orphan-check-diagnostics.stderr
@@ -4,6 +4,7 @@ error[E0210]: type parameter `T` must be used as the type parameter for some loc
 LL | impl<T> RemoteTrait for T where T: LocalTrait {}
    |      ^ type parameter `T` must be used as the type parameter for some local type
    |
+   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
    = note: only traits defined in the current crate can be implemented for a type parameter
 
 error: aborting due to previous error
diff --git a/src/test/ui/parser/issue-66357-unexpected-unreachable.rs b/src/test/ui/parser/issue-66357-unexpected-unreachable.rs
new file mode 100644
index 00000000000..1730adfa914
--- /dev/null
+++ b/src/test/ui/parser/issue-66357-unexpected-unreachable.rs
@@ -0,0 +1,14 @@
+// The problem in #66357 was that the call trace:
+//
+// - parse_fn_block_decl
+// - expect_or
+// - unexpected
+// - expect_one_of
+// - expected_one_of_not_found
+// - recover_closing_delimiter
+//
+// ended up bubbling up `Ok(true)` to `unexpected` which then used `unreachable!()`.
+
+fn f() { |[](* }
+//~^ ERROR expected one of `,` or `:`, found `(`
+//~| ERROR expected one of `)`, `-`, `_`, `box`, `mut`, `ref`, `|`, identifier, or path, found `*`
diff --git a/src/test/ui/parser/issue-66357-unexpected-unreachable.stderr b/src/test/ui/parser/issue-66357-unexpected-unreachable.stderr
new file mode 100644
index 00000000000..00d84e2afe3
--- /dev/null
+++ b/src/test/ui/parser/issue-66357-unexpected-unreachable.stderr
@@ -0,0 +1,16 @@
+error: expected one of `,` or `:`, found `(`
+  --> $DIR/issue-66357-unexpected-unreachable.rs:12:13
+   |
+LL | fn f() { |[](* }
+   |             ^ expected one of `,` or `:`
+
+error: expected one of `)`, `-`, `_`, `box`, `mut`, `ref`, `|`, identifier, or path, found `*`
+  --> $DIR/issue-66357-unexpected-unreachable.rs:12:14
+   |
+LL | fn f() { |[](* }
+   |             -^ help: `)` may belong here
+   |             |
+   |             unclosed delimiter
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/parser/macros-no-semicolon-items.stderr b/src/test/ui/parser/macros-no-semicolon-items.stderr
index 980ceeed8c6..f9019b78c8d 100644
--- a/src/test/ui/parser/macros-no-semicolon-items.stderr
+++ b/src/test/ui/parser/macros-no-semicolon-items.stderr
@@ -6,8 +6,8 @@ LL | macro_rules! foo()
    |
 help: change the delimiters to curly braces
    |
-LL | macro_rules! foo {}
-   |                  ^^
+LL | macro_rules! foo{}
+   |                 ^^
 help: add a semicolon
    |
 LL | macro_rules! foo();
@@ -26,7 +26,7 @@ LL | | )
    |
 help: change the delimiters to curly braces
    |
-LL | bar! {
+LL | bar!{
 LL |     blah
 LL |     blah
 LL |     blah
diff --git a/src/test/ui/parser/mbe_missing_right_paren.rs b/src/test/ui/parser/mbe_missing_right_paren.rs
new file mode 100644
index 00000000000..689176b3eb7
--- /dev/null
+++ b/src/test/ui/parser/mbe_missing_right_paren.rs
@@ -0,0 +1,3 @@
+// ignore-tidy-trailing-newlines
+// error-pattern: aborting due to 3 previous errors
+macro_rules! abc(ؼ
\ No newline at end of file
diff --git a/src/test/ui/parser/mbe_missing_right_paren.stderr b/src/test/ui/parser/mbe_missing_right_paren.stderr
new file mode 100644
index 00000000000..4504fc0eb00
--- /dev/null
+++ b/src/test/ui/parser/mbe_missing_right_paren.stderr
@@ -0,0 +1,31 @@
+error: this file contains an un-closed delimiter
+  --> $DIR/mbe_missing_right_paren.rs:3:19
+   |
+LL | macro_rules! abc(ؼ
+   |                 - ^
+   |                 |
+   |                 un-closed delimiter
+
+error: macros that expand to items must be delimited with braces or followed by a semicolon
+  --> $DIR/mbe_missing_right_paren.rs:3:17
+   |
+LL | macro_rules! abc(ؼ
+   |                 ^^
+   |
+help: change the delimiters to curly braces
+   |
+LL | macro_rules! abc{ؼ}
+   |                 ^ ^
+help: add a semicolon
+   |
+LL | macro_rules! abc(ؼ;
+   |                   ^
+
+error: unexpected end of macro invocation
+  --> $DIR/mbe_missing_right_paren.rs:3:1
+   |
+LL | macro_rules! abc(ؼ
+   | ^^^^^^^^^^^^^^^^^^ missing tokens in macro arguments
+
+error: aborting due to 3 previous errors
+
diff --git a/src/test/ui/parser/recover-range-pats.stderr b/src/test/ui/parser/recover-range-pats.stderr
index 89ec059cb82..160ab18e34a 100644
--- a/src/test/ui/parser/recover-range-pats.stderr
+++ b/src/test/ui/parser/recover-range-pats.stderr
@@ -401,19 +401,17 @@ error[E0029]: only char and numeric types are allowed in range patterns
   --> $DIR/recover-range-pats.rs:19:12
    |
 LL |     if let true..Y = 0 {}
-   |            ^^^^ ranges require char or numeric types
-   |
-   = note: start type: bool
-   = note: end type: u8
+   |            ^^^^  - this is of type `u8`
+   |            |
+   |            this is of type `bool` but it should be `char` or numeric
 
 error[E0029]: only char and numeric types are allowed in range patterns
   --> $DIR/recover-range-pats.rs:20:15
    |
 LL |     if let X..true = 0 {}
-   |               ^^^^ ranges require char or numeric types
-   |
-   = note: start type: u8
-   = note: end type: bool
+   |            -  ^^^^ this is of type `bool` but it should be `char` or numeric
+   |            |
+   |            this is of type `u8`
 
 error[E0308]: mismatched types
   --> $DIR/recover-range-pats.rs:21:12
@@ -437,19 +435,17 @@ error[E0029]: only char and numeric types are allowed in range patterns
   --> $DIR/recover-range-pats.rs:32:12
    |
 LL |     if let true..=Y = 0 {}
-   |            ^^^^ ranges require char or numeric types
-   |
-   = note: start type: bool
-   = note: end type: u8
+   |            ^^^^   - this is of type `u8`
+   |            |
+   |            this is of type `bool` but it should be `char` or numeric
 
 error[E0029]: only char and numeric types are allowed in range patterns
   --> $DIR/recover-range-pats.rs:33:16
    |
 LL |     if let X..=true = 0 {}
-   |                ^^^^ ranges require char or numeric types
-   |
-   = note: start type: u8
-   = note: end type: bool
+   |            -   ^^^^ this is of type `bool` but it should be `char` or numeric
+   |            |
+   |            this is of type `u8`
 
 error[E0308]: mismatched types
   --> $DIR/recover-range-pats.rs:34:12
@@ -473,19 +469,17 @@ error[E0029]: only char and numeric types are allowed in range patterns
   --> $DIR/recover-range-pats.rs:45:12
    |
 LL |     if let true...Y = 0 {}
-   |            ^^^^ ranges require char or numeric types
-   |
-   = note: start type: bool
-   = note: end type: u8
+   |            ^^^^   - this is of type `u8`
+   |            |
+   |            this is of type `bool` but it should be `char` or numeric
 
 error[E0029]: only char and numeric types are allowed in range patterns
   --> $DIR/recover-range-pats.rs:47:16
    |
 LL |     if let X...true = 0 {}
-   |                ^^^^ ranges require char or numeric types
-   |
-   = note: start type: u8
-   = note: end type: bool
+   |            -   ^^^^ this is of type `bool` but it should be `char` or numeric
+   |            |
+   |            this is of type `u8`
 
 error[E0308]: mismatched types
   --> $DIR/recover-range-pats.rs:49:12
@@ -509,10 +503,7 @@ error[E0029]: only char and numeric types are allowed in range patterns
   --> $DIR/recover-range-pats.rs:60:12
    |
 LL |     if let true.. = 0 {}
-   |            ^^^^ ranges require char or numeric types
-   |
-   = note: start type: bool
-   = note: end type: [type error]
+   |            ^^^^ this is of type `bool` but it should be `char` or numeric
 
 error[E0308]: mismatched types
   --> $DIR/recover-range-pats.rs:62:12
@@ -527,10 +518,7 @@ error[E0029]: only char and numeric types are allowed in range patterns
   --> $DIR/recover-range-pats.rs:70:12
    |
 LL |     if let true..= = 0 {}
-   |            ^^^^ ranges require char or numeric types
-   |
-   = note: start type: bool
-   = note: end type: [type error]
+   |            ^^^^ this is of type `bool` but it should be `char` or numeric
 
 error[E0308]: mismatched types
   --> $DIR/recover-range-pats.rs:72:12
@@ -545,10 +533,7 @@ error[E0029]: only char and numeric types are allowed in range patterns
   --> $DIR/recover-range-pats.rs:82:12
    |
 LL |     if let true... = 0 {}
-   |            ^^^^ ranges require char or numeric types
-   |
-   = note: start type: bool
-   = note: end type: [type error]
+   |            ^^^^ this is of type `bool` but it should be `char` or numeric
 
 error[E0308]: mismatched types
   --> $DIR/recover-range-pats.rs:85:12
@@ -563,10 +548,7 @@ error[E0029]: only char and numeric types are allowed in range patterns
   --> $DIR/recover-range-pats.rs:94:14
    |
 LL |     if let ..true = 0 {}
-   |              ^^^^ ranges require char or numeric types
-   |
-   = note: start type: [type error]
-   = note: end type: bool
+   |              ^^^^ this is of type `bool` but it should be `char` or numeric
 
 error[E0308]: mismatched types
   --> $DIR/recover-range-pats.rs:96:12
@@ -581,10 +563,7 @@ error[E0029]: only char and numeric types are allowed in range patterns
   --> $DIR/recover-range-pats.rs:104:15
    |
 LL |     if let ..=true = 0 {}
-   |               ^^^^ ranges require char or numeric types
-   |
-   = note: start type: [type error]
-   = note: end type: bool
+   |               ^^^^ this is of type `bool` but it should be `char` or numeric
 
 error[E0308]: mismatched types
   --> $DIR/recover-range-pats.rs:106:12
@@ -599,10 +578,7 @@ error[E0029]: only char and numeric types are allowed in range patterns
   --> $DIR/recover-range-pats.rs:116:15
    |
 LL |     if let ...true = 0 {}
-   |               ^^^^ ranges require char or numeric types
-   |
-   = note: start type: [type error]
-   = note: end type: bool
+   |               ^^^^ this is of type `bool` but it should be `char` or numeric
 
 error[E0308]: mismatched types
   --> $DIR/recover-range-pats.rs:119:12
diff --git a/src/test/ui/pattern/patkind-litrange-no-expr.stderr b/src/test/ui/pattern/patkind-litrange-no-expr.stderr
index 7474d326793..78768d282e7 100644
--- a/src/test/ui/pattern/patkind-litrange-no-expr.stderr
+++ b/src/test/ui/pattern/patkind-litrange-no-expr.stderr
@@ -7,11 +7,11 @@ LL |     Arith = 1 + 1,
 error[E0029]: only char and numeric types are allowed in range patterns
   --> $DIR/patkind-litrange-no-expr.rs:20:13
    |
+LL |                 $( $value ..= 42 => Some($name::$variant), )* // PatKind::Range
+   |                               -- this is of type `{integer}`
+...
 LL |     Arith = 1 + 1,
-   |             ^^^^^ ranges require char or numeric types
-   |
-   = note: start type: {integer}
-   = note: end type: {integer}
+   |             ^^^^^ this is of type `_` but it should be `char` or numeric
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/qualified/qualified-path-params.stderr b/src/test/ui/qualified/qualified-path-params.stderr
index 92792f2e86a..7a74a37021b 100644
--- a/src/test/ui/qualified/qualified-path-params.stderr
+++ b/src/test/ui/qualified/qualified-path-params.stderr
@@ -8,10 +8,9 @@ error[E0029]: only char and numeric types are allowed in range patterns
   --> $DIR/qualified-path-params.rs:22:15
    |
 LL |         0 ..= <S as Tr>::A::f::<u8> => {}
-   |               ^^^^^^^^^^^^^^^^^^^^^ ranges require char or numeric types
-   |
-   = note: start type: {integer}
-   = note: end type: fn() {S::f::<u8>}
+   |         -     ^^^^^^^^^^^^^^^^^^^^^ this is of type `fn() {S::f::<u8>}` but it should be `char` or numeric
+   |         |
+   |         this is of type `{integer}`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs
index b115539b4af..15f8abd75d5 100644
--- a/src/tools/compiletest/src/main.rs
+++ b/src/tools/compiletest/src/main.rs
@@ -574,22 +574,59 @@ pub fn test_opts(config: &Config) -> test::TestOpts {
 
 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
     debug!("making tests from {:?}", config.src_base.display());
+    let inputs = common_inputs_stamp(config);
     let mut tests = Vec::new();
     collect_tests_from_dir(
         config,
         &config.src_base,
         &config.src_base,
         &PathBuf::new(),
+        &inputs,
         &mut tests,
-    ).unwrap();
+    ).expect(&format!("Could not read tests from {}", config.src_base.display()));
     tests
 }
 
+/// Returns a stamp constructed from input files common to all test cases.
+fn common_inputs_stamp(config: &Config) -> Stamp {
+    let rust_src_dir = config
+        .find_rust_src_root()
+        .expect("Could not find Rust source root");
+
+    let mut stamp = Stamp::from_path(&config.rustc_path);
+
+    // Relevant pretty printer files
+    let pretty_printer_files = [
+        "src/etc/debugger_pretty_printers_common.py",
+        "src/etc/gdb_load_rust_pretty_printers.py",
+        "src/etc/gdb_rust_pretty_printing.py",
+        "src/etc/lldb_batchmode.py",
+        "src/etc/lldb_rust_formatters.py",
+    ];
+    for file in &pretty_printer_files {
+        let path = rust_src_dir.join(file);
+        stamp.add_path(&path);
+    }
+
+    stamp.add_dir(&config.run_lib_path);
+
+    if let Some(ref rustdoc_path) = config.rustdoc_path {
+        stamp.add_path(&rustdoc_path);
+        stamp.add_path(&rust_src_dir.join("src/etc/htmldocck.py"));
+    }
+
+    // Compiletest itself.
+    stamp.add_dir(&rust_src_dir.join("src/tools/compiletest/"));
+
+    stamp
+}
+
 fn collect_tests_from_dir(
     config: &Config,
     base: &Path,
     dir: &Path,
     relative_dir_path: &Path,
+    inputs: &Stamp,
     tests: &mut Vec<test::TestDescAndFn>,
 ) -> io::Result<()> {
     // Ignore directories that contain a file named `compiletest-ignore-dir`.
@@ -602,7 +639,7 @@ fn collect_tests_from_dir(
             file: dir.to_path_buf(),
             relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
         };
-        tests.extend(make_test(config, &paths));
+        tests.extend(make_test(config, &paths, inputs));
         return Ok(());
     }
 
@@ -627,12 +664,14 @@ fn collect_tests_from_dir(
                 file: file_path,
                 relative_dir: relative_dir_path.to_path_buf(),
             };
-            tests.extend(make_test(config, &paths))
+            tests.extend(make_test(config, &paths, inputs))
         } else if file_path.is_dir() {
             let relative_file_path = relative_dir_path.join(file.file_name());
             if &file_name != "auxiliary" {
                 debug!("found directory: {:?}", file_path.display());
-                collect_tests_from_dir(config, base, &file_path, &relative_file_path, tests)?;
+                collect_tests_from_dir(
+                    config, base, &file_path, &relative_file_path,
+                    inputs, tests)?;
             }
         } else {
             debug!("found other file/directory: {:?}", file_path.display());
@@ -655,7 +694,7 @@ pub fn is_test(file_name: &OsString) -> bool {
     !invalid_prefixes.iter().any(|p| file_name.starts_with(p))
 }
 
-pub fn make_test(config: &Config, testpaths: &TestPaths) -> Vec<test::TestDescAndFn> {
+fn make_test(config: &Config, testpaths: &TestPaths, inputs: &Stamp) -> Vec<test::TestDescAndFn> {
     let early_props = if config.mode == Mode::RunMake {
         // Allow `ignore` directives to be in the Makefile.
         EarlyProps::from_file(config, &testpaths.file.join("Makefile"))
@@ -685,19 +724,21 @@ pub fn make_test(config: &Config, testpaths: &TestPaths) -> Vec<test::TestDescAn
     revisions
         .into_iter()
         .map(|revision| {
-            // Debugging emscripten code doesn't make sense today
             let ignore = early_props.ignore == Ignore::Ignore
-                || !up_to_date(
-                    config,
-                    testpaths,
-                    &early_props,
-                    revision.map(|s| s.as_str()),
-                )
+                // Debugging emscripten code doesn't make sense today
                 || ((config.mode == DebugInfoGdbLldb || config.mode == DebugInfoCdb ||
                      config.mode == DebugInfoGdb || config.mode == DebugInfoLldb)
                     && config.target.contains("emscripten"))
                 || (config.mode == DebugInfoGdb && !early_props.ignore.can_run_gdb())
-                || (config.mode == DebugInfoLldb && !early_props.ignore.can_run_lldb());
+                || (config.mode == DebugInfoLldb && !early_props.ignore.can_run_lldb())
+                // Ignore tests that already run and are up to date with respect to inputs.
+                || is_up_to_date(
+                    config,
+                    testpaths,
+                    &early_props,
+                    revision.map(|s| s.as_str()),
+                    inputs,
+                );
             test::TestDescAndFn {
                 desc: test::TestDesc {
                     name: make_test_name(config, testpaths, revision),
@@ -716,98 +757,75 @@ fn stamp(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> Path
     output_base_dir(config, testpaths, revision).join("stamp")
 }
 
-fn up_to_date(
+fn is_up_to_date(
     config: &Config,
     testpaths: &TestPaths,
     props: &EarlyProps,
     revision: Option<&str>,
+    inputs: &Stamp,
 ) -> bool {
     let stamp_name = stamp(config, testpaths, revision);
     // Check hash.
     let contents = match fs::read_to_string(&stamp_name) {
         Ok(f) => f,
         Err(ref e) if e.kind() == ErrorKind::InvalidData => panic!("Can't read stamp contents"),
-        Err(_) => return true,
+        Err(_) => return false,
     };
     let expected_hash = runtest::compute_stamp_hash(config);
     if contents != expected_hash {
-        return true;
+        return false;
     }
 
     // Check timestamps.
-    let rust_src_dir = config
-        .find_rust_src_root()
-        .expect("Could not find Rust source root");
-    let stamp = Stamp::from_path(&stamp_name);
-    let mut inputs = vec![Stamp::from_path(&testpaths.file), Stamp::from_path(&config.rustc_path)];
-    inputs.extend(
-        props
-            .aux
-            .iter()
-            .map(|aux| {
-                Stamp::from_path(&testpaths.file.parent().unwrap().join("auxiliary").join(aux))
-            }),
-    );
-    // Relevant pretty printer files
-    let pretty_printer_files = [
-        "src/etc/debugger_pretty_printers_common.py",
-        "src/etc/gdb_load_rust_pretty_printers.py",
-        "src/etc/gdb_rust_pretty_printing.py",
-        "src/etc/lldb_batchmode.py",
-        "src/etc/lldb_rust_formatters.py",
-    ];
-    inputs.extend(pretty_printer_files.iter().map(|pretty_printer_file| {
-        Stamp::from_path(&rust_src_dir.join(pretty_printer_file))
-    }));
-    inputs.extend(Stamp::from_dir(&config.run_lib_path));
-    if let Some(ref rustdoc_path) = config.rustdoc_path {
-        inputs.push(Stamp::from_path(&rustdoc_path));
-        inputs.push(Stamp::from_path(&rust_src_dir.join("src/etc/htmldocck.py")));
+    let mut inputs = inputs.clone();
+    inputs.add_path(&testpaths.file);
+
+    for aux in &props.aux {
+        let path = testpaths.file.parent()
+            .unwrap()
+            .join("auxiliary")
+            .join(aux);
+        inputs.add_path(&path);
     }
 
     // UI test files.
-    inputs.extend(UI_EXTENSIONS.iter().map(|extension| {
+    for extension in UI_EXTENSIONS {
         let path = &expected_output_path(testpaths, revision, &config.compare_mode, extension);
-        Stamp::from_path(path)
-    }));
-
-    // Compiletest itself.
-    inputs.extend(Stamp::from_dir(&rust_src_dir.join("src/tools/compiletest/")));
+        inputs.add_path(path);
+    }
 
-    inputs.iter().any(|input| input > &stamp)
+    inputs < Stamp::from_path(&stamp_name)
 }
 
-#[derive(Debug, PartialEq, PartialOrd, Ord, Eq)]
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
 struct Stamp {
     time: SystemTime,
-    file: PathBuf,
 }
 
 impl Stamp {
-    fn from_path(p: &Path) -> Self {
-        let time = fs::metadata(p)
+    fn from_path(path: &Path) -> Self {
+        let mut stamp = Stamp { time: SystemTime::UNIX_EPOCH };
+        stamp.add_path(path);
+        stamp
+    }
+
+    fn add_path(&mut self, path: &Path) {
+        let modified = fs::metadata(path)
             .and_then(|metadata| metadata.modified())
             .unwrap_or(SystemTime::UNIX_EPOCH);
-
-        Stamp {
-            time,
-            file: p.into(),
-        }
+        self.time = self.time.max(modified);
     }
 
-    fn from_dir(path: &Path) -> impl Iterator<Item = Stamp> {
-        WalkDir::new(path)
-            .into_iter()
-            .map(|entry| entry.unwrap())
-            .filter(|entry| entry.file_type().is_file())
-            .map(|entry| {
-                let time = (|| -> io::Result<_> { entry.metadata()?.modified() })();
-
-                Stamp {
-                    time: time.unwrap_or(SystemTime::UNIX_EPOCH),
-                    file: entry.path().into(),
-                }
-            })
+    fn add_dir(&mut self, path: &Path) {
+        for entry in WalkDir::new(path) {
+            let entry = entry.unwrap();
+            if entry.file_type().is_file() {
+                let modified = entry.metadata().ok()
+                    .and_then(|metadata| metadata.modified().ok())
+                    .unwrap_or(SystemTime::UNIX_EPOCH);
+                self.time = self.time.max(modified);
+            }
+        }
     }
 }