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