|
|
|
|
@@ -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
|
|
|
|
|
}
|
|
|
|
|
|