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