summaryrefslogtreecommitdiffstats
path: root/src/tools/error_index_generator
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/error_index_generator')
-rw-r--r--src/tools/error_index_generator/Cargo.toml5
-rw-r--r--src/tools/error_index_generator/book_config.toml19
-rw-r--r--src/tools/error_index_generator/build.rs31
-rw-r--r--src/tools/error_index_generator/error-index.css38
-rw-r--r--src/tools/error_index_generator/error-index.js9
-rw-r--r--src/tools/error_index_generator/main.rs377
-rw-r--r--src/tools/error_index_generator/redirect.js16
7 files changed, 225 insertions, 270 deletions
diff --git a/src/tools/error_index_generator/Cargo.toml b/src/tools/error_index_generator/Cargo.toml
index c84b79e11..f4dac6e94 100644
--- a/src/tools/error_index_generator/Cargo.toml
+++ b/src/tools/error_index_generator/Cargo.toml
@@ -4,10 +4,7 @@ version = "0.0.0"
edition = "2021"
[dependencies]
-rustdoc = { path = "../../librustdoc" }
-
-[build-dependencies]
-walkdir = "2"
+mdbook = { version = "0.4", default-features = false, features = ["search"] }
[[bin]]
name = "error_index_generator"
diff --git a/src/tools/error_index_generator/book_config.toml b/src/tools/error_index_generator/book_config.toml
new file mode 100644
index 000000000..885100ae3
--- /dev/null
+++ b/src/tools/error_index_generator/book_config.toml
@@ -0,0 +1,19 @@
+[book]
+title = "Error codes index"
+description = "Book listing all Rust error codes"
+src = ""
+
+[output.html]
+git-repository-url = "https://github.com/rust-lang/rust/"
+additional-css = ["error-index.css"]
+additional-js = ["error-index.js"]
+
+[output.html.search]
+enable = true
+limit-results = 20
+use-boolean-and = true
+boost-title = 2
+boost-hierarchy = 2
+boost-paragraph = 1
+expand = true
+heading-split-level = 0
diff --git a/src/tools/error_index_generator/build.rs b/src/tools/error_index_generator/build.rs
deleted file mode 100644
index 70b00b36c..000000000
--- 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/error-index.css b/src/tools/error_index_generator/error-index.css
new file mode 100644
index 000000000..8975af82d
--- /dev/null
+++ b/src/tools/error_index_generator/error-index.css
@@ -0,0 +1,38 @@
+code.compile_fail {
+ border-left: 2px solid red;
+}
+
+pre .tooltip {
+ position: absolute;
+ left: -25px;
+ top: 0;
+ z-index: 1;
+ color: red;
+ cursor: pointer;
+}
+pre .tooltip::after {
+ display: none;
+ content: "This example deliberately fails to compile";
+ background-color: #000;
+ color: #fff;
+ border-color: #000;
+ text-align: center;
+ padding: 5px 3px 3px 3px;
+ border-radius: 6px;
+ margin-left: 5px;
+}
+pre .tooltip::before {
+ display: none;
+ border-color: transparent black transparent transparent;
+ content: " ";
+ position: absolute;
+ top: 50%;
+ left: 16px;
+ margin-top: -5px;
+ border-width: 5px;
+ border-style: solid;
+}
+
+pre .tooltip:hover::before, pre .tooltip:hover::after {
+ display: inline;
+}
diff --git a/src/tools/error_index_generator/error-index.js b/src/tools/error_index_generator/error-index.js
new file mode 100644
index 000000000..39b371be0
--- /dev/null
+++ b/src/tools/error_index_generator/error-index.js
@@ -0,0 +1,9 @@
+for (const elem of document.querySelectorAll("pre.playground")) {
+ if (elem.querySelector(".compile_fail") === null) {
+ continue;
+ }
+ const child = document.createElement("div");
+ child.className = "tooltip";
+ child.textContent = "ⓘ";
+ elem.appendChild(child);
+}
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
}
diff --git a/src/tools/error_index_generator/redirect.js b/src/tools/error_index_generator/redirect.js
new file mode 100644
index 000000000..8c907f579
--- /dev/null
+++ b/src/tools/error_index_generator/redirect.js
@@ -0,0 +1,16 @@
+(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)) {
+ return;
+ }
+ if (window.location.pathname.indexOf("/error_codes/") !== -1) {
+ // We're not at the top level, so we don't prepend with "./error_codes/".
+ window.location = './' + code + '.html';
+ } else {
+ window.location = './error_codes/' + code + '.html';
+ }
+ }
+})()