Repository: rust-lang/rust
Pull request #140700: Don't crash on error codes passed to `--explain` which exceed our internal limit of 9999 
Author: login: Kivooeo
Labels: name: T-compiler
description: Relevant to the compiler team, which will review and decide on the PR/issue.
name: S-waiting-on-bors
description: Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Created: 2025-05-06T10:10:10Z | Merged: 2025-05-08T02:15:41Z

<!-- homu-ignore:start -->
<!--
If this PR is related to an unstable feature or an otherwise tracked effort,
please link to the relevant tracking issue here. If you don't know of a related
tracking issue or there are none, feel free to ignore this.

This PR will get automatically assigned to a reviewer. In case you would like
a specific user to review your work, you can assign it to them by using

    r? <reviewer name>
-->
<!-- homu-ignore:end -->

removed panic in case where we do `--explain > 9999` and added check for it

now error looks like this instead of ICE
```
$ rustc.exe --explain E10000
error: E10000 is not a valid error code
```
fixes #140647
r? @fmease 

Linked issue #140647: rustc explain panics with error code longer than 4 digits
The following command line causes rustc to panic:

```
rustc --explain E10000
```

Based on the panic message, any error code above 9999 likely triggers this panic.
`rustc --version --verbose`:
```
rustc 1.88.0-nightly (d6a325d93 2025-05-03)
binary: rustc
commit-hash: d6a325d93a84077580c677f89affbe45682606c3
commit-date: 2025-05-03
host: x86_64-pc-windows-msvc
release: 1.88.0-nightly
LLVM version: 20.1.4
```

It appears that the panicking assertion assumes that code further up the stack does basic validation of the error code, when in fact that isn't happening.

### Error output

```
thread 'main' panicked at /rustc/d6a325d93a84077580c677f89affbe45682606c3\compiler\rustc_errors\src\codes.rs:9:1:
assertion failed: value <= 9999
stack backtrace:
   0:     0x7ff992ebd4c2 - std::backtrace_rs::backtrace::win64::trace
                               at /rustc/d6a325d93a84077580c677f89affbe45682606c3/library\std\src\..\..\backtrace\src\backtrace\win64.rs:85
   1:     0x7ff992ebd4c2 - std::backtrace_rs::backtrace::trace_unsynchronized
                               at /rustc/d6a325d93a84077580c677f89affbe45682606c3/library\std\src\..\..\backtrace\src\backtrace\mod.rs:66
   2:     0x7ff992ebd4c2 - std::sys::backtrace::_print_fmt
                               at /rustc/d6a325d93a84077580c677f89affbe45682606c3/library\std\src\sys\backtrace.rs:66
   3:     0x7ff992ebd4c2 - std::sys::backtrace::impl$0::print::impl$0::fmt
                               at /rustc/d6a325d93a84077580c677f89affbe45682606c3/library\std\src\sys\backtrace.rs:39
   4:     0x7ff992eefccb - core::fmt::rt::Argument::fmt
                               at /rustc/d6a325d93a84077580c677f89affbe45682606c3/library\core\src\fmt\rt.rs:181
   5:     0x7ff992eefccb - core::fmt::write
                               at /rustc/d6a325d93a84077580c677f89affbe45682606c3/library\core\src\fmt\mod.rs:1446
   6:     0x7ff992eb3577 - std::io::default_write_fmt
                               at /rustc/d6a325d93a84077580c677f89affbe45682606c3/library\std\src\io\mod.rs:639
   7:     0x7ff992eb3577 - std::io::Write::write_fmt<std::sys::stdio::windows::Stderr>
                               at /rustc/d6a325d93a84077580c677f89affbe45682606c3/library\std\src\io\mod.rs:1914
   8:     0x7ff992ebd305 - std::sys::backtrace::BacktraceLock::print
                               at /rustc/d6a325d93a84077580c677f89affbe45682606c3/library\std\src\sys\backtrace.rs:42
   9:     0x7ff992ec2faa - std::panicking::default_hook::closure$0
                               at /rustc/d6a325d93a84077580c677f89affbe45682606c3/library\std\src\panicking.rs:300
  10:     0x7ff992ec2d40 - std::panicking::default_hook
                               at /rustc/d6a325d93a84077580c677f89affbe45682606c3/library\std\src\panicking.rs:327
  11:     0x7ff9945c40c9 - core[87a27484b78f7475]::slice::sort::unstable::heapsort::heapsort::<((rustc_lint_defs[b0c94f99b9e7b658]::Level, &str), usize), <((rustc_lint_defs[b0c94f99b9e7b658]::Level, &str), usize) as core[87a27484b78f7475]::cmp::PartialOrd>::lt>
  12:     0x7ff992ec3c3e - std::panicking::rust_panic_with_hook
                               at /rustc/d6a325d93a84077580c677f89affbe45682606c3/library\std\src\panicking.rs:841
  13:     0x7ff992ec3962 - std::panicking::begin_panic_handler::closure$0
                               at /rustc/d6a325d93a84077580c677f89affbe45682606c3/library\std\src\panicking.rs:699
  14:     0x7ff992ebe28f - std::sys::backtrace::__rust_end_short_backtrace<std::panicking::begin_panic_handler::closure_env$0,never$>
                               at /rustc/d6a325d93a84077580c677f89affbe45682606c3/library\std\src\sys\backtrace.rs:168
  15:     0x7ff992ec359e - std::panicking::begin_panic_handler
                               at /rustc/d6a325d93a84077580c677f89affbe45682606c3/library\std\src\panicking.rs:697
  16:     0x7ff996030751 - core::panicking::panic_fmt
                               at /rustc/d6a325d93a84077580c677f89affbe45682606c3/library\core\src\panicking.rs:75
  17:     0x7ff99603080d - core::panicking::panic
                               at /rustc/d6a325d93a84077580c677f89affbe45682606c3/library\core\src\panicking.rs:145
  18:     0x7ff98f7f8387 - rustc_driver_impl[4bf9a847fe62d732]::run_compiler
  19:     0x7ff98f8039c7 - rustc_driver_impl[4bf9a847fe62d732]::main
  20:     0x7ff7bb711039 - _wctype
  21:     0x7ff7bb711006 - _wctype
  22:     0x7ff7bb71101c - _wctype
  23:     0x7ff992ea280c - std::rt::lang_start_internal::closure$0
                               at /rustc/d6a325d93a84077580c677f89affbe45682606c3/library\std\src\rt.rs:168
  24:     0x7ff992ea280c - std::panicking::try::do_call
                               at /rustc/d6a325d93a84077580c677f89affbe45682606c3/library\std\src\panicking.rs:589
  25:     0x7ff992ea280c - std::panicking::try
                               at /rustc/d6a325d93a84077580c677f89affbe45682606c3/library\std\src\panicking.rs:552
  26:     0x7ff992ea280c - std::panic::catch_unwind
                               at /rustc/d6a325d93a84077580c677f89affbe45682606c3/library\std\src\panic.rs:359
  27:     0x7ff992ea280c - std::rt::lang_start_internal
                               at /rustc/d6a325d93a84077580c677f89affbe45682606c3/library\std\src\rt.rs:164
  28:     0x7ff7bb71106a - main
  29:     0x7ff7bb71126c - invoke_main
                               at D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:78
  30:     0x7ff7bb71126c - __scrt_common_main_seh
                               at D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
  31:     0x7ffa7b61e8d7 - BaseThreadInitThunk
  32:     0x7ffa7c8114fc - RtlUserThreadStart

error: the compiler unexpectedly panicked. this is a bug.
```


<!-- TRIAGEBOT_START -->

<!-- TRIAGEBOT_ASSIGN_START -->

<!-- TRIAGEBOT_ASSIGN_DATA_START$${"user":"Kivooeo"}$$TRIAGEBOT_ASSIGN_DATA_END -->

<!-- TRIAGEBOT_ASSIGN_END -->
<!-- TRIAGEBOT_END -->
Linked issue comments: user: login: Kivooeo
created_at: 2025-05-06T09:26:26Z
body: @rustbot claim
author_association: MEMBER

Diff:
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index fdf8053b15a67..95cfe221d3fe1 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -463,6 +463,7 @@ fn handle_explain(early_dcx: &EarlyDiagCtxt, registry: Registry, code: &str, col
     // Allow "E0123" or "0123" form.
     let upper_cased_code = code.to_ascii_uppercase();
     if let Ok(code) = upper_cased_code.strip_prefix('E').unwrap_or(&upper_cased_code).parse::<u32>()
+        && code <= ErrCode::MAX_AS_U32
         && let Ok(description) = registry.try_find_description(ErrCode::from_u32(code))
     {
         let mut is_in_code_block = false;
diff --git a/tests/ui/explain.rs b/tests/ui/explain/basic.rs
similarity index 100%
rename from tests/ui/explain.rs
rename to tests/ui/explain/basic.rs
diff --git a/tests/ui/explain.stdout b/tests/ui/explain/basic.stdout
similarity index 100%
rename from tests/ui/explain.stdout
rename to tests/ui/explain/basic.stdout
diff --git a/tests/ui/explain/error-with-no-explanation.rs b/tests/ui/explain/error-with-no-explanation.rs
new file mode 100644
index 0000000000000..383820eb45804
--- /dev/null
+++ b/tests/ui/explain/error-with-no-explanation.rs
@@ -0,0 +1,3 @@
+// It's a valid error with no added explanation
+//@ compile-flags: --explain E9999
+//~? ERROR: E9999 is not a valid error code
diff --git a/tests/ui/explain/error-with-no-explanation.stderr b/tests/ui/explain/error-with-no-explanation.stderr
new file mode 100644
index 0000000000000..afb738cfecb9d
--- /dev/null
+++ b/tests/ui/explain/error-with-no-explanation.stderr
@@ -0,0 +1,2 @@
+error: E9999 is not a valid error code
+
diff --git a/tests/ui/explain/invalid-error-code.rs b/tests/ui/explain/invalid-error-code.rs
new file mode 100644
index 0000000000000..114118d5196b0
--- /dev/null
+++ b/tests/ui/explain/invalid-error-code.rs
@@ -0,0 +1,2 @@
+//@ compile-flags: --explain error_code
+//~? ERROR: error_code is not a valid error code
diff --git a/tests/ui/explain/invalid-error-code.stderr b/tests/ui/explain/invalid-error-code.stderr
new file mode 100644
index 0000000000000..c33122ea88cb1
--- /dev/null
+++ b/tests/ui/explain/invalid-error-code.stderr
@@ -0,0 +1,2 @@
+error: error_code is not a valid error code
+
diff --git a/tests/ui/explain/no-E-prefix.rs b/tests/ui/explain/no-E-prefix.rs
new file mode 100644
index 0000000000000..39c9122bfe1b7
--- /dev/null
+++ b/tests/ui/explain/no-E-prefix.rs
@@ -0,0 +1,2 @@
+//@ compile-flags: --explain 425
+//@ check-pass
diff --git a/tests/ui/explain/no-E-prefix.stdout b/tests/ui/explain/no-E-prefix.stdout
new file mode 100644
index 0000000000000..756b970aa7e69
--- /dev/null
+++ b/tests/ui/explain/no-E-prefix.stdout
@@ -0,0 +1,57 @@
+An unresolved name was used.
+
+Erroneous code examples:
+
+```
+something_that_doesnt_exist::foo;
+// error: unresolved name `something_that_doesnt_exist::foo`
+
+// or:
+
+trait Foo {
+    fn bar() {
+        Self; // error: unresolved name `Self`
+    }
+}
+
+// or:
+
+let x = unknown_variable;  // error: unresolved name `unknown_variable`
+```
+
+Please verify that the name wasn't misspelled and ensure that the
+identifier being referred to is valid for the given situation. Example:
+
+```
+enum something_that_does_exist {
+    Foo,
+}
+```
+
+Or:
+
+```
+mod something_that_does_exist {
+    pub static foo : i32 = 0i32;
+}
+
+something_that_does_exist::foo; // ok!
+```
+
+Or:
+
+```
+let unknown_variable = 12u32;
+let x = unknown_variable; // ok!
+```
+
+If the item is not defined in the current module, it must be imported using a
+`use` statement, like so:
+
+```
+use foo::bar;
+bar();
+```
+
+If the item you are importing is not defined in some super-module of the
+current module, then it must also be declared as public (e.g., `pub fn`).
diff --git a/tests/ui/explain/overflow-error-code.rs b/tests/ui/explain/overflow-error-code.rs
new file mode 100644
index 0000000000000..284d5cddb35a8
--- /dev/null
+++ b/tests/ui/explain/overflow-error-code.rs
@@ -0,0 +1,4 @@
+// Check that we don't crash on error codes exceeding our internal limit.
+// issue: <https://github.com/rust-lang/rust/issues/140647>
+//@ compile-flags: --explain E10000
+//~? ERROR: E10000 is not a valid error code
diff --git a/tests/ui/explain/overflow-error-code.stderr b/tests/ui/explain/overflow-error-code.stderr
new file mode 100644
index 0000000000000..67e584ea32e18
--- /dev/null
+++ b/tests/ui/explain/overflow-error-code.stderr
@@ -0,0 +1,2 @@
+error: E10000 is not a valid error code
+


Reviews:
user: login: fmease
body: > any actual idea why tidy start fails here?

I'm pretty sure it's simply due to tidy (`src/tools/tidy/src/error_codes.rs:361`) grepping through ~all Rust compiler source files looking for `\bE\d{4}\b` and finding the `E9999` in your previous error message that you've since removed.
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-06T10:38:03Z
user: login: Kivooeo
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-06T10:44:33Z
user: login: fmease
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-06T10:44:50Z
user: login: Kivooeo
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-06T10:48:07Z
user: login: ShE3py
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T12:21:29Z
user: login: Kivooeo
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-06T12:24:21Z
user: login: Kivooeo
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-06T12:27:40Z
user: login: Kivooeo
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-06T12:34:46Z
user: login: ShE3py
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T12:36:41Z
user: login: Kivooeo
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-06T13:12:41Z
user: login: Kivooeo
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-06T13:28:07Z
user: login: fmease
body: Two suggestions then we should be good to go!
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-06T14:58:05Z
user: login: davidtwco
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-07T15:49:54Z

Review comments:
diff_hunk: @@ -547,6 +547,7 @@ E0801: 0801,
 E0802: 0802,
 E0803: 0803,
 E0804: 0804,
+E9999: 9999,
path: compiler/rustc_error_codes/src/lib.rs
user: login: fmease
body: No, this shouldn't be necessary now that you've removed the specialized error message.
created_at: 2025-05-06T10:38:00Z
author_association: MEMBER
diff_hunk: @@ -547,6 +547,7 @@ E0801: 0801,
 E0802: 0802,
 E0803: 0803,
 E0804: 0804,
+E9999: 9999,
path: compiler/rustc_error_codes/src/lib.rs
user: login: Kivooeo
body: yeah i removed this custom message error for anything greater than 9999 and removed E9999 from error_codes, ran tidy locally and tidy said it's fine, let's see
created_at: 2025-05-06T10:44:33Z
author_association: MEMBER
diff_hunk: @@ -7,7 +7,6 @@
 use std::fmt;
 
 rustc_index::newtype_index! {
-    #[max = 9999] // Because all error codes have four digits.
path: compiler/rustc_errors/src/codes.rs
user: login: fmease
body: While we obviously shouldn't crash when the *user* enters error codes greater than E9999, the original purpose of this internal limit is to prevent rustc devs from introducing five-digit error codes without changing all other relevant parts of the compiler, docs and tools (like tidy) to now pad all four-digit codes with an additional `0` to become five-digit codes, etc.
created_at: 2025-05-06T10:44:42Z
author_association: MEMBER
diff_hunk: @@ -7,7 +7,6 @@
 use std::fmt;
 
 rustc_index::newtype_index! {
-    #[max = 9999] // Because all error codes have four digits.
path: compiler/rustc_errors/src/codes.rs
user: login: Kivooeo
body: i was wondering how can i fix this to make not panicking here, and without changing NewType entirely, so this is only solution that worked fine that i came up with
created_at: 2025-05-06T10:48:07Z
author_association: MEMBER
diff_hunk: @@ -7,7 +7,6 @@
 use std::fmt;
 
 rustc_index::newtype_index! {
-    #[max = 9999] // Because all error codes have four digits.
path: compiler/rustc_errors/src/codes.rs
user: login: ShE3py
body: Running the ICE with debuginfo gives this line:
https://github.com/rust-lang/rust/blob/651e9cf327358b28db7e37a2ae61727f4a2ef232/compiler/rustc_driver_impl/src/lib.rs#L466
You probably want to manually add an `impl TryFrom<u32> for ErrCode`.
created_at: 2025-05-06T12:21:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -7,7 +7,6 @@
 use std::fmt;
 
 rustc_index::newtype_index! {
-    #[max = 9999] // Because all error codes have four digits.
path: compiler/rustc_errors/src/codes.rs
user: login: Kivooeo
body: doesnt it will still panic inside NewType because of max limitations? 
created_at: 2025-05-06T12:24:21Z
author_association: MEMBER
diff_hunk: @@ -7,7 +7,6 @@
 use std::fmt;
 
 rustc_index::newtype_index! {
-    #[max = 9999] // Because all error codes have four digits.
path: compiler/rustc_errors/src/codes.rs
user: login: Kivooeo
body: i believe that the panic occurs here
https://github.com/rust-lang/rust/blob/651e9cf327358b28db7e37a2ae61727f4a2ef232/compiler/rustc_index_macros/src/newtype.rs#L183

because ICE was adressing to this line 
https://github.com/rust-lang/rust/blob/651e9cf327358b28db7e37a2ae61727f4a2ef232/compiler/rustc_errors/src/codes.rs#L9

```
thread 'main' panicked at /rustc/d6a325d93a84077580c677f89affbe45682606c3\compiler\rustc_errors\src\codes.rs:9:1:
assertion failed: value <= 9999
```
created_at: 2025-05-06T12:27:40Z
author_association: MEMBER
diff_hunk: @@ -7,7 +7,6 @@
 use std::fmt;
 
 rustc_index::newtype_index! {
-    #[max = 9999] // Because all error codes have four digits.
path: compiler/rustc_errors/src/codes.rs
user: login: Kivooeo
body: so, as far as i concerned, we cannot really prevent this panic when having #[max = 9999] and pass something bigger than it, we should not let this error code (that is bigger than max value) get to this assertations because it'll panic is such cases

but i may missing something for sure, if so please let me know we'll brainstorm it together 
created_at: 2025-05-06T12:34:46Z
author_association: MEMBER
diff_hunk: @@ -7,7 +7,6 @@
 use std::fmt;
 
 rustc_index::newtype_index! {
-    #[max = 9999] // Because all error codes have four digits.
path: compiler/rustc_errors/src/codes.rs
user: login: ShE3py
body: The idea is to return `Err(_)`/`None`, i.e. not constructing the newtype if code > 9999 (= not calling `from_u32`, so the `assert!` is not reached).

Alternativement, adding `impl Borrow<u32> for ErrCode` so this fn can directly being called with an `u32` (`try_find_description<C>(&self, code: C) where ErrCode: Borrow<C>`);
https://github.com/rust-lang/rust/blob/651e9cf327358b28db7e37a2ae61727f4a2ef232/compiler/rustc_errors/src/registry.rs#L18-L22

Alternatively, just adding `&& code <= ErrCode::MAX_AS_U32` before this:
https://github.com/rust-lang/rust/blob/651e9cf327358b28db7e37a2ae61727f4a2ef232/compiler/rustc_driver_impl/src/lib.rs#L466
created_at: 2025-05-06T12:36:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -7,7 +7,6 @@
 use std::fmt;
 
 rustc_index::newtype_index! {
-    #[max = 9999] // Because all error codes have four digits.
path: compiler/rustc_errors/src/codes.rs
user: login: Kivooeo
body: yep, currently playing around this ideas, this would take some time because of recompilling, thanks for ideas
created_at: 2025-05-06T13:12:41Z
author_association: MEMBER
diff_hunk: @@ -7,7 +7,6 @@
 use std::fmt;
 
 rustc_index::newtype_index! {
-    #[max = 9999] // Because all error codes have four digits.
path: compiler/rustc_errors/src/codes.rs
user: login: Kivooeo
body: ive tried this thing 
```rust
  pub fn try_find_description(&self, code: ErrCode) -> Result<&'static str, InvalidErrorCode> {
      if code > ErrCode::from_u32(9999) {
          return Err(InvalidErrorCode);
      }
      self.long_descriptions.get(&code).copied().ok_or(InvalidErrorCode)
  }
```
and its not worked for me
but this one `&& code <= ErrCode::MAX_AS_U32` runs like perfect, so we can stick with it
created_at: 2025-05-06T13:28:07Z
author_association: MEMBER
path: tests/ui/explain-with-error.rs
user: login: fmease
body: We no longer want to add new top-level UI tests — `tests/ui/explain.rs` is at the top-level for historical reasons.

Could you either create directory `tests/ui/explain/` and move `tests/ui/explain.{rs,stdout}` to `tests/ui/explain/basic.{rs,stdout}` as well as this test (rename it to `tests/ui/explain/invalid-error-code.*` for clarity perhaps?).

(Alternatively, you could just move this test to `tests/ui/invalid-compile-flags/` and keep the existing `explain.rs` but er, it needs to move at point anyway so why not now?).
created_at: 2025-05-06T14:55:49Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,2 @@
+//@ compile-flags: --explain E10000
path: tests/ui/explain-with-error.rs
user: login: fmease
body: ```suggestion
// Check that we don't crash on error codes exceeding our internal limit.
// issue: <https://github.com/rust-lang/rust/issues/140647>
//@ compile-flags: --explain E10000
```
created_at: 2025-05-06T14:57:33Z
author_association: MEMBER

PR comments:
user: login: rustbot
created_at: 2025-05-06T10:10:14Z
body: fmease is on vacation.

Please choose another assignee.
author_association: COLLABORATOR
user: login: rustbot
created_at: 2025-05-06T10:10:16Z
body: r? @davidtwco

rustbot has assigned @davidtwco.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use `r?` to explicitly pick a reviewer
author_association: COLLABORATOR
user: login: rust-log-analyzer
created_at: 2025-05-06T10:20:39Z
body: 
The job **`mingw-check-tidy`** failed! Check out the build log: [(web)](https://github.com/rust-lang/rust/actions/runs/14857058134/job/41712867218) [(plain)](https://github.com/rust-lang/rust/commit/0f3805b9b373ad27ce3be65a2cba8f833882967b/checks/41712867218/logs)

<details><summary><i>Click to see the possible cause of the failure (guessed by this bot)</i></summary>

```plain
info: removing rustup binaries
info: rustup is uninstalled
##[group]Image checksum input
mingw-check-tidy
# We use the ghcr base image because ghcr doesn't have a rate limit
# and the mingw-check-tidy job doesn't cache docker images in CI.
FROM ghcr.io/rust-lang/ubuntu:22.04

ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
  g++ \
---

COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/

# NOTE: intentionally uses python2 for x.py so we can test it still works.
# validate-toolstate only runs in our CI, so it's ok for it to only support python3.
ENV SCRIPT TIDY_PRINT_DIFF=1 python2.7 ../x.py test \
           --stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp
#
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
#    pip-compile --allow-unsafe --generate-hashes reuse-requirements.in
---
#12 2.832 Building wheels for collected packages: reuse
#12 2.833   Building wheel for reuse (pyproject.toml): started
#12 3.046   Building wheel for reuse (pyproject.toml): finished with status 'done'
#12 3.047   Created wheel for reuse: filename=reuse-4.0.3-cp310-cp310-manylinux_2_35_x86_64.whl size=132719 sha256=d2a2565e7037ad3883fb9337653f2e25bbb588534fbef3697286cbc26d1bf634
#12 3.048   Stored in directory: /tmp/pip-ephem-wheel-cache-dew01gzl/wheels/3d/8d/0a/e0fc6aba4494b28a967ab5eaf951c121d9c677958714e34532
#12 3.050 Successfully built reuse
#12 3.050 Installing collected packages: boolean-py, binaryornot, tomlkit, reuse, python-debian, markupsafe, license-expression, jinja2, chardet, attrs
#12 3.444 Successfully installed attrs-23.2.0 binaryornot-0.4.4 boolean-py-4.0 chardet-5.2.0 jinja2-3.1.4 license-expression-30.3.0 markupsafe-2.1.5 python-debian-0.1.49 reuse-4.0.3 tomlkit-0.13.0
#12 3.445 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
#12 3.982 Collecting virtualenv
#12 4.031   Downloading virtualenv-20.31.1-py3-none-any.whl (6.1 MB)
#12 4.264      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 6.1/6.1 MB 26.3 MB/s eta 0:00:00
#12 4.327 Collecting filelock<4,>=3.12.2
#12 4.335   Downloading filelock-3.18.0-py3-none-any.whl (16 kB)
#12 4.358 Collecting distlib<1,>=0.3.7
#12 4.365   Downloading distlib-0.3.9-py2.py3-none-any.whl (468 kB)
#12 4.377      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 469.0/469.0 KB 49.5 MB/s eta 0:00:00
#12 4.413 Collecting platformdirs<5,>=3.9.1
#12 4.421   Downloading platformdirs-4.3.7-py3-none-any.whl (18 kB)
#12 4.503 Installing collected packages: distlib, platformdirs, filelock, virtualenv
#12 4.693 Successfully installed distlib-0.3.9 filelock-3.18.0 platformdirs-4.3.7 virtualenv-20.31.1
#12 4.694 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
#12 DONE 4.8s

#13 [7/8] COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
#13 DONE 0.0s
---
DirectMap4k:      126912 kB
DirectMap2M:     7213056 kB
DirectMap1G:    11534336 kB
##[endgroup]
Executing TIDY_PRINT_DIFF=1 python2.7 ../x.py test            --stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp
+ TIDY_PRINT_DIFF=1 python2.7 ../x.py test --stage 0 src/tools/tidy tidyselftest --extra-checks=py,cpp
##[group]Building bootstrap
    Finished `dev` profile [unoptimized] target(s) in 0.05s
##[endgroup]
WARN: currently no CI rustc builds have rustc debug assertions enabled. Please either set `rust.debug-assertions` to `false` if you want to use download CI rustc or set `rust.download-rustc` to `false`.
[TIMING] core::build_steps::tool::LibcxxVersionTool { target: x86_64-unknown-linux-gnu } -- 0.215
---
[TIMING] core::build_steps::tool::Tidy { compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }, target: x86_64-unknown-linux-gnu } -- 0.000
fmt check
fmt: checked 6007 files
tidy check
tidy error: Error code `E9999` is used in the compiler but not defined and documented in `compiler/rustc_error_codes/src/lib.rs`.
tidy: Skipping binary file check, read-only filesystem
removing old virtual environment
creating virtual environment at '/checkout/obj/build/venv' using 'python3.10' and 'venv'
creating virtual environment at '/checkout/obj/build/venv' using 'python3.10' and 'virtualenv'
Requirement already satisfied: pip in ./build/venv/lib/python3.10/site-packages (25.1.1)
linting python files
All checks passed!
checking python file formatting
26 files already formatted
checking C++ file formatting
some tidy checks failed
Command has failed. Rerun with -v to see more details.
Build completed unsuccessfully in 0:01:55
  local time: Tue May  6 10:20:27 UTC 2025
  network time: Tue, 06 May 2025 10:20:28 GMT
##[error]Process completed with exit code 1.
Post job cleanup.
```

</details>

author_association: COLLABORATOR
user: login: jieyouxu
created_at: 2025-05-06T10:29:29Z
body: I feel like this is very arbitrary. The `9999` limit is just an internal implementation detail (an internal "this is obviously wrong" check and would be bumped if we ever get to that many error codes) and shouldn't be exposed to the user. I would've just expected

```
$ rustc --explain E12345
error: E12345 is not a valid error code
```

to behave the same as

```
$ rustc --explain E4444
error: E4444 is not a valid error code
```

and

```
$ rustc --explain E12345678901234567890 # exceeds u32::MAX
error: E12345678901234567890 is not a valid error code
```

to gracefully fail with the same error message.
author_association: MEMBER
user: login: Kivooeo
created_at: 2025-05-06T10:33:13Z
body: @jieyouxu any actual idea why tidy start fails here? 
```
tidy check
tidy error: Error code `E9999` is used in the compiler but not defined and documented in `compiler/rustc_error_codes/src/lib.rs`.
```
like... should I just add E9999 there..?
and about your suggestion, you asking to merge both errors into one `error_code is not a valid error code` right? 
author_association: MEMBER
user: login: rustbot
created_at: 2025-05-06T10:36:33Z
body: Some changes occurred in diagnostic error codes

cc @GuillaumeGomez
author_association: COLLABORATOR
user: login: Kivooeo
created_at: 2025-05-06T10:37:37Z
body: oh no, looks like adding E9999 was a miskate here
but it actually makes tidy pass, so idk, correct me here pls 
author_association: MEMBER
user: login: rust-log-analyzer
created_at: 2025-05-06T10:42:08Z
body: 
The job **`x86_64-gnu-llvm-19`** failed! Check out the build log: [(web)](https://github.com/rust-lang/rust/actions/runs/14857551110/job/41714423151) [(plain)](https://github.com/rust-lang/rust/commit/52daf9ff76fa5a10ecf0a903b72de083c7e73f84/checks/41714423151/logs)

<details><summary><i>Click to see the possible cause of the failure (guessed by this bot)</i></summary>

```plain
#18 exporting to docker image format
#18 sending tarball 20.6s done
#18 DONE 27.0s
##[endgroup]
Setting extra environment values for docker:  --env ENABLE_GCC_CODEGEN=1 --env GCC_EXEC_PREFIX=/usr/lib/gcc/
[CI_JOB_NAME=x86_64-gnu-llvm-19]
[CI_JOB_NAME=x86_64-gnu-llvm-19]
debug: `DISABLE_CI_RUSTC_IF_INCOMPATIBLE` configured.
---
sccache: Listening on address 127.0.0.1:4226
##[group]Configure the build
configure: processing command line
configure: 
configure: build.configure-args := ['--build=x86_64-unknown-linux-gnu', '--llvm-root=/usr/lib/llvm-19', '--enable-llvm-link-shared', '--set', 'rust.randomize-layout=true', '--set', 'rust.thin-lto-import-instr-limit=10', '--set', 'build.print-step-timings', '--enable-verbose-tests', '--set', 'build.metrics', '--enable-verbose-configure', '--enable-sccache', '--disable-manage-submodules', '--enable-locked-deps', '--enable-cargo-native-static', '--set', 'rust.codegen-units-std=1', '--set', 'dist.compression-profile=balanced', '--dist-compression-formats=xz', '--set', 'rust.lld=false', '--disable-dist-src', '--release-channel=nightly', '--enable-debug-assertions', '--enable-overflow-checks', '--enable-llvm-assertions', '--set', 'rust.verify-llvm-ir', '--set', 'rust.codegen-backends=llvm,cranelift,gcc', '--set', 'llvm.static-libstdcpp', '--set', 'gcc.download-ci-gcc=true', '--enable-new-symbol-mangling']
configure: build.build          := x86_64-unknown-linux-gnu
configure: target.x86_64-unknown-linux-gnu.llvm-config := /usr/lib/llvm-19/bin/llvm-config
configure: llvm.link-shared     := True
configure: rust.randomize-layout := True
configure: rust.thin-lto-import-instr-limit := 10
---
   Compiling rustc_lint_defs v0.0.0 (/checkout/compiler/rustc_lint_defs)
   Compiling rustc_hir_pretty v0.0.0 (/checkout/compiler/rustc_hir_pretty)
[RUSTC-TIMING] rustc_lint_defs test:false 0.849
   Compiling rustc_errors v0.0.0 (/checkout/compiler/rustc_errors)
error: couldn't read `compiler/rustc_errors/src/../../rustc_error_codes/src/error_codes/E9999.md`: No such file or directory (os error 2)
     |
    ::: /checkout/compiler/rustc_error_codes/src/lib.rs:31:1
     |
31   |      macro_rules! error_codes {
     |      ------------------------ in this expansion of `rustc_error_codes::error_codes!` (#1)
32   |          ($macro:path) => (
33   |    /         $macro!(
34   |    | E0001: 0001,
35   |    | E0002: 0002,
36   |    | E0004: 0004,
...       |
550  |    | E9999: 9999,
551  |    |         );
     |    |_________- in this macro invocation (#2)
    --> compiler/rustc_errors/src/codes.rs:30:17
     |
22   | /    macro_rules! define_error_code_constants_and_diagnostics_table {
23   | |        ($($name:ident: $num:literal,)*) => (
24   | |            $(
25   | |                pub const $name: $crate::ErrCode = $crate::ErrCode::from_u32($num);
...    |
30   | |  /                 include_str!(
31   | |  |                     concat!("../../rustc_error_codes/src/error_codes/", stringify!($name), ".md")
32   | |  |                 )
     | |  |                 ^ in this macro invocation (#3)
     | |  |_________________|
     | |
...    |
36   | |    }
     | |____- in this expansion of `define_error_code_constants_and_diagnostics_table!` (#2)
37   |
38   |      rustc_error_codes::error_codes!(define_error_code_constants_and_diagnostics_table);
     |      ---------------------------------------------------------------------------------- in this macro invocation (#1)
     |
    ::: /checkout/library/core/src/macros/mod.rs:1351:5
     |
1351 |          macro_rules! include_str {
```

</details>

author_association: COLLABORATOR
user: login: ShE3py
created_at: 2025-05-06T14:12:42Z
body: You may want to add a regression test based on [`tests/ui/explain.rs`](https://github.com/rust-lang/rust/blob/651e9cf327358b28db7e37a2ae61727f4a2ef232/tests/ui/explain.rs)
author_association: CONTRIBUTOR
user: login: Kivooeo
created_at: 2025-05-06T14:29:14Z
body: @ShE3py oh, yes i want to add tests for it, didnt knew that there is some for `--explain`, should i create it separate tests or can do it in one file?

yeah, i guess i cant do this in one file
`error: Option 'explain' given more than once`
author_association: MEMBER
user: login: ShE3py
created_at: 2025-05-06T14:37:04Z
body: Yup, that's unfortunate but AFAIK you can only run `rustc` once per file.

Btw, `//~?` may be interesting to use:
<https://rustc-dev-guide.rust-lang.org/tests/ui.html#error-without-line-information>
author_association: CONTRIBUTOR
user: login: Kivooeo
created_at: 2025-05-06T14:42:12Z
body: yep, `//~?` is a nice one
author_association: MEMBER
user: login: Kivooeo
created_at: 2025-05-06T15:06:37Z
body: @fmease seems done, but check pls if i got you and made all right
i also may should add some tests for other cases like valid error code for error that not exists or just something weird like word instead of number etc
author_association: MEMBER
user: login: fmease
created_at: 2025-05-06T16:18:03Z
body: > i also may should add some tests for other cases like valid error code for error that not exists or just something weird like word instead of number etc

If you'd like to, go right ahead, sure sounds like a good idea.
author_association: MEMBER
user: login: Kivooeo
created_at: 2025-05-06T19:38:07Z
body: @fmease should be good to go, added few more test cases
author_association: MEMBER
user: login: davidtwco
created_at: 2025-05-07T15:50:19Z
body: @bors r=davidtwco,fmease rollup
author_association: MEMBER
user: login: bors
created_at: 2025-05-07T15:50:22Z
body: :pushpin: Commit 3c1c0726ad18dc07f3ef9b9465d5997ca13e332c has been approved by `davidtwco,fmease`

It is now in the [queue](https://bors.rust-lang.org/homu/queue/rust) for this repository.

<!-- @bors r=davidtwco,fmease 3c1c0726ad18dc07f3ef9b9465d5997ca13e332c -->
<!-- homu: {"type":"Approved","sha":"3c1c0726ad18dc07f3ef9b9465d5997ca13e332c","approver":"davidtwco,fmease","queue":"https://bors.rust-lang.org/homu/queue/rust"} -->
author_association: COLLABORATOR