]> git.lizzy.rs Git - rust.git/blob - src/tools/error_index_generator/main.rs
22243f9fc9d6ca9b26fd0067c12df0ac59d34672
[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(&self, output: &mut dyn Write, extra: &str) -> Result<(), Box<dyn Error>> {
94         write!(
95             output,
96             r##"<!DOCTYPE html>
97 <html>
98 <head>
99 <title>Rust Compiler Error Index</title>
100 <meta charset="utf-8">
101 <!-- Include rust.css after light.css so its rules take priority. -->
102 <link rel="stylesheet" type="text/css" href="{extra}rustdoc{suffix}.css"/>
103 <link rel="stylesheet" type="text/css" href="{extra}light{suffix}.css"/>
104 <link rel="stylesheet" type="text/css" href="{extra}rust.css"/>
105 <style>
106 .error-undescribed {{
107     display: none;
108 }}
109 </style>
110 </head>
111 <body>
112 "##,
113             suffix = self.0,
114         )?;
115         Ok(())
116     }
117
118     fn title(&self, output: &mut dyn Write, title: &str) -> Result<(), Box<dyn Error>> {
119         write!(output, "<h1>{}</h1>\n", title)?;
120         Ok(())
121     }
122
123     fn footer(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
124         write!(output, "</body></html>")?;
125         Ok(())
126     }
127 }
128
129 /// Output an HTML page for the errors in `err_map` to `output_path`.
130 fn render_markdown(output_path: &Path) -> Result<(), Box<dyn Error>> {
131     let mut output_file = File::create(output_path)?;
132
133     write!(output_file, "# Rust Compiler Error Index\n")?;
134
135     for (err_code, description) in error_codes().iter() {
136         match description {
137             Some(ref desc) => write!(output_file, "## {}\n{}\n", err_code, desc)?,
138             None => {}
139         }
140     }
141
142     Ok(())
143 }
144
145 fn render_html(output_path: &Path, formatter: HTMLFormatter) -> Result<(), Box<dyn Error>> {
146     let mut output_file = File::create(output_path)?;
147
148     let error_codes_dir = "error_codes";
149
150     let parent = output_path.parent().expect("There should have a parent").join(error_codes_dir);
151
152     if !parent.exists() {
153         create_dir_all(&parent)?;
154     }
155
156     formatter.header(&mut output_file, "")?;
157     formatter.title(&mut output_file, "Rust Compiler Error Index")?;
158
159     write!(
160         output_file,
161         "<p>This page lists all the error codes emitted by the Rust compiler. If you want a full \
162             explanation on an error code, click on it.</p>\
163          <ul>",
164     )?;
165     for (err_code, explanation) in error_codes().iter() {
166         if let Some(explanation) = explanation {
167             write!(
168                 output_file,
169                 "<li><a href='./{0}/{1}.html'>{1}</a></li>",
170                 error_codes_dir, err_code
171             )?;
172             formatter.create_error_code_file(err_code, explanation, &parent)?;
173         } else {
174             write!(output_file, "<li>{}</li>", err_code)?;
175         }
176     }
177     write!(output_file, "</ul>")?;
178     formatter.footer(&mut output_file)
179 }
180
181 fn main_with_result(format: OutputFormat, dst: &Path) -> Result<(), Box<dyn Error>> {
182     match format {
183         OutputFormat::Unknown(s) => panic!("Unknown output format: {}", s),
184         OutputFormat::HTML(h) => render_html(dst, h),
185         OutputFormat::Markdown => render_markdown(dst),
186     }
187 }
188
189 fn parse_args() -> (OutputFormat, PathBuf) {
190     let mut args = env::args().skip(1);
191     let format = args.next();
192     let dst = args.next();
193     let resource_suffix = args.next().unwrap_or_else(String::new);
194     let format = format
195         .map(|a| OutputFormat::from(&a, &resource_suffix))
196         .unwrap_or(OutputFormat::from("html", &resource_suffix));
197     let dst = dst.map(PathBuf::from).unwrap_or_else(|| match format {
198         OutputFormat::HTML(..) => PathBuf::from("doc/error-index.html"),
199         OutputFormat::Markdown => PathBuf::from("doc/error-index.md"),
200         OutputFormat::Unknown(..) => PathBuf::from("<nul>"),
201     });
202     (format, dst)
203 }
204
205 fn main() {
206     rustc_driver::init_env_logger("RUST_LOG");
207     let (format, dst) = parse_args();
208     let result =
209         rustc_span::create_default_session_globals_then(move || main_with_result(format, &dst));
210     if let Err(e) = result {
211         panic!("{}", e.to_string());
212     }
213 }