Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

statically guarantee that current error codes are documented #108482

Merged
merged 2 commits into from
Feb 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1445,6 +1445,7 @@ name = "error_index_generator"
version = "0.0.0"
dependencies = [
"mdbook",
"rustc_error_codes",
]

[[package]]
Expand Down
5 changes: 1 addition & 4 deletions compiler/rustc_driver_impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) {
let normalised =
if upper_cased_code.starts_with('E') { upper_cased_code } else { format!("E{code:0>4}") };
match registry.try_find_description(&normalised) {
Ok(Some(description)) => {
Ok(description) => {
let mut is_in_code_block = false;
let mut text = String::new();
// Slice off the leading newline and print.
Expand All @@ -509,9 +509,6 @@ fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) {
print!("{text}");
}
}
Ok(None) => {
early_error(output, &format!("no extended information for {code}"));
}
Err(InvalidErrorCode) => {
early_error(output, &format!("{code} is not a valid error code"));
}
Expand Down
11 changes: 6 additions & 5 deletions compiler/rustc_error_codes/src/error_codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,9 @@ E0790: include_str!("./error_codes/E0790.md"),
E0791: include_str!("./error_codes/E0791.md"),
E0792: include_str!("./error_codes/E0792.md"),
E0793: include_str!("./error_codes/E0793.md"),
;
}

// Undocumented removed error codes. Note that many removed error codes are documented.
Copy link
Member

@RalfJung RalfJung Aug 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can a removed error code be documented? I'm trying to remove an error code in #115277 and tidy complains that the error code exists but isn't used. If I remove the error code then tidy complains that I have to also remove the documentation. So documented removed codes seem to be impossible, in contrast to what the comment says?

This file lacks guidance for what to do when an error code is being removed. Someone should please decide that and document it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#115278 should help

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This list is used for --explain, so even if not emitted by rustc, it's normal to have them. So you never remove an entry unless it doesn't have an error code explanation.

// E0006, // merged with E0005
// E0008, // cannot bind by-move into a pattern guard
// E0019, // merged into E0015
Expand Down Expand Up @@ -570,7 +572,7 @@ E0793: include_str!("./error_codes/E0793.md"),
// E0246, // invalid recursive type
// E0247,
// E0248, // value used as a type, now reported earlier during resolution
// as E0412
// // as E0412
// E0249,
// E0257,
// E0258,
Expand Down Expand Up @@ -631,14 +633,14 @@ E0793: include_str!("./error_codes/E0793.md"),
// E0558, // replaced with a generic attribute input check
// E0563, // cannot determine a type for this `impl Trait` removed in 6383de15
// E0564, // only named lifetimes are allowed in `impl Trait`,
// but `{}` was found in the type `{}`
// // but `{}` was found in the type `{}`
// E0598, // lifetime of {} is too short to guarantee its contents can be...
// E0611, // merged into E0616
// E0612, // merged into E0609
// E0613, // Removed (merged with E0609)
// E0629, // missing 'feature' (rustc_const_unstable)
// E0630, // rustc_const_unstable attribute must be paired with stable/unstable
// attribute
// // attribute
// E0645, // trait aliases not finished
// E0694, // an unknown tool name found in scoped attributes
// E0702, // replaced with a generic attribute input check
Expand All @@ -647,4 +649,3 @@ E0793: include_str!("./error_codes/E0793.md"),
// E0721, // `await` keyword
// E0723, // unstable feature in `const` context
// E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`.
}
7 changes: 3 additions & 4 deletions compiler/rustc_error_codes/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
//! the goal being to make their maintenance easier.

macro_rules! register_diagnostics {
($($ecode:ident: $message:expr,)* ; $($code:ident,)*) => (
pub static DIAGNOSTICS: &[(&str, Option<&str>)] = &[
$( (stringify!($ecode), Some($message)), )*
$( (stringify!($code), None), )*
($($ecode:ident: $message:expr,)*) => (
pub static DIAGNOSTICS: &[(&str, &str)] = &[
$( (stringify!($ecode), $message), )*
];
)
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_errors/src/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ impl DiagnosticCode {
let je_result =
je.registry.as_ref().map(|registry| registry.try_find_description(&s)).unwrap();

DiagnosticCode { code: s, explanation: je_result.unwrap_or(None) }
DiagnosticCode { code: s, explanation: je_result.ok() }
})
}
}
4 changes: 1 addition & 3 deletions compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1477,9 +1477,7 @@ impl HandlerInner {
.emitted_diagnostic_codes
.iter()
.filter_map(|x| match &x {
DiagnosticId::Error(s)
if registry.try_find_description(s).map_or(false, |o| o.is_some()) =>
{
DiagnosticId::Error(s) if registry.try_find_description(s).is_ok() => {
Some(s.clone())
}
_ => None,
Expand Down
12 changes: 4 additions & 8 deletions compiler/rustc_errors/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,17 @@ pub struct InvalidErrorCode;

#[derive(Clone)]
pub struct Registry {
long_descriptions: FxHashMap<&'static str, Option<&'static str>>,
long_descriptions: FxHashMap<&'static str, &'static str>,
}

impl Registry {
pub fn new(long_descriptions: &[(&'static str, Option<&'static str>)]) -> Registry {
pub fn new(long_descriptions: &[(&'static str, &'static str)]) -> Registry {
Registry { long_descriptions: long_descriptions.iter().copied().collect() }
}

/// Returns `InvalidErrorCode` if the code requested does not exist in the
/// registry. Otherwise, returns an `Option` where `None` means the error
/// code is valid but has no extended information.
pub fn try_find_description(
&self,
code: &str,
) -> Result<Option<&'static str>, InvalidErrorCode> {
/// registry.
pub fn try_find_description(&self, code: &str) -> Result<&'static str, InvalidErrorCode> {
self.long_descriptions.get(code).copied().ok_or(InvalidErrorCode)
}
}
1 change: 1 addition & 0 deletions src/tools/error_index_generator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ edition = "2021"

[dependencies]
mdbook = { version = "0.4", default-features = false, features = ["search"] }
rustc_error_codes = { version = "0.0.0", path = "../../../compiler/rustc_error_codes" }

[[bin]]
name = "error_index_generator"
Expand Down
58 changes: 16 additions & 42 deletions src/tools/error_index_generator/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@

extern crate rustc_driver;

// We use the function we generate from `register_diagnostics!`.
use crate::error_codes::error_codes;

use std::env;
use std::error::Error;
use std::fs::{self, File};
Expand All @@ -17,22 +14,6 @@ use std::str::FromStr;
use mdbook::book::{parse_summary, BookItem, Chapter};
use mdbook::{Config, MDBook};

macro_rules! register_diagnostics {
($($error_code:ident: $message:expr,)+ ; $($undocumented:ident,)* ) => {
pub fn error_codes() -> Vec<(&'static str, Option<&'static str>)> {
let mut errors: Vec<(&str, Option<&str>)> = vec![
$((stringify!($error_code), Some($message)),)+
$((stringify!($undocumented), None),)*
];
errors.sort();
errors
}
}
}

#[path = "../../../compiler/rustc_error_codes/src/error_codes.rs"]
mod error_codes;

enum OutputFormat {
HTML,
Markdown,
Expand All @@ -55,11 +36,8 @@ fn render_markdown(output_path: &Path) -> Result<(), Box<dyn Error>> {

write!(output_file, "# Rust Compiler Error Index\n")?;

for (err_code, description) in error_codes().iter() {
match description {
Some(ref desc) => write!(output_file, "## {}\n{}\n", err_code, desc)?,
None => {}
}
for (err_code, description) in rustc_error_codes::DIAGNOSTICS.iter() {
write!(output_file, "## {}\n{}\n", err_code, description)?
}

Ok(())
Expand Down Expand Up @@ -105,27 +83,23 @@ This page lists all the error codes emitted by the Rust compiler.
"
);

let err_codes = error_codes();
let err_codes = rustc_error_codes::DIAGNOSTICS;
let mut chapters = Vec::with_capacity(err_codes.len());

for (err_code, explanation) in err_codes.iter() {
if let Some(explanation) = explanation {
introduction.push_str(&format!(" * [{0}](./{0}.html)\n", err_code));

let content = add_rust_attribute_on_codeblock(explanation);
chapters.push(BookItem::Chapter(Chapter {
name: err_code.to_string(),
content: format!("# Error code {}\n\n{}\n", err_code, content),
number: None,
sub_items: Vec::new(),
// We generate it into the `error_codes` folder.
path: Some(PathBuf::from(&format!("{}.html", err_code))),
source_path: None,
parent_names: Vec::new(),
}));
} else {
introduction.push_str(&format!(" * {}\n", err_code));
}
introduction.push_str(&format!(" * [{0}](./{0}.html)\n", err_code));

let content = add_rust_attribute_on_codeblock(explanation);
chapters.push(BookItem::Chapter(Chapter {
name: err_code.to_string(),
content: format!("# Error code {}\n\n{}\n", err_code, content),
number: None,
sub_items: Vec::new(),
// We generate it into the `error_codes` folder.
path: Some(PathBuf::from(&format!("{}.html", err_code))),
source_path: None,
parent_names: Vec::new(),
}));
}

let mut config = Config::from_str(include_str!("book_config.toml"))?;
Expand Down
24 changes: 3 additions & 21 deletions src/tools/tidy/src/error_codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub fn check(root_path: &Path, search_paths: &[&Path], verbose: bool, bad: &mut
let mut errors = Vec::new();

// Stage 1: create list
let error_codes = extract_error_codes(root_path, &mut errors, verbose);
let error_codes = extract_error_codes(root_path, &mut errors);
println!("Found {} error codes", error_codes.len());
println!("Highest error code: `{}`", error_codes.iter().max().unwrap());

Expand All @@ -65,18 +65,17 @@ pub fn check(root_path: &Path, search_paths: &[&Path], verbose: bool, bad: &mut
}

/// Stage 1: Parses a list of error codes from `error_codes.rs`.
fn extract_error_codes(root_path: &Path, errors: &mut Vec<String>, verbose: bool) -> Vec<String> {
fn extract_error_codes(root_path: &Path, errors: &mut Vec<String>) -> Vec<String> {
let path = root_path.join(Path::new(ERROR_CODES_PATH));
let file =
fs::read_to_string(&path).unwrap_or_else(|e| panic!("failed to read `{path:?}`: {e}"));

let mut error_codes = Vec::new();
let mut reached_undocumented_codes = false;

for line in file.lines() {
let line = line.trim();

if !reached_undocumented_codes && line.starts_with('E') {
if line.starts_with('E') {
let split_line = line.split_once(':');

// Extract the error code from the line, emitting a fatal error if it is not in a correct format.
Expand Down Expand Up @@ -111,23 +110,6 @@ fn extract_error_codes(root_path: &Path, errors: &mut Vec<String>, verbose: bool
}

error_codes.push(err_code);
} else if reached_undocumented_codes && line.starts_with('E') {
let err_code = match line.split_once(',') {
None => line,
Some((err_code, _)) => err_code,
}
.to_string();

verbose_print!(verbose, "warning: Error code `{}` is undocumented.", err_code);

if error_codes.contains(&err_code) {
errors.push(format!("Found duplicate error code: `{}`", err_code));
}

error_codes.push(err_code);
} else if line == ";" {
// Once we reach the undocumented error codes, adapt to different syntax.
reached_undocumented_codes = true;
}
}

Expand Down