mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 02:57:37 +00:00
Rollup merge of #100922 - GuillaumeGomez:rewrite-error-index, r=notriddle
Rewrite error index generator to greatly reduce the size of the pages Fixes https://github.com/rust-lang/rust/issues/100736. Instead of having all error codes in a same page (making the DOM way too big), I split the output into multiple files and generated a list of links (if there is an explanation) to the error codes' explanation into the already existing file. I also used this opportunity to greatly simplify the code. Instead of needing a `build.rs`, I simply imported the file we want and wrote the macro which generates a function containing everything we need. We just need to call it to get the error codes and their explanation (if any). Also, considering the implementations between markdown and HTML formats differed even further, the `Formatter` trait was becoming too problematic so I removed it too. You can test it [here](https://rustdoc.crud.net/imperio/rewrite-error-index/error-index.html). cc ``@jsha`` r? ``@notriddle``
This commit is contained in:
commit
fe1f1f1282
@ -343,7 +343,7 @@ pub trait StructuralEq {
|
||||
/// If you try to implement `Copy` on a struct or enum containing non-`Copy` data, you will get
|
||||
/// the error [E0204].
|
||||
///
|
||||
/// [E0204]: ../../error-index.html#E0204
|
||||
/// [E0204]: ../../error_codes/E0204.html
|
||||
///
|
||||
/// ## When *should* my type be `Copy`?
|
||||
///
|
||||
|
@ -156,7 +156,7 @@ pub trait Drop {
|
||||
/// handled by the compiler, but when using unsafe code, can sometimes occur
|
||||
/// unintentionally, particularly when using [`ptr::drop_in_place`].
|
||||
///
|
||||
/// [E0040]: ../../error-index.html#E0040
|
||||
/// [E0040]: ../../error_codes/E0040.html
|
||||
/// [`panic!`]: crate::panic!
|
||||
/// [`mem::drop`]: drop
|
||||
/// [`ptr::drop_in_place`]: crate::ptr::drop_in_place
|
||||
|
@ -1,31 +0,0 @@
|
||||
use std::path::PathBuf;
|
||||
use std::{env, fs};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
fn main() {
|
||||
// The src directory (we are in src/tools/error_index_generator)
|
||||
// Note that we could skip one of the .. but this ensures we at least loosely find the right
|
||||
// directory.
|
||||
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
|
||||
let error_codes_path = "../../../compiler/rustc_error_codes/src/error_codes.rs";
|
||||
|
||||
println!("cargo:rerun-if-changed={}", error_codes_path);
|
||||
let file = fs::read_to_string(error_codes_path)
|
||||
.unwrap()
|
||||
.replace(": include_str!(\"./error_codes/", ": include_str!(\"./");
|
||||
let contents = format!("(|| {{\n{}\n}})()", file);
|
||||
fs::write(&out_dir.join("all_error_codes.rs"), &contents).unwrap();
|
||||
|
||||
// We copy the md files as well to the target directory.
|
||||
for entry in WalkDir::new("../../../compiler/rustc_error_codes/src/error_codes") {
|
||||
let entry = entry.unwrap();
|
||||
match entry.path().extension() {
|
||||
Some(s) if s == "md" => {}
|
||||
_ => continue,
|
||||
}
|
||||
println!("cargo:rerun-if-changed={}", entry.path().to_str().unwrap());
|
||||
let md_content = fs::read_to_string(entry.path()).unwrap();
|
||||
fs::write(&out_dir.join(entry.file_name()), &md_content).unwrap();
|
||||
}
|
||||
}
|
@ -3,11 +3,11 @@
|
||||
extern crate rustc_driver;
|
||||
extern crate rustc_span;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::BTreeMap;
|
||||
use crate::error_codes::error_codes;
|
||||
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::fs::File;
|
||||
use std::fs::{create_dir_all, File};
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
@ -16,49 +16,86 @@ use rustc_span::edition::DEFAULT_EDITION;
|
||||
|
||||
use rustdoc::html::markdown::{ErrorCodes, HeadingOffset, IdMap, Markdown, Playground};
|
||||
|
||||
pub struct ErrorMetadata {
|
||||
pub description: Option<String>,
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Mapping from error codes to metadata that can be (de)serialized.
|
||||
pub type ErrorMetadataMap = BTreeMap<String, ErrorMetadata>;
|
||||
#[path = "../../../compiler/rustc_error_codes/src/error_codes.rs"]
|
||||
mod error_codes;
|
||||
|
||||
enum OutputFormat {
|
||||
HTML(HTMLFormatter),
|
||||
Markdown(MarkdownFormatter),
|
||||
Markdown,
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl OutputFormat {
|
||||
fn from(format: &str, resource_suffix: &str) -> OutputFormat {
|
||||
match &*format.to_lowercase() {
|
||||
"html" => OutputFormat::HTML(HTMLFormatter(
|
||||
RefCell::new(IdMap::new()),
|
||||
resource_suffix.to_owned(),
|
||||
)),
|
||||
"markdown" => OutputFormat::Markdown(MarkdownFormatter),
|
||||
"html" => OutputFormat::HTML(HTMLFormatter(resource_suffix.to_owned())),
|
||||
"markdown" => OutputFormat::Markdown,
|
||||
s => OutputFormat::Unknown(s.to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait Formatter {
|
||||
fn header(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>>;
|
||||
fn title(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>>;
|
||||
fn error_code_block(
|
||||
struct HTMLFormatter(String);
|
||||
|
||||
impl HTMLFormatter {
|
||||
fn create_error_code_file(
|
||||
&self,
|
||||
err_code: &str,
|
||||
explanation: &str,
|
||||
parent_dir: &Path,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let mut output_file = File::create(parent_dir.join(err_code).with_extension("html"))?;
|
||||
|
||||
self.header(&mut output_file, "../", "")?;
|
||||
self.title(&mut output_file, &format!("Error code {}", err_code))?;
|
||||
|
||||
let mut id_map = IdMap::new();
|
||||
let playground =
|
||||
Playground { crate_name: None, url: String::from("https://play.rust-lang.org/") };
|
||||
write!(
|
||||
output_file,
|
||||
"{}",
|
||||
Markdown {
|
||||
content: explanation,
|
||||
links: &[],
|
||||
ids: &mut id_map,
|
||||
error_codes: ErrorCodes::Yes,
|
||||
edition: DEFAULT_EDITION,
|
||||
playground: &Some(playground),
|
||||
heading_offset: HeadingOffset::H1,
|
||||
}
|
||||
.into_string()
|
||||
)?;
|
||||
write!(
|
||||
output_file,
|
||||
"<p>\
|
||||
<a style='text-align: center;display: block;width: 100%;' \
|
||||
href='../error-index.html'>Back to list of error codes</a>\
|
||||
</p>",
|
||||
)?;
|
||||
|
||||
self.footer(&mut output_file)
|
||||
}
|
||||
|
||||
fn header(
|
||||
&self,
|
||||
output: &mut dyn Write,
|
||||
info: &ErrorMetadata,
|
||||
err_code: &str,
|
||||
) -> Result<(), Box<dyn Error>>;
|
||||
fn footer(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>>;
|
||||
}
|
||||
|
||||
struct HTMLFormatter(RefCell<IdMap>, String);
|
||||
struct MarkdownFormatter;
|
||||
|
||||
impl Formatter for HTMLFormatter {
|
||||
fn header(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
|
||||
extra_path: &str,
|
||||
extra: &str,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
write!(
|
||||
output,
|
||||
r##"<!DOCTYPE html>
|
||||
@ -67,188 +104,106 @@ impl Formatter for HTMLFormatter {
|
||||
<title>Rust Compiler Error Index</title>
|
||||
<meta charset="utf-8">
|
||||
<!-- Include rust.css after light.css so its rules take priority. -->
|
||||
<link rel="stylesheet" type="text/css" href="rustdoc{suffix}.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="light{suffix}.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="rust.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="{extra_path}rustdoc{suffix}.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="{extra_path}light{suffix}.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="{extra_path}rust.css"/>
|
||||
<style>
|
||||
.error-undescribed {{
|
||||
display: none;
|
||||
}}
|
||||
</style>
|
||||
</style>{extra}
|
||||
</head>
|
||||
<body>
|
||||
"##,
|
||||
suffix = self.1
|
||||
suffix = self.0,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn title(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
|
||||
write!(output, "<h1>Rust Compiler Error Index</h1>\n")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn error_code_block(
|
||||
&self,
|
||||
output: &mut dyn Write,
|
||||
info: &ErrorMetadata,
|
||||
err_code: &str,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
// Enclose each error in a div so they can be shown/hidden en masse.
|
||||
let desc_desc = match info.description {
|
||||
Some(_) => "error-described",
|
||||
None => "error-undescribed",
|
||||
};
|
||||
write!(output, "<div class=\"{}\">", desc_desc)?;
|
||||
|
||||
// Error title (with self-link).
|
||||
write!(
|
||||
output,
|
||||
"<h2 id=\"{0}\" class=\"section-header\"><a href=\"#{0}\">{0}</a></h2>\n",
|
||||
err_code
|
||||
)?;
|
||||
|
||||
// Description rendered as markdown.
|
||||
match info.description {
|
||||
Some(ref desc) => {
|
||||
let mut id_map = self.0.borrow_mut();
|
||||
let playground = Playground {
|
||||
crate_name: None,
|
||||
url: String::from("https://play.rust-lang.org/"),
|
||||
};
|
||||
write!(
|
||||
output,
|
||||
"{}",
|
||||
Markdown {
|
||||
content: desc,
|
||||
links: &[],
|
||||
ids: &mut id_map,
|
||||
error_codes: ErrorCodes::Yes,
|
||||
edition: DEFAULT_EDITION,
|
||||
playground: &Some(playground),
|
||||
heading_offset: HeadingOffset::H1,
|
||||
}
|
||||
.into_string()
|
||||
)?
|
||||
}
|
||||
None => write!(output, "<p>No description.</p>\n")?,
|
||||
}
|
||||
|
||||
write!(output, "</div>\n")?;
|
||||
fn title(&self, output: &mut dyn Write, title: &str) -> Result<(), Box<dyn Error>> {
|
||||
write!(output, "<h1>{}</h1>\n", title)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn footer(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
|
||||
write!(
|
||||
output,
|
||||
r##"<script>
|
||||
function onEach(arr, func) {{
|
||||
if (arr && arr.length > 0 && func) {{
|
||||
var length = arr.length;
|
||||
var i;
|
||||
for (i = 0; i < length; ++i) {{
|
||||
if (func(arr[i])) {{
|
||||
return true;
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
return false;
|
||||
}}
|
||||
|
||||
function onEachLazy(lazyArray, func) {{
|
||||
return onEach(
|
||||
Array.prototype.slice.call(lazyArray),
|
||||
func);
|
||||
}}
|
||||
|
||||
function hasClass(elem, className) {{
|
||||
return elem && elem.classList && elem.classList.contains(className);
|
||||
}}
|
||||
|
||||
onEachLazy(document.getElementsByClassName('rust-example-rendered'), function(e) {{
|
||||
if (hasClass(e, 'compile_fail')) {{
|
||||
e.addEventListener("mouseover", function(event) {{
|
||||
e.parentElement.previousElementSibling.childNodes[0].style.color = '#f00';
|
||||
}});
|
||||
e.addEventListener("mouseout", function(event) {{
|
||||
e.parentElement.previousElementSibling.childNodes[0].style.color = '';
|
||||
}});
|
||||
}} else if (hasClass(e, 'ignore')) {{
|
||||
e.addEventListener("mouseover", function(event) {{
|
||||
e.parentElement.previousElementSibling.childNodes[0].style.color = '#ff9200';
|
||||
}});
|
||||
e.addEventListener("mouseout", function(event) {{
|
||||
e.parentElement.previousElementSibling.childNodes[0].style.color = '';
|
||||
}});
|
||||
}}
|
||||
}});
|
||||
</script>
|
||||
</body>
|
||||
</html>"##
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Formatter for MarkdownFormatter {
|
||||
#[allow(unused_variables)]
|
||||
fn header(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn title(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
|
||||
write!(output, "# Rust Compiler Error Index\n")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn error_code_block(
|
||||
&self,
|
||||
output: &mut dyn Write,
|
||||
info: &ErrorMetadata,
|
||||
err_code: &str,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
Ok(match info.description {
|
||||
Some(ref desc) => write!(output, "## {}\n{}\n", err_code, desc)?,
|
||||
None => (),
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn footer(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
|
||||
write!(output, "</body></html>")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Output an HTML page for the errors in `err_map` to `output_path`.
|
||||
fn render_error_page<T: Formatter>(
|
||||
err_map: &ErrorMetadataMap,
|
||||
output_path: &Path,
|
||||
formatter: T,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
fn render_markdown(output_path: &Path) -> Result<(), Box<dyn Error>> {
|
||||
let mut output_file = File::create(output_path)?;
|
||||
|
||||
formatter.header(&mut output_file)?;
|
||||
formatter.title(&mut output_file)?;
|
||||
write!(output_file, "# Rust Compiler Error Index\n")?;
|
||||
|
||||
for (err_code, info) in err_map {
|
||||
formatter.error_code_block(&mut output_file, info, err_code)?;
|
||||
for (err_code, description) in error_codes().iter() {
|
||||
match description {
|
||||
Some(ref desc) => write!(output_file, "## {}\n{}\n", err_code, desc)?,
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render_html(output_path: &Path, formatter: HTMLFormatter) -> Result<(), Box<dyn Error>> {
|
||||
let mut output_file = File::create(output_path)?;
|
||||
|
||||
let error_codes_dir = "error_codes";
|
||||
|
||||
let parent = output_path.parent().expect("There should have a parent").join(error_codes_dir);
|
||||
|
||||
if !parent.exists() {
|
||||
create_dir_all(&parent)?;
|
||||
}
|
||||
|
||||
formatter.header(
|
||||
&mut output_file,
|
||||
"",
|
||||
&format!(
|
||||
r#"<script>(function() {{
|
||||
if (window.location.hash) {{
|
||||
let code = window.location.hash.replace(/^#/, '');
|
||||
// We have to make sure this pattern matches to avoid inadvertently creating an
|
||||
// open redirect.
|
||||
if (/^E[0-9]+$/.test(code)) {{
|
||||
window.location = './{error_codes_dir}/' + code + '.html';
|
||||
}}
|
||||
}}
|
||||
}})()</script>"#
|
||||
),
|
||||
)?;
|
||||
formatter.title(&mut output_file, "Rust Compiler Error Index")?;
|
||||
|
||||
write!(
|
||||
output_file,
|
||||
"<p>This page lists all the error codes emitted by the Rust compiler. If you want a full \
|
||||
explanation on an error code, click on it.</p>\
|
||||
<ul>",
|
||||
)?;
|
||||
for (err_code, explanation) in error_codes().iter() {
|
||||
if let Some(explanation) = explanation {
|
||||
write!(
|
||||
output_file,
|
||||
"<li><a href='./{0}/{1}.html'>{1}</a></li>",
|
||||
error_codes_dir, err_code
|
||||
)?;
|
||||
formatter.create_error_code_file(err_code, explanation, &parent)?;
|
||||
} else {
|
||||
write!(output_file, "<li>{}</li>", err_code)?;
|
||||
}
|
||||
}
|
||||
write!(output_file, "</ul>")?;
|
||||
formatter.footer(&mut output_file)
|
||||
}
|
||||
|
||||
fn main_with_result(format: OutputFormat, dst: &Path) -> Result<(), Box<dyn Error>> {
|
||||
let long_codes = register_all();
|
||||
let mut err_map = BTreeMap::new();
|
||||
for (code, desc) in long_codes {
|
||||
err_map.insert(code.to_string(), ErrorMetadata { description: desc.map(String::from) });
|
||||
}
|
||||
match format {
|
||||
OutputFormat::Unknown(s) => panic!("Unknown output format: {}", s),
|
||||
OutputFormat::HTML(h) => render_error_page(&err_map, dst, h)?,
|
||||
OutputFormat::Markdown(m) => render_error_page(&err_map, dst, m)?,
|
||||
OutputFormat::HTML(h) => render_html(dst, h),
|
||||
OutputFormat::Markdown => render_markdown(dst),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_args() -> (OutputFormat, PathBuf) {
|
||||
@ -261,7 +216,7 @@ fn parse_args() -> (OutputFormat, PathBuf) {
|
||||
.unwrap_or(OutputFormat::from("html", &resource_suffix));
|
||||
let dst = dst.map(PathBuf::from).unwrap_or_else(|| match format {
|
||||
OutputFormat::HTML(..) => PathBuf::from("doc/error-index.html"),
|
||||
OutputFormat::Markdown(..) => PathBuf::from("doc/error-index.md"),
|
||||
OutputFormat::Markdown => PathBuf::from("doc/error-index.md"),
|
||||
OutputFormat::Unknown(..) => PathBuf::from("<nul>"),
|
||||
});
|
||||
(format, dst)
|
||||
@ -276,23 +231,3 @@ fn main() {
|
||||
panic!("{}", e.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
fn register_all() -> Vec<(&'static str, Option<&'static str>)> {
|
||||
let mut long_codes: Vec<(&'static str, Option<&'static str>)> = Vec::new();
|
||||
macro_rules! register_diagnostics {
|
||||
($($ecode:ident: $message:expr,)* ; $($code:ident,)*) => (
|
||||
$(
|
||||
{long_codes.extend([
|
||||
(stringify!($ecode), Some($message)),
|
||||
].iter());}
|
||||
)*
|
||||
$(
|
||||
{long_codes.extend([
|
||||
stringify!($code),
|
||||
].iter().cloned().map(|s| (s, None)).collect::<Vec<_>>());}
|
||||
)*
|
||||
)
|
||||
}
|
||||
include!(concat!(env!("OUT_DIR"), "/all_error_codes.rs"));
|
||||
long_codes
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user