summaryrefslogtreecommitdiffstats
path: root/src/tools/error_index_generator/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/error_index_generator/main.rs')
-rw-r--r--src/tools/error_index_generator/main.rs377
1 files changed, 142 insertions, 235 deletions
diff --git a/src/tools/error_index_generator/main.rs b/src/tools/error_index_generator/main.rs
index 1ce02e48c..1bde8e007 100644
--- a/src/tools/error_index_generator/main.rs
+++ b/src/tools/error_index_generator/main.rs
@@ -1,267 +1,195 @@
#![feature(rustc_private)]
extern crate rustc_driver;
-extern crate rustc_span;
-use std::cell::RefCell;
-use std::collections::BTreeMap;
+// 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::File;
+use std::fs::{self, File};
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
-use rustc_span::edition::DEFAULT_EDITION;
+use std::str::FromStr;
-use rustdoc::html::markdown::{ErrorCodes, HeadingOffset, IdMap, Markdown, Playground};
+use mdbook::book::{parse_summary, BookItem, Chapter};
+use mdbook::{Config, MDBook};
-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),
+ HTML,
+ Markdown,
Unknown(String),
}
impl OutputFormat {
- fn from(format: &str, resource_suffix: &str) -> OutputFormat {
+ fn from(format: &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,
+ "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(
- &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;
+/// 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)?;
-impl Formatter for HTMLFormatter {
- fn header(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
- write!(
- output,
- r##"<!DOCTYPE html>
-<html>
-<head>
-<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"/>
-<style>
-.error-undescribed {{
- display: none;
-}}
-</style>
-</head>
-<body>
-"##,
- suffix = self.1
- )?;
- Ok(())
- }
+ write!(output_file, "# Rust Compiler Error Index\n")?;
- fn title(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
- write!(output, "<h1>Rust Compiler Error Index</h1>\n")?;
- Ok(())
+ for (err_code, description) in error_codes().iter() {
+ match description {
+ Some(ref desc) => write!(output_file, "## {}\n{}\n", err_code, desc)?,
+ None => {}
+ }
}
- 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
- )?;
+ Ok(())
+}
- // 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,
+// By default, mdbook doesn't consider code blocks as Rust ones contrary to rustdoc so we have
+// to manually add `rust` attribute whenever needed.
+fn add_rust_attribute_on_codeblock(explanation: &str) -> String {
+ // Very hacky way to add the rust attribute on all code blocks.
+ let mut skip = true;
+ explanation.split("\n```").fold(String::new(), |mut acc, part| {
+ if !acc.is_empty() {
+ acc.push_str("\n```");
+ }
+ if !skip {
+ if let Some(attrs) = part.split('\n').next() {
+ if !attrs.contains("rust")
+ && (attrs.is_empty()
+ || attrs.contains("compile_fail")
+ || attrs.contains("ignore")
+ || attrs.contains("edition"))
+ {
+ if !attrs.is_empty() {
+ acc.push_str("rust,");
+ } else {
+ acc.push_str("rust");
}
- .into_string()
- )?
+ }
}
- None => write!(output, "<p>No description.</p>\n")?,
}
-
- write!(output, "</div>\n")?;
- 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(())
- }
+ skip = !skip;
+ acc.push_str(part);
+ acc
+ })
}
-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>> {
- Ok(())
+fn render_html(output_path: &Path) -> Result<(), Box<dyn Error>> {
+ let mut introduction = format!(
+ "<script src='redirect.js'></script>
+# Rust error codes index
+
+This page lists all the error codes emitted by the Rust compiler.
+
+"
+ );
+
+ let err_codes = error_codes();
+ 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));
+ }
}
-}
-
-/// 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>> {
- let mut output_file = File::create(output_path)?;
-
- formatter.header(&mut output_file)?;
- formatter.title(&mut output_file)?;
- for (err_code, info) in err_map {
- formatter.error_code_block(&mut output_file, info, err_code)?;
- }
+ let mut config = Config::from_str(include_str!("book_config.toml"))?;
+ config.build.build_dir = output_path.join("error_codes").to_path_buf();
+ let mut book = MDBook::load_with_config_and_summary(
+ env!("CARGO_MANIFEST_DIR"),
+ config,
+ parse_summary("")?,
+ )?;
+ let chapter = Chapter {
+ name: "Rust error codes index".to_owned(),
+ content: introduction,
+ number: None,
+ sub_items: chapters,
+ // Very important: this file is named as `error-index.html` and not `index.html`!
+ path: Some(PathBuf::from("error-index.html")),
+ source_path: None,
+ parent_names: Vec::new(),
+ };
+ book.book.sections.push(BookItem::Chapter(chapter));
+ book.build()?;
+
+ // We can't put this content into another file, otherwise `mbdbook` will also put it into the
+ // output directory, making a duplicate.
+ fs::write(
+ output_path.join("error-index.html"),
+ r#"<!DOCTYPE html>
+<html>
+ <head>
+ <title>Rust error codes index - Error codes index</title>
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
+ <meta name="description" content="Book listing all Rust error codes">
+ <script src="error_codes/redirect.js"></script>
+ </head>
+ <body>
+ <div>If you are not automatically redirected to the error code index, please <a id="index-link" href="./error_codes/error-index.html">here</a>.
+ <script>document.getElementById("index-link").click()</script>
+ </body>
+</html>"#,
+ )?;
+
+ // No need for a 404 file, it's already handled by the server.
+ fs::remove_file(output_path.join("error_codes/404.html"))?;
- formatter.footer(&mut output_file)
+ Ok(())
}
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 => render_html(dst),
+ OutputFormat::Markdown => render_markdown(dst),
}
- Ok(())
}
fn parse_args() -> (OutputFormat, PathBuf) {
let mut args = env::args().skip(1);
let format = args.next();
let dst = args.next();
- let resource_suffix = args.next().unwrap_or_else(String::new);
- let format = format
- .map(|a| OutputFormat::from(&a, &resource_suffix))
- .unwrap_or(OutputFormat::from("html", &resource_suffix));
+ let format = format.map(|a| OutputFormat::from(&a)).unwrap_or(OutputFormat::from("html"));
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::HTML => PathBuf::from("doc"),
+ OutputFormat::Markdown => PathBuf::from("doc/error-index.md"),
OutputFormat::Unknown(..) => PathBuf::from("<nul>"),
});
(format, dst)
@@ -270,29 +198,8 @@ fn parse_args() -> (OutputFormat, PathBuf) {
fn main() {
rustc_driver::init_env_logger("RUST_LOG");
let (format, dst) = parse_args();
- let result =
- rustc_span::create_default_session_globals_then(move || main_with_result(format, &dst));
+ let result = main_with_result(format, &dst);
if let Err(e) = result {
- 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<_>>());}
- )*
- )
+ panic!("{:?}", e);
}
- include!(concat!(env!("OUT_DIR"), "/all_error_codes.rs"));
- long_codes
}