]> git.proxmox.com Git - rustc.git/blob - src/tools/error_index_generator/main.rs
01a3fc812b2087cbad4ef95e08524e1d0cf38060
[rustc.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"));