about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-01-08 09:00:31 +0000
committerbors <bors@rust-lang.org>2023-01-08 09:00:31 +0000
commit8ea62f6c30c94778d9146ddc26c8520ed2b50a58 (patch)
tree3d04b0d8d3e598939b6858ab2e51d06724875278
parent2afe58571e53d48a1fc2354271abe5aff60c5c44 (diff)
parent789ebdca4797247351ec8770d26f8e23214f1f25 (diff)
Auto merge of #106588 - JohnTitor:rollup-4z80tjx, r=JohnTitor
Rollup of 8 pull requests

Successful merges:

 - #103104 (Stabilize `main_separator_str`)
 - #106410 (Suggest `mut self: &mut Self` for `?Sized` impls)
 - #106457 (Adjust comments about pre-push.sh hook)
 - #106546 (jsondoclint: Check local items in `paths` are also in `index`.)
 - #106557 (Add some UI tests and reword error-code docs)
 - #106562 (Clarify examples for `VecDeque::get/get_mut`)
 - #106580 (remove unreachable error code `E0313`)
 - #106581 (Do not emit wrong E0308 suggestion for closure mismatch)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs27
-rw-r--r--compiler/rustc_error_codes/src/error_codes.rs3
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0015.md23
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs5
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs1
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note.rs37
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs4
-rw-r--r--library/alloc/src/collections/vec_deque/mod.rs4
-rw-r--r--library/std/src/path.rs2
-rw-r--r--src/bootstrap/setup.rs4
-rwxr-xr-xsrc/etc/pre-push.sh2
-rw-r--r--src/test/ui/borrowck/issue-93078.rs15
-rw-r--r--src/test/ui/borrowck/issue-93078.stderr12
-rw-r--r--src/test/ui/error-codes/E0013.rs4
-rw-r--r--src/test/ui/error-codes/E0013.stderr11
-rw-r--r--src/test/ui/error-codes/E0015.rs8
-rw-r--r--src/test/ui/error-codes/E0015.stderr11
-rw-r--r--src/test/ui/type/closure-with-wrong-borrows.rs10
-rw-r--r--src/test/ui/type/closure-with-wrong-borrows.stderr19
-rw-r--r--src/tools/jsondoclint/src/validator.rs29
-rw-r--r--src/tools/jsondoclint/src/validator/tests.rs100
-rw-r--r--src/tools/tidy/src/error_codes.rs31
22 files changed, 268 insertions, 94 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index c022fb55a16..09ef84f7cbb 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -344,20 +344,25 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     } else {
                         err.span_help(source_info.span, "try removing `&mut` here");
                     }
-                } else if decl.mutability == Mutability::Not
-                    && !matches!(
+                } else if decl.mutability == Mutability::Not {
+                    if matches!(
                         decl.local_info,
                         Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf(
                             hir::ImplicitSelfKind::MutRef
-                        ))))
-                    )
-                {
-                    err.span_suggestion_verbose(
-                        decl.source_info.span.shrink_to_lo(),
-                        "consider making the binding mutable",
-                        "mut ",
-                        Applicability::MachineApplicable,
-                    );
+                        ),)))
+                    ) {
+                        err.note(
+                            "as `Self` may be unsized, this call attempts to take `&mut &mut self`",
+                        );
+                        err.note("however, `&mut self` expands to `self: &mut Self`, therefore `self` cannot be borrowed mutably");
+                    } else {
+                        err.span_suggestion_verbose(
+                            decl.source_info.span.shrink_to_lo(),
+                            "consider making the binding mutable",
+                            "mut ",
+                            Applicability::MachineApplicable,
+                        );
+                    };
                 }
             }
 
diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs
index 3fba2cf5749..76d5da19399 100644
--- a/compiler/rustc_error_codes/src/error_codes.rs
+++ b/compiler/rustc_error_codes/src/error_codes.rs
@@ -579,8 +579,7 @@ E0791: include_str!("./error_codes/E0791.md"),
 //  E0300, // unexpanded macro
 //  E0304, // expected signed integer constant
 //  E0305, // expected constant
-    E0313, // lifetime of borrowed pointer outlives lifetime of captured
-           // variable
+//  E0313, // removed: found unreachable
 //  E0314, // closure outlives stack frame
 //  E0315, // cannot invoke closure outside of its lifetime
 //  E0319, // trait impls for defaulted traits allowed just for structs/enums
diff --git a/compiler/rustc_error_codes/src/error_codes/E0015.md b/compiler/rustc_error_codes/src/error_codes/E0015.md
index 021a0219d13..ac78f66adad 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0015.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0015.md
@@ -1,5 +1,4 @@
-A constant item was initialized with something that is not a constant
-expression.
+A non-`const` function was called in a `const` context.
 
 Erroneous code example:
 
@@ -8,26 +7,20 @@ fn create_some() -> Option<u8> {
     Some(1)
 }
 
-const FOO: Option<u8> = create_some(); // error!
+// error: cannot call non-const fn `create_some` in constants
+const FOO: Option<u8> = create_some();
 ```
 
-The only functions that can be called in static or constant expressions are
-`const` functions, and struct/enum constructors.
+All functions used in a `const` context (constant or static expression) must
+be marked `const`.
 
 To fix this error, you can declare `create_some` as a constant function:
 
 ```
-const fn create_some() -> Option<u8> { // declared as a const function
+// declared as a `const` function:
+const fn create_some() -> Option<u8> {
     Some(1)
 }
 
-const FOO: Option<u8> = create_some(); // ok!
-
-// These are also working:
-struct Bar {
-    x: u8,
-}
-
-const OTHER_FOO: Option<u8> = Some(1);
-const BAR: Bar = Bar {x: 1};
+const FOO: Option<u8> = create_some(); // no error!
 ```
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index 0f191c21a0a..f15cf5e3ef9 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -1379,7 +1379,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         }
                     }
                     // If we've reached our target type with just removing `&`, then just print now.
-                    if steps == 0 {
+                    if steps == 0 && !remove.trim().is_empty() {
                         return Some((
                             prefix_span,
                             format!("consider removing the `{}`", remove.trim()),
@@ -1438,6 +1438,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         } else {
                             (prefix_span, format!("{}{}", prefix, "*".repeat(steps)))
                         };
+                        if suggestion.trim().is_empty() {
+                            return None;
+                        }
 
                         return Some((
                             span,
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 0644c7ada10..b02e7d73c51 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -1,4 +1,3 @@
-// ignore-tidy-filelength
 //! Error Reporting Code for the inference engine
 //!
 //! Because of the way inference, and in particular region inference,
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
index d91ef882bc4..7bb79d7bda8 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -25,16 +25,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             infer::Reborrow(span) => {
                 RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diagnostic(err)
             }
-            infer::ReborrowUpvar(span, ref upvar_id) => {
-                let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
-                RegionOriginNote::WithName {
-                    span,
-                    msg: fluent::infer_reborrow,
-                    name: &var_name.to_string(),
-                    continues: false,
-                }
-                .add_to_diagnostic(err);
-            }
             infer::RelateObjectBound(span) => {
                 RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound }
                     .add_to_diagnostic(err);
@@ -162,33 +152,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 );
                 err
             }
-            infer::ReborrowUpvar(span, ref upvar_id) => {
-                let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
-                let mut err = struct_span_err!(
-                    self.tcx.sess,
-                    span,
-                    E0313,
-                    "lifetime of borrowed pointer outlives lifetime of captured variable `{}`...",
-                    var_name
-                );
-                note_and_explain_region(
-                    self.tcx,
-                    &mut err,
-                    "...the borrowed pointer is valid for ",
-                    sub,
-                    "...",
-                    None,
-                );
-                note_and_explain_region(
-                    self.tcx,
-                    &mut err,
-                    &format!("...but `{}` is only valid for ", var_name),
-                    sup,
-                    "",
-                    None,
-                );
-                err
-            }
             infer::RelateObjectBound(span) => {
                 let mut err = struct_span_err!(
                     self.tcx.sess,
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index b17a465eb38..bc8bbd4be6a 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -409,9 +409,6 @@ pub enum SubregionOrigin<'tcx> {
     /// Creating a pointer `b` to contents of another reference
     Reborrow(Span),
 
-    /// Creating a pointer `b` to contents of an upvar
-    ReborrowUpvar(Span, ty::UpvarId),
-
     /// Data with type `Ty<'tcx>` was borrowed
     DataBorrowed(Ty<'tcx>, Span),
 
@@ -1954,7 +1951,6 @@ impl<'tcx> SubregionOrigin<'tcx> {
             RelateParamBound(a, ..) => a,
             RelateRegionParamBound(a) => a,
             Reborrow(a) => a,
-            ReborrowUpvar(a, _) => a,
             DataBorrowed(_, a) => a,
             ReferenceOutlivesReferent(_, a) => a,
             CompareImplItemObligation { span, .. } => span,
diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs
index 0017baa66ab..451e4936bc5 100644
--- a/library/alloc/src/collections/vec_deque/mod.rs
+++ b/library/alloc/src/collections/vec_deque/mod.rs
@@ -636,6 +636,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// buf.push_back(3);
     /// buf.push_back(4);
     /// buf.push_back(5);
+    /// buf.push_back(6);
     /// assert_eq!(buf.get(1), Some(&4));
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -661,10 +662,11 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// buf.push_back(3);
     /// buf.push_back(4);
     /// buf.push_back(5);
+    /// buf.push_back(6);
+    /// assert_eq!(buf[1], 4);
     /// if let Some(elem) = buf.get_mut(1) {
     ///     *elem = 7;
     /// }
-    ///
     /// assert_eq!(buf[1], 7);
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/std/src/path.rs b/library/std/src/path.rs
index 19d8f1edaf4..82d68369312 100644
--- a/library/std/src/path.rs
+++ b/library/std/src/path.rs
@@ -271,7 +271,7 @@ pub const MAIN_SEPARATOR: char = crate::sys::path::MAIN_SEP;
 /// The primary separator of path components for the current platform.
 ///
 /// For example, `/` on Unix and `\` on Windows.
-#[unstable(feature = "main_separator_str", issue = "94071")]
+#[stable(feature = "main_separator_str", since = "CURRENT_RUSTC_VERSION")]
 pub const MAIN_SEPARATOR_STR: &str = crate::sys::path::MAIN_SEP_STR;
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs
index cd360cbef96..ca4feac6fac 100644
--- a/src/bootstrap/setup.rs
+++ b/src/bootstrap/setup.rs
@@ -351,7 +351,7 @@ pub fn interactive_path() -> io::Result<Profile> {
     Ok(template)
 }
 
-// install a git hook to automatically run tidy --bless, if they want
+// install a git hook to automatically run tidy, if they want
 fn install_git_hook_maybe(config: &Config) -> io::Result<()> {
     let git = t!(config.git().args(&["rev-parse", "--git-common-dir"]).output().map(|output| {
         assert!(output.status.success(), "failed to run `git`");
@@ -367,7 +367,7 @@ fn install_git_hook_maybe(config: &Config) -> io::Result<()> {
     println!();
     println!(
         "Rust's CI will automatically fail if it doesn't pass `tidy`, the internal tool for ensuring code quality.
-If you'd like, x.py can install a git hook for you that will automatically run `tidy --bless` before
+If you'd like, x.py can install a git hook for you that will automatically run `test tidy` before
 pushing your code to ensure your code is up to par. If you decide later that this behavior is
 undesirable, simply delete the `pre-push` file from .git/hooks."
     );
diff --git a/src/etc/pre-push.sh b/src/etc/pre-push.sh
index 2a3086338b4..7a846d44ad6 100755
--- a/src/etc/pre-push.sh
+++ b/src/etc/pre-push.sh
@@ -1,6 +1,6 @@
 #!/usr/bin/env bash
 #
-# Call `tidy --bless` before git push
+# Call `tidy` before git push
 # Copy this script to .git/hooks to activate,
 # and remove it from .git/hooks to deactivate.
 #
diff --git a/src/test/ui/borrowck/issue-93078.rs b/src/test/ui/borrowck/issue-93078.rs
new file mode 100644
index 00000000000..2e608c5db3e
--- /dev/null
+++ b/src/test/ui/borrowck/issue-93078.rs
@@ -0,0 +1,15 @@
+trait Modify {
+    fn modify(&mut self) ;
+}
+
+impl<T> Modify for T  {
+    fn modify(&mut self)  {}
+}
+
+trait Foo {
+    fn mute(&mut self) {
+        self.modify(); //~ ERROR cannot borrow `self` as mutable
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/borrowck/issue-93078.stderr b/src/test/ui/borrowck/issue-93078.stderr
new file mode 100644
index 00000000000..771a652a173
--- /dev/null
+++ b/src/test/ui/borrowck/issue-93078.stderr
@@ -0,0 +1,12 @@
+error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable
+  --> $DIR/issue-93078.rs:11:9
+   |
+LL |         self.modify();
+   |         ^^^^^^^^^^^^^ cannot borrow as mutable
+   |
+   = note: as `Self` may be unsized, this call attempts to take `&mut &mut self`
+   = note: however, `&mut self` expands to `self: &mut Self`, therefore `self` cannot be borrowed mutably
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/src/test/ui/error-codes/E0013.rs b/src/test/ui/error-codes/E0013.rs
new file mode 100644
index 00000000000..9b3982a785b
--- /dev/null
+++ b/src/test/ui/error-codes/E0013.rs
@@ -0,0 +1,4 @@
+static X: i32 = 42;
+const Y: i32 = X; //~ ERROR constants cannot refer to statics [E0013]
+
+fn main() {}
diff --git a/src/test/ui/error-codes/E0013.stderr b/src/test/ui/error-codes/E0013.stderr
new file mode 100644
index 00000000000..dc22053a638
--- /dev/null
+++ b/src/test/ui/error-codes/E0013.stderr
@@ -0,0 +1,11 @@
+error[E0013]: constants cannot refer to statics
+  --> $DIR/E0013.rs:2:16
+   |
+LL | const Y: i32 = X;
+   |                ^
+   |
+   = help: consider extracting the value of the `static` to a `const`, and referring to that
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0013`.
diff --git a/src/test/ui/error-codes/E0015.rs b/src/test/ui/error-codes/E0015.rs
new file mode 100644
index 00000000000..b0211358d81
--- /dev/null
+++ b/src/test/ui/error-codes/E0015.rs
@@ -0,0 +1,8 @@
+fn create_some() -> Option<u8> {
+    Some(1)
+}
+
+const FOO: Option<u8> = create_some();
+//~^ ERROR cannot call non-const fn `create_some` in constants [E0015]
+
+fn main() {}
diff --git a/src/test/ui/error-codes/E0015.stderr b/src/test/ui/error-codes/E0015.stderr
new file mode 100644
index 00000000000..ec1ce47b2ce
--- /dev/null
+++ b/src/test/ui/error-codes/E0015.stderr
@@ -0,0 +1,11 @@
+error[E0015]: cannot call non-const fn `create_some` in constants
+  --> $DIR/E0015.rs:5:25
+   |
+LL | const FOO: Option<u8> = create_some();
+   |                         ^^^^^^^^^^^^^
+   |
+   = note: calls in constants are limited to constant functions, tuple structs and tuple variants
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0015`.
diff --git a/src/test/ui/type/closure-with-wrong-borrows.rs b/src/test/ui/type/closure-with-wrong-borrows.rs
new file mode 100644
index 00000000000..5f6a78351a2
--- /dev/null
+++ b/src/test/ui/type/closure-with-wrong-borrows.rs
@@ -0,0 +1,10 @@
+struct S<'a>(&'a str);
+
+fn f(inner: fn(&str, &S)) {
+}
+
+#[allow(unreachable_code)]
+fn main() {
+    let inner: fn(_, _) = unimplemented!();
+    f(inner); //~ ERROR mismatched types
+}
diff --git a/src/test/ui/type/closure-with-wrong-borrows.stderr b/src/test/ui/type/closure-with-wrong-borrows.stderr
new file mode 100644
index 00000000000..7370bc76467
--- /dev/null
+++ b/src/test/ui/type/closure-with-wrong-borrows.stderr
@@ -0,0 +1,19 @@
+error[E0308]: mismatched types
+  --> $DIR/closure-with-wrong-borrows.rs:9:7
+   |
+LL |     f(inner);
+   |     - ^^^^^ one type is more general than the other
+   |     |
+   |     arguments to this function are incorrect
+   |
+   = note: expected fn pointer `for<'a, 'b, 'c> fn(&'a str, &'b S<'c>)`
+              found fn pointer `fn(_, _)`
+note: function defined here
+  --> $DIR/closure-with-wrong-borrows.rs:3:4
+   |
+LL | fn f(inner: fn(&str, &S)) {
+   |    ^ -------------------
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs
index f1b9c1acbae..c6f55410e44 100644
--- a/src/tools/jsondoclint/src/validator.rs
+++ b/src/tools/jsondoclint/src/validator.rs
@@ -3,14 +3,17 @@ use std::hash::Hash;
 
 use rustdoc_json_types::{
     Constant, Crate, DynTrait, Enum, FnDecl, Function, FunctionPointer, GenericArg, GenericArgs,
-    GenericBound, GenericParamDef, Generics, Id, Impl, Import, ItemEnum, Module, OpaqueTy, Path,
-    Primitive, ProcMacro, Static, Struct, StructKind, Term, Trait, TraitAlias, Type, TypeBinding,
-    TypeBindingKind, Typedef, Union, Variant, VariantKind, WherePredicate,
+    GenericBound, GenericParamDef, Generics, Id, Impl, Import, ItemEnum, ItemSummary, Module,
+    OpaqueTy, Path, Primitive, ProcMacro, Static, Struct, StructKind, Term, Trait, TraitAlias,
+    Type, TypeBinding, TypeBindingKind, Typedef, Union, Variant, VariantKind, WherePredicate,
 };
 use serde_json::Value;
 
 use crate::{item_kind::Kind, json_find, Error, ErrorKind};
 
+// This is a rustc implementation detail that we rely on here
+const LOCAL_CRATE_ID: u32 = 0;
+
 /// The Validator walks over the JSON tree, and ensures it is well formed.
 /// It is made of several parts.
 ///
@@ -53,12 +56,19 @@ impl<'a> Validator<'a> {
     }
 
     pub fn check_crate(&mut self) {
+        // Graph traverse the index
         let root = &self.krate.root;
         self.add_mod_id(root);
         while let Some(id) = set_remove(&mut self.todo) {
             self.seen_ids.insert(id);
             self.check_item(id);
         }
+
+        let root_crate_id = self.krate.index[root].crate_id;
+        assert_eq!(root_crate_id, LOCAL_CRATE_ID, "LOCAL_CRATE_ID is wrong");
+        for (id, item_info) in &self.krate.paths {
+            self.check_item_info(id, item_info);
+        }
     }
 
     fn check_item(&mut self, id: &'a Id) {
@@ -364,6 +374,19 @@ impl<'a> Validator<'a> {
         fp.generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd));
     }
 
+    fn check_item_info(&mut self, id: &Id, item_info: &ItemSummary) {
+        // FIXME: Their should be a better way to determine if an item is local, rather than relying on `LOCAL_CRATE_ID`,
+        // which encodes rustc implementation details.
+        if item_info.crate_id == LOCAL_CRATE_ID && !self.krate.index.contains_key(id) {
+            self.errs.push(Error {
+                id: id.clone(),
+                kind: ErrorKind::Custom(
+                    "Id for local item in `paths` but not in `index`".to_owned(),
+                ),
+            })
+        }
+    }
+
     fn add_id_checked(&mut self, id: &'a Id, valid: fn(Kind) -> bool, expected: &str) {
         if let Some(kind) = self.kind_of(id) {
             if valid(kind) {
diff --git a/src/tools/jsondoclint/src/validator/tests.rs b/src/tools/jsondoclint/src/validator/tests.rs
index 37b826153ef..1ef41ff123a 100644
--- a/src/tools/jsondoclint/src/validator/tests.rs
+++ b/src/tools/jsondoclint/src/validator/tests.rs
@@ -1,6 +1,6 @@
 use std::collections::HashMap;
 
-use rustdoc_json_types::{Crate, Item, Visibility};
+use rustdoc_json_types::{Crate, Item, ItemKind, ItemSummary, Visibility, FORMAT_VERSION};
 
 use crate::json_find::SelectorPart;
 
@@ -64,3 +64,101 @@ fn errors_on_missing_links() {
         }],
     );
 }
+
+// Test we would catch
+// https://github.com/rust-lang/rust/issues/104064#issuecomment-1368589718
+#[test]
+fn errors_on_local_in_paths_and_not_index() {
+    let krate = Crate {
+        root: id("0:0:1572"),
+        crate_version: None,
+        includes_private: false,
+        index: HashMap::from_iter([
+            (
+                id("0:0:1572"),
+                Item {
+                    id: id("0:0:1572"),
+                    crate_id: 0,
+                    name: Some("microcore".to_owned()),
+                    span: None,
+                    visibility: Visibility::Public,
+                    docs: None,
+                    links: HashMap::from_iter([(("prim@i32".to_owned(), id("0:1:1571")))]),
+                    attrs: Vec::new(),
+                    deprecation: None,
+                    inner: ItemEnum::Module(Module {
+                        is_crate: true,
+                        items: vec![id("0:1:717")],
+                        is_stripped: false,
+                    }),
+                },
+            ),
+            (
+                id("0:1:717"),
+                Item {
+                    id: id("0:1:717"),
+                    crate_id: 0,
+                    name: Some("i32".to_owned()),
+                    span: None,
+                    visibility: Visibility::Public,
+                    docs: None,
+                    links: HashMap::default(),
+                    attrs: Vec::new(),
+                    deprecation: None,
+                    inner: ItemEnum::Primitive(Primitive { name: "i32".to_owned(), impls: vec![] }),
+                },
+            ),
+        ]),
+        paths: HashMap::from_iter([(
+            id("0:1:1571"),
+            ItemSummary {
+                crate_id: 0,
+                path: vec!["microcore".to_owned(), "i32".to_owned()],
+                kind: ItemKind::Primitive,
+            },
+        )]),
+        external_crates: HashMap::default(),
+        format_version: rustdoc_json_types::FORMAT_VERSION,
+    };
+
+    check(
+        &krate,
+        &[Error {
+            id: id("0:1:1571"),
+            kind: ErrorKind::Custom("Id for local item in `paths` but not in `index`".to_owned()),
+        }],
+    );
+}
+
+#[test]
+#[should_panic = "LOCAL_CRATE_ID is wrong"]
+fn checks_local_crate_id_is_correct() {
+    let krate = Crate {
+        root: id("root"),
+        crate_version: None,
+        includes_private: false,
+        index: HashMap::from_iter([(
+            id("root"),
+            Item {
+                id: id("root"),
+                crate_id: LOCAL_CRATE_ID.wrapping_add(1),
+                name: Some("irrelavent".to_owned()),
+                span: None,
+                visibility: Visibility::Public,
+                docs: None,
+                links: HashMap::default(),
+                attrs: Vec::new(),
+                deprecation: None,
+                inner: ItemEnum::Module(Module {
+                    is_crate: true,
+                    items: vec![],
+                    is_stripped: false,
+                }),
+            },
+        )]),
+        paths: HashMap::default(),
+        external_crates: HashMap::default(),
+        format_version: FORMAT_VERSION,
+    };
+    check(&krate, &[]);
+}
diff --git a/src/tools/tidy/src/error_codes.rs b/src/tools/tidy/src/error_codes.rs
index fdc9d789905..8d70335a9e7 100644
--- a/src/tools/tidy/src/error_codes.rs
+++ b/src/tools/tidy/src/error_codes.rs
@@ -15,8 +15,6 @@
 //!
 //! 4. We check that the error code is actually emitted by the compiler.
 //!   - This is done by searching `compiler/` with a regex.
-//!
-//! This tidy check was merged and refactored from two others. See #PR_NUM for information about linting changes that occurred during this refactor.
 
 use std::{ffi::OsStr, fs, path::Path};
 
@@ -33,8 +31,8 @@ const IGNORE_DOCTEST_CHECK: &[&str] = &["E0464", "E0570", "E0601", "E0602"];
 
 // Error codes that don't yet have a UI test. This list will eventually be removed.
 const IGNORE_UI_TEST_CHECK: &[&str] = &[
-    "E0313", "E0461", "E0465", "E0476", "E0490", "E0514", "E0523", "E0554", "E0640", "E0717",
-    "E0729", "E0789",
+    "E0461", "E0465", "E0476", "E0490", "E0514", "E0523", "E0554", "E0640", "E0717", "E0729",
+    "E0789",
 ];
 
 macro_rules! verbose_print {
@@ -57,7 +55,7 @@ pub fn check(root_path: &Path, search_paths: &[&Path], verbose: bool, bad: &mut
     let no_longer_emitted = check_error_codes_docs(root_path, &error_codes, &mut errors, verbose);
 
     // Stage 3: check list has UI tests
-    check_error_codes_tests(root_path, &error_codes, &mut errors, verbose);
+    check_error_codes_tests(root_path, &error_codes, &mut errors, verbose, &no_longer_emitted);
 
     // Stage 4: check list is emitted by compiler
     check_error_codes_used(search_paths, &error_codes, &mut errors, &no_longer_emitted, verbose);
@@ -174,8 +172,9 @@ fn check_error_codes_docs(
             return;
         }
 
-        let (found_code_example, found_proper_doctest, emit_ignore_warning, emit_no_longer_warning) =
+        let (found_code_example, found_proper_doctest, emit_ignore_warning, no_longer_emitted) =
             check_explanation_has_doctest(&contents, &err_code);
+
         if emit_ignore_warning {
             verbose_print!(
                 verbose,
@@ -183,13 +182,11 @@ fn check_error_codes_docs(
                 `IGNORE_DOCTEST_CHECK` constant instead."
             );
         }
-        if emit_no_longer_warning {
+
+        if no_longer_emitted {
             no_longer_emitted_codes.push(err_code.to_owned());
-            verbose_print!(
-                verbose,
-                "warning: Error code `{err_code}` is no longer emitted and should be removed entirely."
-            );
         }
+
         if !found_code_example {
             verbose_print!(
                 verbose,
@@ -226,7 +223,7 @@ fn check_explanation_has_doctest(explanation: &str, err_code: &str) -> (bool, bo
     let mut found_proper_doctest = false;
 
     let mut emit_ignore_warning = false;
-    let mut emit_no_longer_warning = false;
+    let mut no_longer_emitted = false;
 
     for line in explanation.lines() {
         let line = line.trim();
@@ -246,13 +243,13 @@ fn check_explanation_has_doctest(explanation: &str, err_code: &str) -> (bool, bo
         } else if line
             .starts_with("#### Note: this error code is no longer emitted by the compiler")
         {
-            emit_no_longer_warning = true;
+            no_longer_emitted = true;
             found_code_example = true;
             found_proper_doctest = true;
         }
     }
 
-    (found_code_example, found_proper_doctest, emit_ignore_warning, emit_no_longer_warning)
+    (found_code_example, found_proper_doctest, emit_ignore_warning, no_longer_emitted)
 }
 
 // Stage 3: Checks that each error code has a UI test in the correct directory
@@ -261,6 +258,7 @@ fn check_error_codes_tests(
     error_codes: &[String],
     errors: &mut Vec<String>,
     verbose: bool,
+    no_longer_emitted: &[String],
 ) {
     let tests_path = root_path.join(Path::new(ERROR_TESTS_PATH));
 
@@ -295,6 +293,11 @@ fn check_error_codes_tests(
             }
         };
 
+        if no_longer_emitted.contains(code) {
+            // UI tests *can't* contain error codes that are no longer emitted.
+            continue;
+        }
+
         let mut found_code = false;
 
         for line in file.lines() {