Rollup merge of #108482 - Ezrashaw:force-error-docs, r=GuillaumeGomez

statically guarantee that current error codes are documented

Closes #61137 (that's right!)

Pretty simple refactor (often just a change from `Result<Option<&str>>` to `Result<&str>`)

r? `@GuillaumeGomez` (could you specially look at 53044158ef? I believe you wrote that in the first place, just want to make sure you're happy with the change)
This commit is contained in:
Matthias Krüger 2023-02-26 12:05:00 +01:00 committed by GitHub
commit edd27cf4ca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 36 additions and 87 deletions

View file

@ -1445,6 +1445,7 @@ name = "error_index_generator"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"mdbook", "mdbook",
"rustc_error_codes",
] ]
[[package]] [[package]]

View file

@ -485,7 +485,7 @@ fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) {
let normalised = let normalised =
if upper_cased_code.starts_with('E') { upper_cased_code } else { format!("E{code:0>4}") }; if upper_cased_code.starts_with('E') { upper_cased_code } else { format!("E{code:0>4}") };
match registry.try_find_description(&normalised) { match registry.try_find_description(&normalised) {
Ok(Some(description)) => { Ok(description) => {
let mut is_in_code_block = false; let mut is_in_code_block = false;
let mut text = String::new(); let mut text = String::new();
// Slice off the leading newline and print. // Slice off the leading newline and print.
@ -509,9 +509,6 @@ fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) {
print!("{text}"); print!("{text}");
} }
} }
Ok(None) => {
early_error(output, &format!("no extended information for {code}"));
}
Err(InvalidErrorCode) => { Err(InvalidErrorCode) => {
early_error(output, &format!("{code} is not a valid error code")); early_error(output, &format!("{code} is not a valid error code"));
} }

View file

@ -513,7 +513,9 @@ E0790: include_str!("./error_codes/E0790.md"),
E0791: include_str!("./error_codes/E0791.md"), E0791: include_str!("./error_codes/E0791.md"),
E0792: include_str!("./error_codes/E0792.md"), E0792: include_str!("./error_codes/E0792.md"),
E0793: include_str!("./error_codes/E0793.md"), E0793: include_str!("./error_codes/E0793.md"),
; }
// Undocumented removed error codes. Note that many removed error codes are documented.
// E0006, // merged with E0005 // E0006, // merged with E0005
// E0008, // cannot bind by-move into a pattern guard // E0008, // cannot bind by-move into a pattern guard
// E0019, // merged into E0015 // E0019, // merged into E0015
@ -570,7 +572,7 @@ E0793: include_str!("./error_codes/E0793.md"),
// E0246, // invalid recursive type // E0246, // invalid recursive type
// E0247, // E0247,
// E0248, // value used as a type, now reported earlier during resolution // E0248, // value used as a type, now reported earlier during resolution
// as E0412 // // as E0412
// E0249, // E0249,
// E0257, // E0257,
// E0258, // E0258,
@ -631,14 +633,14 @@ E0793: include_str!("./error_codes/E0793.md"),
// E0558, // replaced with a generic attribute input check // E0558, // replaced with a generic attribute input check
// E0563, // cannot determine a type for this `impl Trait` removed in 6383de15 // E0563, // cannot determine a type for this `impl Trait` removed in 6383de15
// E0564, // only named lifetimes are allowed in `impl Trait`, // 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... // E0598, // lifetime of {} is too short to guarantee its contents can be...
// E0611, // merged into E0616 // E0611, // merged into E0616
// E0612, // merged into E0609 // E0612, // merged into E0609
// E0613, // Removed (merged with E0609) // E0613, // Removed (merged with E0609)
// E0629, // missing 'feature' (rustc_const_unstable) // E0629, // missing 'feature' (rustc_const_unstable)
// E0630, // rustc_const_unstable attribute must be paired with stable/unstable // E0630, // rustc_const_unstable attribute must be paired with stable/unstable
// attribute // // attribute
// E0645, // trait aliases not finished // E0645, // trait aliases not finished
// E0694, // an unknown tool name found in scoped attributes // E0694, // an unknown tool name found in scoped attributes
// E0702, // replaced with a generic attribute input check // E0702, // replaced with a generic attribute input check
@ -647,4 +649,3 @@ E0793: include_str!("./error_codes/E0793.md"),
// E0721, // `await` keyword // E0721, // `await` keyword
// E0723, // unstable feature in `const` context // E0723, // unstable feature in `const` context
// E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`. // E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`.
}

View file

@ -5,10 +5,9 @@
//! the goal being to make their maintenance easier. //! the goal being to make their maintenance easier.
macro_rules! register_diagnostics { macro_rules! register_diagnostics {
($($ecode:ident: $message:expr,)* ; $($code:ident,)*) => ( ($($ecode:ident: $message:expr,)*) => (
pub static DIAGNOSTICS: &[(&str, Option<&str>)] = &[ pub static DIAGNOSTICS: &[(&str, &str)] = &[
$( (stringify!($ecode), Some($message)), )* $( (stringify!($ecode), $message), )*
$( (stringify!($code), None), )*
]; ];
) )
} }

View file

@ -580,7 +580,7 @@ impl DiagnosticCode {
let je_result = let je_result =
je.registry.as_ref().map(|registry| registry.try_find_description(&s)).unwrap(); 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() }
}) })
} }
} }

View file

@ -1477,9 +1477,7 @@ impl HandlerInner {
.emitted_diagnostic_codes .emitted_diagnostic_codes
.iter() .iter()
.filter_map(|x| match &x { .filter_map(|x| match &x {
DiagnosticId::Error(s) DiagnosticId::Error(s) if registry.try_find_description(s).is_ok() => {
if registry.try_find_description(s).map_or(false, |o| o.is_some()) =>
{
Some(s.clone()) Some(s.clone())
} }
_ => None, _ => None,

View file

@ -5,21 +5,17 @@ pub struct InvalidErrorCode;
#[derive(Clone)] #[derive(Clone)]
pub struct Registry { pub struct Registry {
long_descriptions: FxHashMap<&'static str, Option<&'static str>>, long_descriptions: FxHashMap<&'static str, &'static str>,
} }
impl Registry { 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() } Registry { long_descriptions: long_descriptions.iter().copied().collect() }
} }
/// Returns `InvalidErrorCode` if the code requested does not exist in the /// Returns `InvalidErrorCode` if the code requested does not exist in the
/// registry. Otherwise, returns an `Option` where `None` means the error /// registry.
/// code is valid but has no extended information. pub fn try_find_description(&self, code: &str) -> Result<&'static str, InvalidErrorCode> {
pub fn try_find_description(
&self,
code: &str,
) -> Result<Option<&'static str>, InvalidErrorCode> {
self.long_descriptions.get(code).copied().ok_or(InvalidErrorCode) self.long_descriptions.get(code).copied().ok_or(InvalidErrorCode)
} }
} }

View file

@ -5,6 +5,7 @@ edition = "2021"
[dependencies] [dependencies]
mdbook = { version = "0.4", default-features = false, features = ["search"] } mdbook = { version = "0.4", default-features = false, features = ["search"] }
rustc_error_codes = { version = "0.0.0", path = "../../../compiler/rustc_error_codes" }
[[bin]] [[bin]]
name = "error_index_generator" name = "error_index_generator"

View file

@ -2,9 +2,6 @@
extern crate rustc_driver; extern crate rustc_driver;
// We use the function we generate from `register_diagnostics!`.
use crate::error_codes::error_codes;
use std::env; use std::env;
use std::error::Error; use std::error::Error;
use std::fs::{self, File}; use std::fs::{self, File};
@ -17,22 +14,6 @@ use std::str::FromStr;
use mdbook::book::{parse_summary, BookItem, Chapter}; use mdbook::book::{parse_summary, BookItem, Chapter};
use mdbook::{Config, MDBook}; 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 { enum OutputFormat {
HTML, HTML,
Markdown, Markdown,
@ -55,11 +36,8 @@ fn render_markdown(output_path: &Path) -> Result<(), Box<dyn Error>> {
write!(output_file, "# Rust Compiler Error Index\n")?; write!(output_file, "# Rust Compiler Error Index\n")?;
for (err_code, description) in error_codes().iter() { for (err_code, description) in rustc_error_codes::DIAGNOSTICS.iter() {
match description { write!(output_file, "## {}\n{}\n", err_code, description)?
Some(ref desc) => write!(output_file, "## {}\n{}\n", err_code, desc)?,
None => {}
}
} }
Ok(()) Ok(())
@ -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()); let mut chapters = Vec::with_capacity(err_codes.len());
for (err_code, explanation) in err_codes.iter() { for (err_code, explanation) in err_codes.iter() {
if let Some(explanation) = explanation { introduction.push_str(&format!(" * [{0}](./{0}.html)\n", err_code));
introduction.push_str(&format!(" * [{0}](./{0}.html)\n", err_code));
let content = add_rust_attribute_on_codeblock(explanation); let content = add_rust_attribute_on_codeblock(explanation);
chapters.push(BookItem::Chapter(Chapter { chapters.push(BookItem::Chapter(Chapter {
name: err_code.to_string(), name: err_code.to_string(),
content: format!("# Error code {}\n\n{}\n", err_code, content), content: format!("# Error code {}\n\n{}\n", err_code, content),
number: None, number: None,
sub_items: Vec::new(), sub_items: Vec::new(),
// We generate it into the `error_codes` folder. // We generate it into the `error_codes` folder.
path: Some(PathBuf::from(&format!("{}.html", err_code))), path: Some(PathBuf::from(&format!("{}.html", err_code))),
source_path: None, source_path: None,
parent_names: Vec::new(), parent_names: Vec::new(),
})); }));
} else {
introduction.push_str(&format!(" * {}\n", err_code));
}
} }
let mut config = Config::from_str(include_str!("book_config.toml"))?; let mut config = Config::from_str(include_str!("book_config.toml"))?;

View file

@ -45,7 +45,7 @@ pub fn check(root_path: &Path, search_paths: &[&Path], verbose: bool, bad: &mut
let mut errors = Vec::new(); let mut errors = Vec::new();
// Stage 1: create list // 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!("Found {} error codes", error_codes.len());
println!("Highest error code: `{}`", error_codes.iter().max().unwrap()); println!("Highest error code: `{}`", error_codes.iter().max().unwrap());
@ -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`. /// 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 path = root_path.join(Path::new(ERROR_CODES_PATH));
let file = let file =
fs::read_to_string(&path).unwrap_or_else(|e| panic!("failed to read `{path:?}`: {e}")); fs::read_to_string(&path).unwrap_or_else(|e| panic!("failed to read `{path:?}`: {e}"));
let mut error_codes = Vec::new(); let mut error_codes = Vec::new();
let mut reached_undocumented_codes = false;
for line in file.lines() { for line in file.lines() {
let line = line.trim(); let line = line.trim();
if !reached_undocumented_codes && line.starts_with('E') { if line.starts_with('E') {
let split_line = line.split_once(':'); 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. // Extract the error code from the line, emitting a fatal error if it is not in a correct format.
@ -111,23 +110,6 @@ fn extract_error_codes(root_path: &Path, errors: &mut Vec<String>, verbose: bool
} }
error_codes.push(err_code); 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;
} }
} }