]> git.proxmox.com Git - rustc.git/blob - src/tools/error_index_generator/main.rs
New upstream version 1.68.2+dfsg1
[rustc.git] / src / tools / error_index_generator / main.rs
1 #![feature(rustc_private)]
2
3 extern crate rustc_driver;
4
5 // We use the function we generate from `register_diagnostics!`.
6 use crate::error_codes::error_codes;
7
8 use std::env;
9 use std::error::Error;
10 use std::fs::{self, File};
11 use std::io::Write;
12 use std::path::Path;
13 use std::path::PathBuf;
14
15 use std::str::FromStr;
16
17 use mdbook::book::{parse_summary, BookItem, Chapter};
18 use mdbook::{Config, MDBook};
19
20 macro_rules! register_diagnostics {
21 ($($error_code:ident: $message:expr,)+ ; $($undocumented:ident,)* ) => {
22 pub fn error_codes() -> Vec<(&'static str, Option<&'static str>)> {
23 let mut errors: Vec<(&str, Option<&str>)> = vec![
24 $((stringify!($error_code), Some($message)),)+
25 $((stringify!($undocumented), None),)+
26 ];
27 errors.sort();
28 errors
29 }
30 }
31 }
32
33 #[path = "../../../compiler/rustc_error_codes/src/error_codes.rs"]
34 mod error_codes;
35
36 enum OutputFormat {
37 HTML,
38 Markdown,
39 Unknown(String),
40 }
41
42 impl OutputFormat {
43 fn from(format: &str) -> OutputFormat {
44 match &*format.to_lowercase() {
45 "html" => OutputFormat::HTML,
46 "markdown" => OutputFormat::Markdown,
47 s => OutputFormat::Unknown(s.to_owned()),
48 }
49 }
50 }
51
52 /// Output an HTML page for the errors in `err_map` to `output_path`.
53 fn render_markdown(output_path: &Path) -> Result<(), Box<dyn Error>> {
54 let mut output_file = File::create(output_path)?;
55
56 write!(output_file, "# Rust Compiler Error Index\n")?;
57
58 for (err_code, description) in error_codes().iter() {
59 match description {
60 Some(ref desc) => write!(output_file, "## {}\n{}\n", err_code, desc)?,
61 None => {}
62 }
63 }
64
65 Ok(())
66 }
67
68 // By default, mdbook doesn't consider code blocks as Rust ones contrary to rustdoc so we have
69 // to manually add `rust` attribute whenever needed.
70 fn add_rust_attribute_on_codeblock(explanation: &str) -> String {
71 // Very hacky way to add the rust attribute on all code blocks.
72 let mut skip = true;
73 explanation.split("\n```").fold(String::new(), |mut acc, part| {
74 if !acc.is_empty() {
75 acc.push_str("\n```");
76 }
77 if !skip {
78 if let Some(attrs) = part.split('\n').next() {
79 if !attrs.contains("rust")
80 && (attrs.is_empty()
81 || attrs.contains("compile_fail")
82 || attrs.contains("ignore")
83 || attrs.contains("edition"))
84 {
85 if !attrs.is_empty() {
86 acc.push_str("rust,");
87 } else {
88 acc.push_str("rust");
89 }
90 }
91 }
92 }
93 skip = !skip;
94 acc.push_str(part);
95 acc
96 })
97 }
98
99 fn render_html(output_path: &Path) -> Result<(), Box<dyn Error>> {
100 let mut introduction = format!(
101 "# Rust error codes index
102
103 This page lists all the error codes emitted by the Rust compiler.
104
105 "
106 );
107
108 let err_codes = error_codes();
109 let mut chapters = Vec::with_capacity(err_codes.len());
110
111 for (err_code, explanation) in err_codes.iter() {
112 if let Some(explanation) = explanation {
113 introduction.push_str(&format!(" * [{0}](./{0}.html)\n", err_code));
114
115 let content = add_rust_attribute_on_codeblock(explanation);
116 chapters.push(BookItem::Chapter(Chapter {
117 name: err_code.to_string(),
118 content: format!("# Error code {}\n\n{}\n", err_code, content),
119 number: None,
120 sub_items: Vec::new(),
121 // We generate it into the `error_codes` folder.
122 path: Some(PathBuf::from(&format!("{}.html", err_code))),
123 source_path: None,
124 parent_names: Vec::new(),
125 }));
126 } else {
127 introduction.push_str(&format!(" * {}\n", err_code));
128 }
129 }
130
131 let mut config = Config::from_str(include_str!("book_config.toml"))?;
132 config.build.build_dir = output_path.join("error_codes").to_path_buf();
133 let mut book = MDBook::load_with_config_and_summary(
134 env!("CARGO_MANIFEST_DIR"),
135 config,
136 parse_summary("")?,
137 )?;
138 let chapter = Chapter {
139 name: "Rust error codes index".to_owned(),
140 content: introduction,
141 number: None,
142 sub_items: chapters,
143 // Very important: this file is named as `error-index.html` and not `index.html`!
144 path: Some(PathBuf::from("error-index.html")),
145 source_path: None,
146 parent_names: Vec::new(),
147 };
148 book.book.sections.push(BookItem::Chapter(chapter));
149 book.build()?;
150
151 // The error-index used to be generated manually (without mdbook), and the
152 // index was located at the top level. Now that it is generated with
153 // mdbook, error-index.html has moved to error_codes/error-index.html.
154 // This adds a redirect so that old links go to the new location.
155 //
156 // We can't put this content into another file, otherwise `mdbook` will also put it into the
157 // output directory, making a duplicate.
158 fs::write(
159 output_path.join("error-index.html"),
160 r#"<!DOCTYPE html>
161 <html>
162 <head>
163 <title>Rust error codes index - Error codes index</title>
164 <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
165 <meta name="description" content="Book listing all Rust error codes">
166 <script src="error_codes/redirect.js"></script>
167 </head>
168 <body>
169 <div>If you are not automatically redirected to the error code index, please <a id="index-link" href="./error_codes/error-index.html">here</a>.
170 </body>
171 </html>"#,
172 )?;
173
174 Ok(())
175 }
176
177 fn main_with_result(format: OutputFormat, dst: &Path) -> Result<(), Box<dyn Error>> {
178 match format {
179 OutputFormat::Unknown(s) => panic!("Unknown output format: {}", s),
180 OutputFormat::HTML => render_html(dst),
181 OutputFormat::Markdown => render_markdown(dst),
182 }
183 }
184
185 fn parse_args() -> (OutputFormat, PathBuf) {
186 let mut args = env::args().skip(1);
187 let format = args.next();
188 let dst = args.next();
189 let format = format.map(|a| OutputFormat::from(&a)).unwrap_or(OutputFormat::from("html"));
190 let dst = dst.map(PathBuf::from).unwrap_or_else(|| match format {
191 OutputFormat::HTML => PathBuf::from("doc"),
192 OutputFormat::Markdown => PathBuf::from("doc/error-index.md"),
193 OutputFormat::Unknown(..) => PathBuf::from("<nul>"),
194 });
195 (format, dst)
196 }
197
198 fn main() {
199 rustc_driver::init_env_logger("RUST_LOG");
200 let (format, dst) = parse_args();
201 let result = main_with_result(format, &dst);
202 if let Err(e) = result {
203 panic!("{:?}", e);
204 }
205 }