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