about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume.gomez@huawei.com>2022-08-23 18:27:18 +0200
committerGuillaume Gomez <guillaume.gomez@huawei.com>2022-08-23 18:27:18 +0200
commit8ad36c45f8116c80cda018e63b9db6862e73a87d (patch)
tree73f1d1dd225d856745f3c2b9b58303c47e42854c
parenta9bb589cd678e034d194193fa892942315b10e2a (diff)
downloadrust-8ad36c45f8116c80cda018e63b9db6862e73a87d.tar.gz
rust-8ad36c45f8116c80cda018e63b9db6862e73a87d.zip
Rewrite error index generator to greatly reduce the size of the pages
-rw-r--r--src/tools/error_index_generator/build.rs31
-rw-r--r--src/tools/error_index_generator/main.rs297
2 files changed, 106 insertions, 222 deletions
diff --git a/src/tools/error_index_generator/build.rs b/src/tools/error_index_generator/build.rs
deleted file mode 100644
index 70b00b36cf1..00000000000
--- a/src/tools/error_index_generator/build.rs
+++ /dev/null
@@ -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();
-    }
-}
diff --git a/src/tools/error_index_generator/main.rs b/src/tools/error_index_generator/main.rs
index 1ce02e48c05..22243f9fc9d 100644
--- a/src/tools/error_index_generator/main.rs
+++ b/src/tools/error_index_generator/main.rs
@@ -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,81 @@ 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,
-        output: &mut dyn Write,
-        info: &ErrorMetadata,
         err_code: &str,
-    ) -> Result<(), Box<dyn Error>>;
-    fn footer(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>>;
-}
+        explanation: &str,
+        parent_dir: &Path,
+    ) -> Result<(), Box<dyn Error>> {
+        let mut output_file = File::create(parent_dir.join(err_code).with_extension("html"))?;
 
-struct HTMLFormatter(RefCell<IdMap>, String);
-struct MarkdownFormatter;
+        self.header(&mut output_file, "../")?;
+        self.title(&mut output_file, &format!("Error code {}", err_code))?;
 
-impl Formatter for HTMLFormatter {
-    fn header(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
+        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, extra: &str) -> Result<(), Box<dyn Error>> {
         write!(
             output,
             r##"<!DOCTYPE html>
@@ -67,9 +99,9 @@ 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}rustdoc{suffix}.css"/>
+<link rel="stylesheet" type="text/css" href="{extra}light{suffix}.css"/>
+<link rel="stylesheet" type="text/css" href="{extra}rust.css"/>
 <style>
 .error-undescribed {{
     display: none;
@@ -78,177 +110,80 @@ impl Formatter for HTMLFormatter {
 </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>"##
-        )?;
+        write!(output, "</body></html>")?;
         Ok(())
     }
 }
 
-impl Formatter for MarkdownFormatter {
-    #[allow(unused_variables)]
-    fn header(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
-        Ok(())
-    }
+/// Output an HTML page for the errors in `err_map` to `output_path`.
+fn render_markdown(output_path: &Path) -> Result<(), Box<dyn Error>> {
+    let mut output_file = File::create(output_path)?;
 
-    fn title(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
-        write!(output, "# Rust Compiler Error Index\n")?;
-        Ok(())
-    }
+    write!(output_file, "# Rust Compiler Error Index\n")?;
 
-    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 => (),
-        })
+    for (err_code, description) in error_codes().iter() {
+        match description {
+            Some(ref desc) => write!(output_file, "## {}\n{}\n", err_code, desc)?,
+            None => {}
+        }
     }
 
-    #[allow(unused_variables)]
-    fn footer(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
-        Ok(())
-    }
+    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_html(output_path: &Path, formatter: HTMLFormatter) -> Result<(), Box<dyn Error>> {
     let mut output_file = File::create(output_path)?;
 
-    formatter.header(&mut output_file)?;
-    formatter.title(&mut output_file)?;
+    let error_codes_dir = "error_codes";
+
+    let parent = output_path.parent().expect("There should have a parent").join(error_codes_dir);
 
-    for (err_code, info) in err_map {
-        formatter.error_code_block(&mut output_file, info, err_code)?;
+    if !parent.exists() {
+        create_dir_all(&parent)?;
     }
 
+    formatter.header(&mut output_file, "")?;
+    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 +196,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 +211,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
-}