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 53044158eff0d64673a6100f701c57b484232aca? 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"
dependencies = [
"mdbook",
"rustc_error_codes",
]
[[package]]

View File

@ -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.
@ -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"));
}

View File

@ -513,7 +513,9 @@
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.
// E0006, // merged with E0005
// E0008, // cannot bind by-move into a pattern guard
// E0019, // merged into E0015
@ -570,7 +572,7 @@
// E0246, // invalid recursive type
// E0247,
// E0248, // value used as a type, now reported earlier during resolution
// as E0412
// // as E0412
// E0249,
// E0257,
// E0258,
@ -631,14 +633,14 @@
// 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
@ -647,4 +649,3 @@
// E0721, // `await` keyword
// E0723, // unstable feature in `const` context
// 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.
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), )*
];
)
}

View File

@ -580,7 +580,7 @@ fn map_opt_string(s: Option<DiagnosticId>, je: &JsonEmitter) -> Option<Diagnosti
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() }
})
}
}

View File

@ -1477,9 +1477,7 @@ fn print_error_count(&mut self, registry: &Registry) {
.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,

View File

@ -5,21 +5,17 @@
#[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)
}
}

View File

@ -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"

View File

@ -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};
@ -17,22 +14,6 @@
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,
@ -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(())
@ -105,27 +83,23 @@ fn render_html(output_path: &Path) -> Result<(), Box<dyn Error>> {
"
);
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));
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));
}
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"))?;

View File

@ -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());
@ -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.
@ -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;
}
}