]> git.lizzy.rs Git - rust.git/blob - src/tools/error_index_generator/main.rs
Rollup merge of #97249 - GuillaumeGomez:details-summary-fixes, r=notriddle
[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, HeadingOffset, 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                         content: desc,
124                         links: &[],
125                         ids: &mut id_map,
126                         error_codes: ErrorCodes::Yes,
127                         edition: DEFAULT_EDITION,
128                         playground: &Some(playground),
129                         heading_offset: HeadingOffset::H1,
130                     }
131                     .into_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         var length = arr.length;
148         var i;
149         for (i = 0; i < length; ++i) {{
150             if (func(arr[i])) {{
151                 return true;
152             }}
153         }}
154     }}
155     return false;
156 }}
157
158 function onEachLazy(lazyArray, func) {{
159     return onEach(
160         Array.prototype.slice.call(lazyArray),
161         func);
162 }}
163
164 function hasClass(elem, className) {{
165     return elem && elem.classList && elem.classList.contains(className);
166 }}
167
168 onEachLazy(document.getElementsByClassName('rust-example-rendered'), function(e) {{
169     if (hasClass(e, 'compile_fail')) {{
170         e.addEventListener("mouseover", function(event) {{
171             e.parentElement.previousElementSibling.childNodes[0].style.color = '#f00';
172         }});
173         e.addEventListener("mouseout", function(event) {{
174             e.parentElement.previousElementSibling.childNodes[0].style.color = '';
175         }});
176     }} else if (hasClass(e, 'ignore')) {{
177         e.addEventListener("mouseover", function(event) {{
178             e.parentElement.previousElementSibling.childNodes[0].style.color = '#ff9200';
179         }});
180         e.addEventListener("mouseout", function(event) {{
181             e.parentElement.previousElementSibling.childNodes[0].style.color = '';
182         }});
183     }}
184 }});
185 </script>
186 </body>
187 </html>"##
188         )?;
189         Ok(())
190     }
191 }
192
193 impl Formatter for MarkdownFormatter {
194     #[allow(unused_variables)]
195     fn header(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
196         Ok(())
197     }
198
199     fn title(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
200         write!(output, "# Rust Compiler Error Index\n")?;
201         Ok(())
202     }
203
204     fn error_code_block(
205         &self,
206         output: &mut dyn Write,
207         info: &ErrorMetadata,
208         err_code: &str,
209     ) -> Result<(), Box<dyn Error>> {
210         Ok(match info.description {
211             Some(ref desc) => write!(output, "## {}\n{}\n", err_code, desc)?,
212             None => (),
213         })
214     }
215
216     #[allow(unused_variables)]
217     fn footer(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
218         Ok(())
219     }
220 }
221
222 /// Output an HTML page for the errors in `err_map` to `output_path`.
223 fn render_error_page<T: Formatter>(
224     err_map: &ErrorMetadataMap,
225     output_path: &Path,
226     formatter: T,
227 ) -> Result<(), Box<dyn Error>> {
228     let mut output_file = File::create(output_path)?;
229
230     formatter.header(&mut output_file)?;
231     formatter.title(&mut output_file)?;
232
233     for (err_code, info) in err_map {
234         formatter.error_code_block(&mut output_file, info, err_code)?;
235     }
236
237     formatter.footer(&mut output_file)
238 }
239
240 fn main_with_result(format: OutputFormat, dst: &Path) -> Result<(), Box<dyn Error>> {
241     let long_codes = register_all();
242     let mut err_map = BTreeMap::new();
243     for (code, desc) in long_codes {
244         err_map.insert(code.to_string(), ErrorMetadata { description: desc.map(String::from) });
245     }
246     match format {
247         OutputFormat::Unknown(s) => panic!("Unknown output format: {}", s),
248         OutputFormat::HTML(h) => render_error_page(&err_map, dst, h)?,
249         OutputFormat::Markdown(m) => render_error_page(&err_map, dst, m)?,
250     }
251     Ok(())
252 }
253
254 fn parse_args() -> (OutputFormat, PathBuf) {
255     let mut args = env::args().skip(1);
256     let format = args.next();
257     let dst = args.next();
258     let resource_suffix = args.next().unwrap_or_else(String::new);
259     let format = format
260         .map(|a| OutputFormat::from(&a, &resource_suffix))
261         .unwrap_or(OutputFormat::from("html", &resource_suffix));
262     let dst = dst.map(PathBuf::from).unwrap_or_else(|| match format {
263         OutputFormat::HTML(..) => PathBuf::from("doc/error-index.html"),
264         OutputFormat::Markdown(..) => PathBuf::from("doc/error-index.md"),
265         OutputFormat::Unknown(..) => PathBuf::from("<nul>"),
266     });
267     (format, dst)
268 }
269
270 fn main() {
271     rustc_driver::init_env_logger("RUST_LOG");
272     let (format, dst) = parse_args();
273     let result =
274         rustc_span::create_default_session_globals_then(move || main_with_result(format, &dst));
275     if let Err(e) = result {
276         panic!("{}", e.to_string());
277     }
278 }
279
280 fn register_all() -> Vec<(&'static str, Option<&'static str>)> {
281     let mut long_codes: Vec<(&'static str, Option<&'static str>)> = Vec::new();
282     macro_rules! register_diagnostics {
283         ($($ecode:ident: $message:expr,)* ; $($code:ident,)*) => (
284             $(
285                 {long_codes.extend([
286                     (stringify!($ecode), Some($message)),
287                 ].iter());}
288             )*
289             $(
290                 {long_codes.extend([
291                     stringify!($code),
292                 ].iter().cloned().map(|s| (s, None)).collect::<Vec<_>>());}
293             )*
294         )
295     }
296     include!(concat!(env!("OUT_DIR"), "/all_error_codes.rs"));
297     long_codes
298 }