1 #![feature(rustc_private)]
3 #![deny(rust_2018_idioms)]
5 extern crate env_logger;
7 extern crate serialize as rustc_serialize;
9 use std::collections::BTreeMap;
11 use std::error::Error;
12 use std::fs::{self, read_dir, File};
15 use std::path::PathBuf;
16 use std::cell::RefCell;
18 use syntax::edition::DEFAULT_EDITION;
19 use syntax::diagnostics::metadata::{get_metadata_dir, ErrorMetadataMap, ErrorMetadata};
21 use rustdoc::html::markdown::{Markdown, IdMap, ErrorCodes, PLAYGROUND};
22 use rustc_serialize::json;
26 Markdown(MarkdownFormatter),
31 fn from(format: &str, resource_suffix: &str) -> OutputFormat {
32 match &*format.to_lowercase() {
33 "html" => OutputFormat::HTML(HTMLFormatter(RefCell::new(IdMap::new()),
34 resource_suffix.to_owned())),
35 "markdown" => OutputFormat::Markdown(MarkdownFormatter),
36 s => OutputFormat::Unknown(s.to_owned()),
42 fn header(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>>;
43 fn title(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>>;
44 fn error_code_block(&self, output: &mut dyn Write, info: &ErrorMetadata,
45 err_code: &str) -> Result<(), Box<dyn Error>>;
46 fn footer(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>>;
49 struct HTMLFormatter(RefCell<IdMap>, String);
50 struct MarkdownFormatter;
52 impl Formatter for HTMLFormatter {
53 fn header(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
54 write!(output, r##"<!DOCTYPE html>
57 <title>Rust Compiler Error Index</title>
58 <meta charset="utf-8">
59 <!-- Include rust.css after light.css so its rules take priority. -->
60 <link rel="stylesheet" type="text/css" href="light{suffix}.css"/>
61 <link rel="stylesheet" type="text/css" href="rust.css"/>
73 fn title(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
74 write!(output, "<h1>Rust Compiler Error Index</h1>\n")?;
78 fn error_code_block(&self, output: &mut dyn Write, info: &ErrorMetadata,
79 err_code: &str) -> Result<(), Box<dyn Error>> {
80 // Enclose each error in a div so they can be shown/hidden en masse.
81 let desc_desc = match info.description {
82 Some(_) => "error-described",
83 None => "error-undescribed",
85 let use_desc = match info.use_site {
86 Some(_) => "error-used",
87 None => "error-unused",
89 write!(output, "<div class=\"{} {}\">", desc_desc, use_desc)?;
91 // Error title (with self-link).
93 "<h2 id=\"{0}\" class=\"section-header\"><a href=\"#{0}\">{0}</a></h2>\n",
96 // Description rendered as markdown.
97 match info.description {
99 let mut id_map = self.0.borrow_mut();
101 Markdown(desc, &[], RefCell::new(&mut id_map),
102 ErrorCodes::Yes, DEFAULT_EDITION))?
104 None => write!(output, "<p>No description.</p>\n")?,
107 write!(output, "</div>\n")?;
111 fn footer(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
112 write!(output, r##"<script>
113 function onEach(arr, func) {{
114 if (arr && arr.length > 0 && func) {{
115 for (var i = 0; i < arr.length; i++) {{
121 function hasClass(elem, className) {{
122 if (elem && className && elem.className) {{
123 var elemClass = elem.className;
124 var start = elemClass.indexOf(className);
127 }} else if (elemClass.length === className.length) {{
130 if (start > 0 && elemClass[start - 1] !== ' ') {{
133 var end = start + className.length;
134 if (end < elemClass.length && elemClass[end] !== ' ') {{
139 if (start > 0 && elemClass[start - 1] !== ' ') {{
142 var end = start + className.length;
143 if (end < elemClass.length && elemClass[end] !== ' ') {{
151 onEach(document.getElementsByClassName('rust-example-rendered'), function(e) {{
152 if (hasClass(e, 'compile_fail')) {{
153 e.addEventListener("mouseover", function(event) {{
154 e.previousElementSibling.childNodes[0].style.color = '#f00';
156 e.addEventListener("mouseout", function(event) {{
157 e.previousElementSibling.childNodes[0].style.color = '';
159 }} else if (hasClass(e, 'ignore')) {{
160 e.addEventListener("mouseover", function(event) {{
161 e.previousElementSibling.childNodes[0].style.color = '#ff9200';
163 e.addEventListener("mouseout", function(event) {{
164 e.previousElementSibling.childNodes[0].style.color = '';
175 impl Formatter for MarkdownFormatter {
176 #[allow(unused_variables)]
177 fn header(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
181 fn title(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
182 write!(output, "# Rust Compiler Error Index\n")?;
186 fn error_code_block(&self, output: &mut dyn Write, info: &ErrorMetadata,
187 err_code: &str) -> Result<(), Box<dyn Error>> {
188 Ok(match info.description {
189 Some(ref desc) => write!(output, "## {}\n{}\n", err_code, desc)?,
194 #[allow(unused_variables)]
195 fn footer(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
200 /// Loads all the metadata files from `metadata_dir` into an in-memory map.
201 fn load_all_errors(metadata_dir: &Path) -> Result<ErrorMetadataMap, Box<dyn Error>> {
202 let mut all_errors = BTreeMap::new();
204 for entry in read_dir(metadata_dir)? {
205 let path = entry?.path();
207 let metadata_str = fs::read_to_string(&path)?;
209 let some_errors: ErrorMetadataMap = json::decode(&metadata_str)?;
211 for (err_code, info) in some_errors {
212 all_errors.insert(err_code, info);
219 /// Output an HTML page for the errors in `err_map` to `output_path`.
220 fn render_error_page<T: Formatter>(err_map: &ErrorMetadataMap, output_path: &Path,
221 formatter: T) -> Result<(), Box<dyn Error>> {
222 let mut output_file = File::create(output_path)?;
224 formatter.header(&mut output_file)?;
225 formatter.title(&mut output_file)?;
227 for (err_code, info) in err_map {
228 formatter.error_code_block(&mut output_file, info, err_code)?;
231 formatter.footer(&mut output_file)
234 fn main_with_result(format: OutputFormat, dst: &Path) -> Result<(), Box<dyn Error>> {
235 let build_arch = env::var("CFG_BUILD")?;
236 let metadata_dir = get_metadata_dir(&build_arch);
237 let err_map = load_all_errors(&metadata_dir)?;
239 OutputFormat::Unknown(s) => panic!("Unknown output format: {}", s),
240 OutputFormat::HTML(h) => render_error_page(&err_map, dst, h)?,
241 OutputFormat::Markdown(m) => render_error_page(&err_map, dst, m)?,
246 fn parse_args() -> (OutputFormat, PathBuf) {
247 let mut args = env::args().skip(1);
248 let format = args.next();
249 let dst = args.next();
250 let resource_suffix = args.next().unwrap_or_else(String::new);
251 let format = format.map(|a| OutputFormat::from(&a, &resource_suffix))
252 .unwrap_or(OutputFormat::from("html", &resource_suffix));
253 let dst = dst.map(PathBuf::from).unwrap_or_else(|| {
255 OutputFormat::HTML(..) => PathBuf::from("doc/error-index.html"),
256 OutputFormat::Markdown(..) => PathBuf::from("doc/error-index.md"),
257 OutputFormat::Unknown(..) => PathBuf::from("<nul>"),
265 PLAYGROUND.with(|slot| {
266 *slot.borrow_mut() = Some((None, String::from("https://play.rust-lang.org/")));
268 let (format, dst) = parse_args();
269 let result = syntax::with_default_globals(move || {
270 main_with_result(format, &dst)
272 if let Err(e) = result {
273 panic!("{}", e.description());