]> git.lizzy.rs Git - rust.git/blob - src/tools/error_index_generator/main.rs
Auto merge of #100935 - cuviper:upgrade-android-ci, r=Mark-Simulacrum
[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 crate::error_codes::error_codes;
7
8 use std::env;
9 use std::error::Error;
10 use std::fs::{create_dir_all, 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 macro_rules! register_diagnostics {
20     ($($error_code:ident: $message:expr,)+ ; $($undocumented:ident,)* ) => {
21         pub fn error_codes() -> Vec<(&'static str, Option<&'static str>)> {
22             let mut errors: Vec<(&str, Option<&str>)> = vec![
23                 $((stringify!($error_code), Some($message)),)+
24                 $((stringify!($undocumented), None),)+
25             ];
26             errors.sort();
27             errors
28         }
29     }
30 }
31
32 #[path = "../../../compiler/rustc_error_codes/src/error_codes.rs"]
33 mod error_codes;
34
35 enum OutputFormat {
36     HTML(HTMLFormatter),
37     Markdown,
38     Unknown(String),
39 }
40
41 impl OutputFormat {
42     fn from(format: &str, resource_suffix: &str) -> OutputFormat {
43         match &*format.to_lowercase() {
44             "html" => OutputFormat::HTML(HTMLFormatter(resource_suffix.to_owned())),
45             "markdown" => OutputFormat::Markdown,
46             s => OutputFormat::Unknown(s.to_owned()),
47         }
48     }
49 }
50
51 struct HTMLFormatter(String);
52
53 impl HTMLFormatter {
54     fn create_error_code_file(
55         &self,
56         err_code: &str,
57         explanation: &str,
58         parent_dir: &Path,
59     ) -> Result<(), Box<dyn Error>> {
60         let mut output_file = File::create(parent_dir.join(err_code).with_extension("html"))?;
61
62         self.header(&mut output_file, "../", "")?;
63         self.title(&mut output_file, &format!("Error code {}", err_code))?;
64
65         let mut id_map = IdMap::new();
66         let playground =
67             Playground { crate_name: None, url: String::from("https://play.rust-lang.org/") };
68         write!(
69             output_file,
70             "{}",
71             Markdown {
72                 content: explanation,
73                 links: &[],
74                 ids: &mut id_map,
75                 error_codes: ErrorCodes::Yes,
76                 edition: DEFAULT_EDITION,
77                 playground: &Some(playground),
78                 heading_offset: HeadingOffset::H1,
79             }
80             .into_string()
81         )?;
82         write!(
83             output_file,
84             "<p>\
85                 <a style='text-align: center;display: block;width: 100%;' \
86                    href='../error-index.html'>Back to list of error codes</a>\
87              </p>",
88         )?;
89
90         self.footer(&mut output_file)
91     }
92
93     fn header(
94         &self,
95         output: &mut dyn Write,
96         extra_path: &str,
97         extra: &str,
98     ) -> Result<(), Box<dyn Error>> {
99         write!(
100             output,
101             r##"<!DOCTYPE html>
102 <html>
103 <head>
104 <title>Rust Compiler Error Index</title>
105 <meta charset="utf-8">
106 <!-- Include rust.css after light.css so its rules take priority. -->
107 <link rel="stylesheet" type="text/css" href="{extra_path}rustdoc{suffix}.css"/>
108 <link rel="stylesheet" type="text/css" href="{extra_path}light{suffix}.css"/>
109 <link rel="stylesheet" type="text/css" href="{extra_path}rust.css"/>
110 <style>
111 .error-undescribed {{
112     display: none;
113 }}
114 </style>{extra}
115 </head>
116 <body>
117 "##,
118             suffix = self.0,
119         )?;
120         Ok(())
121     }
122
123     fn title(&self, output: &mut dyn Write, title: &str) -> Result<(), Box<dyn Error>> {
124         write!(output, "<h1>{}</h1>\n", title)?;
125         Ok(())
126     }
127
128     fn footer(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
129         write!(output, "</body></html>")?;
130         Ok(())
131     }
132 }
133
134 /// Output an HTML page for the errors in `err_map` to `output_path`.
135 fn render_markdown(output_path: &Path) -> Result<(), Box<dyn Error>> {
136     let mut output_file = File::create(output_path)?;
137
138     write!(output_file, "# Rust Compiler Error Index\n")?;
139
140     for (err_code, description) in error_codes().iter() {
141         match description {
142             Some(ref desc) => write!(output_file, "## {}\n{}\n", err_code, desc)?,
143             None => {}
144         }
145     }
146
147     Ok(())
148 }
149
150 fn render_html(output_path: &Path, formatter: HTMLFormatter) -> Result<(), Box<dyn Error>> {
151     let mut output_file = File::create(output_path)?;
152
153     let error_codes_dir = "error_codes";
154
155     let parent = output_path.parent().expect("There should have a parent").join(error_codes_dir);
156
157     if !parent.exists() {
158         create_dir_all(&parent)?;
159     }
160
161     formatter.header(
162         &mut output_file,
163         "",
164         &format!(
165             r#"<script>(function() {{
166     if (window.location.hash) {{
167         let code = window.location.hash.replace(/^#/, '');
168         // We have to make sure this pattern matches to avoid inadvertently creating an
169         // open redirect.
170         if (/^E[0-9]+$/.test(code)) {{
171             window.location = './{error_codes_dir}/' + code + '.html';
172         }}
173     }}
174 }})()</script>"#
175         ),
176     )?;
177     formatter.title(&mut output_file, "Rust Compiler Error Index")?;
178
179     write!(
180         output_file,
181         "<p>This page lists all the error codes emitted by the Rust compiler. If you want a full \
182             explanation on an error code, click on it.</p>\
183          <ul>",
184     )?;
185     for (err_code, explanation) in error_codes().iter() {
186         if let Some(explanation) = explanation {
187             write!(
188                 output_file,
189                 "<li><a href='./{0}/{1}.html'>{1}</a></li>",
190                 error_codes_dir, err_code
191             )?;
192             formatter.create_error_code_file(err_code, explanation, &parent)?;
193         } else {
194             write!(output_file, "<li>{}</li>", err_code)?;
195         }
196     }
197     write!(output_file, "</ul>")?;
198     formatter.footer(&mut output_file)
199 }
200
201 fn main_with_result(format: OutputFormat, dst: &Path) -> Result<(), Box<dyn Error>> {
202     match format {
203         OutputFormat::Unknown(s) => panic!("Unknown output format: {}", s),
204         OutputFormat::HTML(h) => render_html(dst, h),
205         OutputFormat::Markdown => render_markdown(dst),
206     }
207 }
208
209 fn parse_args() -> (OutputFormat, PathBuf) {
210     let mut args = env::args().skip(1);
211     let format = args.next();
212     let dst = args.next();
213     let resource_suffix = args.next().unwrap_or_else(String::new);
214     let format = format
215         .map(|a| OutputFormat::from(&a, &resource_suffix))
216         .unwrap_or(OutputFormat::from("html", &resource_suffix));
217     let dst = dst.map(PathBuf::from).unwrap_or_else(|| match format {
218         OutputFormat::HTML(..) => PathBuf::from("doc/error-index.html"),
219         OutputFormat::Markdown => PathBuf::from("doc/error-index.md"),
220         OutputFormat::Unknown(..) => PathBuf::from("<nul>"),
221     });
222     (format, dst)
223 }
224
225 fn main() {
226     rustc_driver::init_env_logger("RUST_LOG");
227     let (format, dst) = parse_args();
228     let result =
229         rustc_span::create_default_session_globals_then(move || main_with_result(format, &dst));
230     if let Err(e) = result {
231         panic!("{}", e.to_string());
232     }
233 }