diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /src/tools/error_index_generator | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/error_index_generator')
-rw-r--r-- | src/tools/error_index_generator/Cargo.toml | 14 | ||||
-rw-r--r-- | src/tools/error_index_generator/build.rs | 31 | ||||
-rw-r--r-- | src/tools/error_index_generator/main.rs | 298 |
3 files changed, 343 insertions, 0 deletions
diff --git a/src/tools/error_index_generator/Cargo.toml b/src/tools/error_index_generator/Cargo.toml new file mode 100644 index 000000000..c84b79e11 --- /dev/null +++ b/src/tools/error_index_generator/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "error_index_generator" +version = "0.0.0" +edition = "2021" + +[dependencies] +rustdoc = { path = "../../librustdoc" } + +[build-dependencies] +walkdir = "2" + +[[bin]] +name = "error_index_generator" +path = "main.rs" diff --git a/src/tools/error_index_generator/build.rs b/src/tools/error_index_generator/build.rs new file mode 100644 index 000000000..70b00b36c --- /dev/null +++ b/src/tools/error_index_generator/build.rs @@ -0,0 +1,31 @@ +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 new file mode 100644 index 000000000..1ce02e48c --- /dev/null +++ b/src/tools/error_index_generator/main.rs @@ -0,0 +1,298 @@ +#![feature(rustc_private)] + +extern crate rustc_driver; +extern crate rustc_span; + +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::env; +use std::error::Error; +use std::fs::File; +use std::io::Write; +use std::path::Path; +use std::path::PathBuf; + +use rustc_span::edition::DEFAULT_EDITION; + +use rustdoc::html::markdown::{ErrorCodes, HeadingOffset, IdMap, Markdown, Playground}; + +pub struct ErrorMetadata { + pub description: Option<String>, +} + +/// Mapping from error codes to metadata that can be (de)serialized. +pub type ErrorMetadataMap = BTreeMap<String, ErrorMetadata>; + +enum OutputFormat { + HTML(HTMLFormatter), + Markdown(MarkdownFormatter), + 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), + 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; + +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(()) + } + + 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")?; + 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>> { + 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>> { + 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)?; + } + + 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)?, + } + 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 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::Unknown(..) => PathBuf::from("<nul>"), + }); + (format, dst) +} + +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)); + 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<_>>());} + )* + ) + } + include!(concat!(env!("OUT_DIR"), "/all_error_codes.rs")); + long_codes +} |