]> git.lizzy.rs Git - rust.git/blob - src/tools/error_index_generator/main.rs
Rollup merge of #85302 - r00ster91:patch-7, r=joshtriplett
[rust.git] / src / tools / error_index_generator / main.rs
1 #![feature(rustc_private)]
2
3 extern crate rustc_driver;
4 extern crate rustc_span;
5
6 use std::cell::RefCell;
7 use std::collections::BTreeMap;
8 use std::env;
9 use std::error::Error;
10 use std::fs::File;
11 use std::io::Write;
12 use std::path::Path;
13 use std::path::PathBuf;
14
15 use rustc_span::edition::DEFAULT_EDITION;
16
17 use rustdoc::html::markdown::{ErrorCodes, IdMap, Markdown, Playground};
18
19 pub struct ErrorMetadata {
20     pub description: Option<String>,
21 }
22
23 /// Mapping from error codes to metadata that can be (de)serialized.
24 pub type ErrorMetadataMap = BTreeMap<String, ErrorMetadata>;
25
26 enum OutputFormat {
27     HTML(HTMLFormatter),
28     Markdown(MarkdownFormatter),
29     Unknown(String),
30 }
31
32 impl OutputFormat {
33     fn from(format: &str, resource_suffix: &str) -> OutputFormat {
34         match &*format.to_lowercase() {
35             "html" => OutputFormat::HTML(HTMLFormatter(
36                 RefCell::new(IdMap::new()),
37                 resource_suffix.to_owned(),
38             )),
39             "markdown" => OutputFormat::Markdown(MarkdownFormatter),
40             s => OutputFormat::Unknown(s.to_owned()),
41         }
42     }
43 }
44
45 trait Formatter {
46     fn header(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>>;
47     fn title(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>>;
48     fn error_code_block(
49         &self,
50         output: &mut dyn Write,
51         info: &ErrorMetadata,
52         err_code: &str,
53     ) -> Result<(), Box<dyn Error>>;
54     fn footer(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>>;
55 }
56
57 struct HTMLFormatter(RefCell<IdMap>, String);
58 struct MarkdownFormatter;
59
60 impl Formatter for HTMLFormatter {
61     fn header(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
62         write!(
63             output,
64             r##"<!DOCTYPE html>
65 <html>
66 <head>
67 <title>Rust Compiler Error Index</title>
68 <meta charset="utf-8">
69 <!-- Include rust.css after light.css so its rules take priority. -->
70 <link rel="stylesheet" type="text/css" href="light{suffix}.css"/>
71 <link rel="stylesheet" type="text/css" href="rust.css"/>
72 <style>
73 .error-undescribed {{
74     display: none;
75 }}
76 </style>
77 </head>
78 <body>
79 "##,
80             suffix = self.1
81         )?;
82         Ok(())
83     }
84
85     fn title(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
86         write!(output, "<h1>Rust Compiler Error Index</h1>\n")?;
87         Ok(())
88     }
89
90     fn error_code_block(
91         &self,
92         output: &mut dyn Write,
93         info: &ErrorMetadata,
94         err_code: &str,
95     ) -> Result<(), Box<dyn Error>> {
96         // Enclose each error in a div so they can be shown/hidden en masse.
97         let desc_desc = match info.description {
98             Some(_) => "error-described",
99             None => "error-undescribed",
100         };
101         write!(output, "<div class=\"{}\">", desc_desc)?;
102
103         // Error title (with self-link).
104         write!(
105             output,
106             "<h2 id=\"{0}\" class=\"section-header\"><a href=\"#{0}\">{0}</a></h2>\n",
107             err_code
108         )?;
109
110         // Description rendered as markdown.
111         match info.description {
112             Some(ref desc) => {
113                 let mut id_map = self.0.borrow_mut();
114                 let playground = Playground {
115                     crate_name: None,
116                     url: String::from("https://play.rust-lang.org/"),
117                 };
118                 write!(
119                     output,
120                     "{}",
121                     Markdown(
122                         desc,
123                         &[],
124                         &mut id_map,
125                         ErrorCodes::Yes,
126                         DEFAULT_EDITION,
127                         &Some(playground)
128                     )
129                     .into_string()
130                 )?
131             }
132             None => write!(output, "<p>No description.</p>\n")?,
133         }
134
135         write!(output, "</div>\n")?;
136         Ok(())
137     }
138
139     fn footer(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
140         write!(
141             output,
142             r##"<script>
143 function onEach(arr, func) {{
144     if (arr && arr.length > 0 && func) {{
145         for (var i = 0; i < arr.length; i++) {{
146             func(arr[i]);
147         }}
148     }}
149 }}
150
151 function hasClass(elem, className) {{
152     if (elem && className && elem.className) {{
153         var elemClass = elem.className;
154         var start = elemClass.indexOf(className);
155         if (start === -1) {{
156             return false;
157         }} else if (elemClass.length === className.length) {{
158             return true;
159         }} else {{
160             if (start > 0 && elemClass[start - 1] !== ' ') {{
161                 return false;
162             }}
163             var end = start + className.length;
164             if (end < elemClass.length && elemClass[end] !== ' ') {{
165                 return false;
166             }}
167             return true;
168         }}
169         if (start > 0 && elemClass[start - 1] !== ' ') {{
170             return false;
171         }}
172         var end = start + className.length;
173         if (end < elemClass.length && elemClass[end] !== ' ') {{
174             return false;
175         }}
176         return true;
177     }}
178     return false;
179 }}
180
181 onEach(document.getElementsByClassName('rust-example-rendered'), function(e) {{
182     if (hasClass(e, 'compile_fail')) {{
183         e.addEventListener("mouseover", function(event) {{
184             e.previousElementSibling.childNodes[0].style.color = '#f00';
185         }});
186         e.addEventListener("mouseout", function(event) {{
187             e.previousElementSibling.childNodes[0].style.color = '';
188         }});
189     }} else if (hasClass(e, 'ignore')) {{
190         e.addEventListener("mouseover", function(event) {{
191             e.previousElementSibling.childNodes[0].style.color = '#ff9200';
192         }});
193         e.addEventListener("mouseout", function(event) {{
194             e.previousElementSibling.childNodes[0].style.color = '';
195         }});
196     }}
197 }});
198 </script>
199 </body>
200 </html>"##
201         )?;
202         Ok(())
203     }
204 }
205
206 impl Formatter for MarkdownFormatter {
207     #[allow(unused_variables)]
208     fn header(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
209         Ok(())
210     }
211
212     fn title(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
213         write!(output, "# Rust Compiler Error Index\n")?;
214         Ok(())
215     }
216
217     fn error_code_block(
218         &self,
219         output: &mut dyn Write,
220         info: &ErrorMetadata,
221         err_code: &str,
222     ) -> Result<(), Box<dyn Error>> {
223         Ok(match info.description {
224             Some(ref desc) => write!(output, "## {}\n{}\n", err_code, desc)?,
225             None => (),
226         })
227     }
228
229     #[allow(unused_variables)]
230     fn footer(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
231         Ok(())
232     }
233 }
234
235 /// Output an HTML page for the errors in `err_map` to `output_path`.
236 fn render_error_page<T: Formatter>(
237     err_map: &ErrorMetadataMap,
238     output_path: &Path,
239     formatter: T,
240 ) -> Result<(), Box<dyn Error>> {
241     let mut output_file = File::create(output_path)?;
242
243     formatter.header(&mut output_file)?;
244     formatter.title(&mut output_file)?;
245
246     for (err_code, info) in err_map {
247         formatter.error_code_block(&mut output_file, info, err_code)?;
248     }
249
250     formatter.footer(&mut output_file)
251 }
252
253 fn main_with_result(format: OutputFormat, dst: &Path) -> Result<(), Box<dyn Error>> {
254     let long_codes = register_all();
255     let mut err_map = BTreeMap::new();
256     for (code, desc) in long_codes {
257         err_map.insert(code.to_string(), ErrorMetadata { description: desc.map(String::from) });
258     }
259     match format {
260         OutputFormat::Unknown(s) => panic!("Unknown output format: {}", s),
261         OutputFormat::HTML(h) => render_error_page(&err_map, dst, h)?,
262         OutputFormat::Markdown(m) => render_error_page(&err_map, dst, m)?,
263     }
264     Ok(())
265 }
266
267 fn parse_args() -> (OutputFormat, PathBuf) {
268     let mut args = env::args().skip(1);
269     let format = args.next();
270     let dst = args.next();
271     let resource_suffix = args.next().unwrap_or_else(String::new);
272     let format = format
273         .map(|a| OutputFormat::from(&a, &resource_suffix))
274         .unwrap_or(OutputFormat::from("html", &resource_suffix));
275     let dst = dst.map(PathBuf::from).unwrap_or_else(|| match format {
276         OutputFormat::HTML(..) => PathBuf::from("doc/error-index.html"),
277         OutputFormat::Markdown(..) => PathBuf::from("doc/error-index.md"),
278         OutputFormat::Unknown(..) => PathBuf::from("<nul>"),
279     });
280     (format, dst)
281 }
282
283 fn main() {
284     rustc_driver::init_env_logger("RUST_LOG");
285     let (format, dst) = parse_args();
286     let result = rustc_span::with_default_session_globals(move || main_with_result(format, &dst));
287     if let Err(e) = result {
288         panic!("{}", e.to_string());
289     }
290 }
291
292 include!(concat!(env!("OUT_DIR"), "/error_codes.rs"));