]> git.lizzy.rs Git - rust.git/blob - src/tools/error_index_generator/main.rs
Rollup merge of #88820 - hlopko:add_pie_relocation_model, r=petrochenkov
[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="rustdoc{suffix}.css"/>
71 <link rel="stylesheet" type="text/css" href="light{suffix}.css"/>
72 <link rel="stylesheet" type="text/css" href="rust.css"/>
73 <style>
74 .error-undescribed {{
75     display: none;
76 }}
77 </style>
78 </head>
79 <body>
80 "##,
81             suffix = self.1
82         )?;
83         Ok(())
84     }
85
86     fn title(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
87         write!(output, "<h1>Rust Compiler Error Index</h1>\n")?;
88         Ok(())
89     }
90
91     fn error_code_block(
92         &self,
93         output: &mut dyn Write,
94         info: &ErrorMetadata,
95         err_code: &str,
96     ) -> Result<(), Box<dyn Error>> {
97         // Enclose each error in a div so they can be shown/hidden en masse.
98         let desc_desc = match info.description {
99             Some(_) => "error-described",
100             None => "error-undescribed",
101         };
102         write!(output, "<div class=\"{}\">", desc_desc)?;
103
104         // Error title (with self-link).
105         write!(
106             output,
107             "<h2 id=\"{0}\" class=\"section-header\"><a href=\"#{0}\">{0}</a></h2>\n",
108             err_code
109         )?;
110
111         // Description rendered as markdown.
112         match info.description {
113             Some(ref desc) => {
114                 let mut id_map = self.0.borrow_mut();
115                 let playground = Playground {
116                     crate_name: None,
117                     url: String::from("https://play.rust-lang.org/"),
118                 };
119                 write!(
120                     output,
121                     "{}",
122                     Markdown(
123                         desc,
124                         &[],
125                         &mut id_map,
126                         ErrorCodes::Yes,
127                         DEFAULT_EDITION,
128                         &Some(playground)
129                     )
130                     .into_string()
131                 )?
132             }
133             None => write!(output, "<p>No description.</p>\n")?,
134         }
135
136         write!(output, "</div>\n")?;
137         Ok(())
138     }
139
140     fn footer(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
141         write!(
142             output,
143             r##"<script>
144 function onEach(arr, func) {{
145     if (arr && arr.length > 0 && func) {{
146         for (var i = 0; i < arr.length; i++) {{
147             func(arr[i]);
148         }}
149     }}
150 }}
151
152 function hasClass(elem, className) {{
153     if (elem && className && elem.className) {{
154         var elemClass = elem.className;
155         var start = elemClass.indexOf(className);
156         if (start === -1) {{
157             return false;
158         }} else if (elemClass.length === className.length) {{
159             return true;
160         }} else {{
161             if (start > 0 && elemClass[start - 1] !== ' ') {{
162                 return false;
163             }}
164             var end = start + className.length;
165             if (end < elemClass.length && elemClass[end] !== ' ') {{
166                 return false;
167             }}
168             return true;
169         }}
170         if (start > 0 && elemClass[start - 1] !== ' ') {{
171             return false;
172         }}
173         var end = start + className.length;
174         if (end < elemClass.length && elemClass[end] !== ' ') {{
175             return false;
176         }}
177         return true;
178     }}
179     return false;
180 }}
181
182 onEach(document.getElementsByClassName('rust-example-rendered'), function(e) {{
183     if (hasClass(e, 'compile_fail')) {{
184         e.addEventListener("mouseover", function(event) {{
185             e.previousElementSibling.childNodes[0].style.color = '#f00';
186         }});
187         e.addEventListener("mouseout", function(event) {{
188             e.previousElementSibling.childNodes[0].style.color = '';
189         }});
190     }} else if (hasClass(e, 'ignore')) {{
191         e.addEventListener("mouseover", function(event) {{
192             e.previousElementSibling.childNodes[0].style.color = '#ff9200';
193         }});
194         e.addEventListener("mouseout", function(event) {{
195             e.previousElementSibling.childNodes[0].style.color = '';
196         }});
197     }}
198 }});
199 </script>
200 </body>
201 </html>"##
202         )?;
203         Ok(())
204     }
205 }
206
207 impl Formatter for MarkdownFormatter {
208     #[allow(unused_variables)]
209     fn header(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
210         Ok(())
211     }
212
213     fn title(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
214         write!(output, "# Rust Compiler Error Index\n")?;
215         Ok(())
216     }
217
218     fn error_code_block(
219         &self,
220         output: &mut dyn Write,
221         info: &ErrorMetadata,
222         err_code: &str,
223     ) -> Result<(), Box<dyn Error>> {
224         Ok(match info.description {
225             Some(ref desc) => write!(output, "## {}\n{}\n", err_code, desc)?,
226             None => (),
227         })
228     }
229
230     #[allow(unused_variables)]
231     fn footer(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
232         Ok(())
233     }
234 }
235
236 /// Output an HTML page for the errors in `err_map` to `output_path`.
237 fn render_error_page<T: Formatter>(
238     err_map: &ErrorMetadataMap,
239     output_path: &Path,
240     formatter: T,
241 ) -> Result<(), Box<dyn Error>> {
242     let mut output_file = File::create(output_path)?;
243
244     formatter.header(&mut output_file)?;
245     formatter.title(&mut output_file)?;
246
247     for (err_code, info) in err_map {
248         formatter.error_code_block(&mut output_file, info, err_code)?;
249     }
250
251     formatter.footer(&mut output_file)
252 }
253
254 fn main_with_result(format: OutputFormat, dst: &Path) -> Result<(), Box<dyn Error>> {
255     let long_codes = register_all();
256     let mut err_map = BTreeMap::new();
257     for (code, desc) in long_codes {
258         err_map.insert(code.to_string(), ErrorMetadata { description: desc.map(String::from) });
259     }
260     match format {
261         OutputFormat::Unknown(s) => panic!("Unknown output format: {}", s),
262         OutputFormat::HTML(h) => render_error_page(&err_map, dst, h)?,
263         OutputFormat::Markdown(m) => render_error_page(&err_map, dst, m)?,
264     }
265     Ok(())
266 }
267
268 fn parse_args() -> (OutputFormat, PathBuf) {
269     let mut args = env::args().skip(1);
270     let format = args.next();
271     let dst = args.next();
272     let resource_suffix = args.next().unwrap_or_else(String::new);
273     let format = format
274         .map(|a| OutputFormat::from(&a, &resource_suffix))
275         .unwrap_or(OutputFormat::from("html", &resource_suffix));
276     let dst = dst.map(PathBuf::from).unwrap_or_else(|| match format {
277         OutputFormat::HTML(..) => PathBuf::from("doc/error-index.html"),
278         OutputFormat::Markdown(..) => PathBuf::from("doc/error-index.md"),
279         OutputFormat::Unknown(..) => PathBuf::from("<nul>"),
280     });
281     (format, dst)
282 }
283
284 fn main() {
285     rustc_driver::init_env_logger("RUST_LOG");
286     let (format, dst) = parse_args();
287     let result =
288         rustc_span::create_default_session_globals_then(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"));