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