]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_driver/src/lib.rs
New upstream version 1.61.0+dfsg1
[rustc.git] / compiler / rustc_driver / src / lib.rs
CommitLineData
1a4d82fc
JJ
1//! The Rust compiler.
2//!
3//! # Note
4//!
5//! This API is completely unstable and subject to change.
6
1b1a35ee 7#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
0bf4aa26 8#![feature(nll)]
5e7ed085 9#![feature(let_else)]
1b1a35ee 10#![feature(once_cell)]
dfeec247 11#![recursion_limit = "256"]
5e7ed085 12#![allow(rustc::potential_query_instability)]
94b46f34 13
92a42be0 14#[macro_use]
3dfed10e 15extern crate tracing;
e1599b0c
XL
16
17pub extern crate rustc_plugin_impl as plugin;
1a4d82fc 18
3dfed10e 19use rustc_ast as ast;
ba9703b0 20use rustc_codegen_ssa::{traits::CodegenBackend, CodegenResults};
5869c6ff 21use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
532ac7d7 22use rustc_data_structures::sync::SeqCst;
ba9703b0 23use rustc_errors::registry::{InvalidErrorCode, Registry};
5e7ed085 24use rustc_errors::{ErrorGuaranteed, PResult};
fc512014 25use rustc_feature::find_gated_cfg;
17df50a5 26use rustc_interface::util::{self, collect_crate_types, get_codegen_backend};
dfeec247
XL
27use rustc_interface::{interface, Queries};
28use rustc_lint::LintStore;
a2a8927a 29use rustc_log::stdout_isatty;
dfeec247
XL
30use rustc_metadata::locator;
31use rustc_save_analysis as save;
32use rustc_save_analysis::DumpHandler;
5099ac24 33use rustc_serialize::json::ToJson;
136023e0 34use rustc_session::config::{nightly_options, CG_OPTIONS, DB_OPTIONS};
1b1a35ee 35use rustc_session::config::{ErrorOutputType, Input, OutputType, PrintRequest, TrimmedDefPaths};
c295e0f8 36use rustc_session::cstore::MetadataLoader;
ba9703b0
XL
37use rustc_session::getopts;
38use rustc_session::lint::{Lint, LintId};
39use rustc_session::{config, DiagnosticOutput, Session};
17df50a5 40use rustc_session::{early_error, early_error_no_abort, early_warn};
ba9703b0
XL
41use rustc_span::source_map::{FileLoader, FileName};
42use rustc_span::symbol::sym;
476ff2be 43
0bf4aa26 44use std::borrow::Cow;
2c00a5a8 45use std::cmp::max;
9cc50fc6 46use std::default::Default;
85aaf69f 47use std::env;
041b39d2 48use std::ffi::OsString;
74b04a01 49use std::fs;
c34b1796 50use std::io::{self, Read, Write};
1b1a35ee 51use std::lazy::SyncLazy;
532ac7d7
XL
52use std::panic::{self, catch_unwind};
53use std::path::PathBuf;
041b39d2 54use std::process::{self, Command, Stdio};
c34b1796 55use std::str;
416331ca 56use std::time::Instant;
1a4d82fc 57
6a06907d 58pub mod args;
dfeec247 59pub mod pretty;
2c00a5a8 60
8faf50e0 61/// Exit status code used for successful compilation and help output.
532ac7d7 62pub const EXIT_SUCCESS: i32 = 0;
8faf50e0 63
416331ca 64/// Exit status code used for compilation failures and invalid flags.
532ac7d7 65pub const EXIT_FAILURE: i32 = 1;
8faf50e0 66
3dfed10e
XL
67const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust/issues/new\
68 ?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md";
0bf4aa26 69
5099ac24 70const ICE_REPORT_COMPILER_FLAGS: &[&str] = &["-Z", "-C", "--crate-type"];
0bf4aa26
XL
71
72const ICE_REPORT_COMPILER_FLAGS_EXCLUDE: &[&str] = &["metadata", "extra-filename"];
73
74const ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE: &[&str] = &["incremental"];
0531ce1d 75
5e7ed085 76pub fn abort_on_err<T>(result: Result<T, ErrorGuaranteed>, sess: &Session) -> T {
7453a54e 77 match result {
532ac7d7 78 Err(..) => {
041b39d2
XL
79 sess.abort_if_errors();
80 panic!("error reported but abort_if_errors didn't abort???");
81 }
7453a54e
SL
82 Ok(x) => x,
83 }
84}
85
532ac7d7
XL
86pub trait Callbacks {
87 /// Called before creating the compiler instance
88 fn config(&mut self, _config: &mut interface::Config) {}
416331ca
XL
89 /// Called after parsing. Return value instructs the compiler whether to
90 /// continue the compilation afterwards (defaults to `Compilation::Continue`)
60c5eb7d
XL
91 fn after_parsing<'tcx>(
92 &mut self,
93 _compiler: &interface::Compiler,
94 _queries: &'tcx Queries<'tcx>,
95 ) -> Compilation {
416331ca 96 Compilation::Continue
8faf50e0 97 }
416331ca
XL
98 /// Called after expansion. Return value instructs the compiler whether to
99 /// continue the compilation afterwards (defaults to `Compilation::Continue`)
60c5eb7d
XL
100 fn after_expansion<'tcx>(
101 &mut self,
102 _compiler: &interface::Compiler,
103 _queries: &'tcx Queries<'tcx>,
104 ) -> Compilation {
416331ca
XL
105 Compilation::Continue
106 }
107 /// Called after analysis. Return value instructs the compiler whether to
108 /// continue the compilation afterwards (defaults to `Compilation::Continue`)
60c5eb7d
XL
109 fn after_analysis<'tcx>(
110 &mut self,
111 _compiler: &interface::Compiler,
112 _queries: &'tcx Queries<'tcx>,
113 ) -> Compilation {
416331ca 114 Compilation::Continue
2c00a5a8
XL
115 }
116}
3b2f2976 117
416331ca
XL
118#[derive(Default)]
119pub struct TimePassesCallbacks {
120 time_passes: bool,
121}
122
123impl Callbacks for TimePassesCallbacks {
124 fn config(&mut self, config: &mut interface::Config) {
e1599b0c
XL
125 // If a --prints=... option has been given, we don't print the "total"
126 // time because it will mess up the --prints output. See #64339.
dfeec247
XL
127 self.time_passes = config.opts.prints.is_empty()
128 && (config.opts.debugging_opts.time_passes || config.opts.debugging_opts.time);
1b1a35ee 129 config.opts.trimmed_def_paths = TrimmedDefPaths::GoodPath;
416331ca
XL
130 }
131}
132
60c5eb7d 133pub fn diagnostics_registry() -> Registry {
c295e0f8 134 Registry::new(rustc_error_codes::DIAGNOSTICS)
60c5eb7d
XL
135}
136
17df50a5 137/// This is the primary entry point for rustc.
2a314972
XL
138pub struct RunCompiler<'a, 'b> {
139 at_args: &'a [String],
140 callbacks: &'b mut (dyn Callbacks + Send),
141 file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
142 emitter: Option<Box<dyn Write + Send>>,
143 make_codegen_backend:
144 Option<Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>>,
145}
146
147impl<'a, 'b> RunCompiler<'a, 'b> {
148 pub fn new(at_args: &'a [String], callbacks: &'b mut (dyn Callbacks + Send)) -> Self {
149 Self { at_args, callbacks, file_loader: None, emitter: None, make_codegen_backend: None }
150 }
17df50a5
XL
151
152 /// Set a custom codegen backend.
153 ///
cdc7bbd5 154 /// Used by cg_clif.
2a314972
XL
155 pub fn set_make_codegen_backend(
156 &mut self,
157 make_codegen_backend: Option<
158 Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>,
159 >,
160 ) -> &mut Self {
161 self.make_codegen_backend = make_codegen_backend;
162 self
163 }
17df50a5
XL
164
165 /// Emit diagnostics to the specified location.
166 ///
cdc7bbd5 167 /// Used by RLS.
2a314972
XL
168 pub fn set_emitter(&mut self, emitter: Option<Box<dyn Write + Send>>) -> &mut Self {
169 self.emitter = emitter;
170 self
171 }
17df50a5
XL
172
173 /// Load files from sources other than the file system.
174 ///
cdc7bbd5 175 /// Used by RLS.
2a314972
XL
176 pub fn set_file_loader(
177 &mut self,
178 file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
179 ) -> &mut Self {
180 self.file_loader = file_loader;
181 self
182 }
17df50a5
XL
183
184 /// Parse args and run the compiler.
2a314972
XL
185 pub fn run(self) -> interface::Result<()> {
186 run_compiler(
187 self.at_args,
188 self.callbacks,
189 self.file_loader,
190 self.emitter,
191 self.make_codegen_backend,
192 )
193 }
194}
2a314972 195fn run_compiler(
e1599b0c 196 at_args: &[String],
532ac7d7
XL
197 callbacks: &mut (dyn Callbacks + Send),
198 file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
dfeec247 199 emitter: Option<Box<dyn Write + Send>>,
1b1a35ee
XL
200 make_codegen_backend: Option<
201 Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>,
202 >,
532ac7d7 203) -> interface::Result<()> {
6a06907d
XL
204 let args = args::arg_expand_all(at_args);
205
29967ef6 206 let diagnostic_output = emitter.map_or(DiagnosticOutput::Default, DiagnosticOutput::Raw);
5e7ed085 207 let Some(matches) = handle_options(&args) else { return Ok(()) };
2c00a5a8 208
e74abb32 209 let sopts = config::build_session_options(&matches);
532ac7d7
XL
210
211 if let Some(ref code) = matches.opt_str("explain") {
60c5eb7d 212 handle_explain(diagnostics_registry(), code, sopts.error_format);
532ac7d7 213 return Ok(());
2c00a5a8
XL
214 }
215
17df50a5 216 let cfg = interface::parse_cfgspecs(matches.opt_strs("cfg"));
5099ac24 217 let check_cfg = interface::parse_check_cfg(matches.opt_strs("check-cfg"));
532ac7d7 218 let (odir, ofile) = make_output(&matches);
17df50a5
XL
219 let mut config = interface::Config {
220 opts: sopts,
221 crate_cfg: cfg,
5099ac24 222 crate_check_cfg: check_cfg,
17df50a5
XL
223 input: Input::File(PathBuf::new()),
224 input_path: None,
225 output_file: ofile,
226 output_dir: odir,
227 file_loader,
228 diagnostic_output,
17df50a5
XL
229 lint_caps: Default::default(),
230 parse_sess_created: None,
231 register_lints: None,
232 override_queries: None,
233 make_codegen_backend,
234 registry: diagnostics_registry(),
235 };
236
237 match make_input(config.opts.error_format, &matches.free) {
5e7ed085 238 Err(reported) => return Err(reported),
17df50a5
XL
239 Ok(Some((input, input_file_path))) => {
240 config.input = input;
241 config.input_path = input_file_path;
242
243 callbacks.config(&mut config);
244 }
245 Ok(None) => match matches.free.len() {
dfeec247 246 0 => {
17df50a5 247 callbacks.config(&mut config);
dfeec247
XL
248 interface::run_compiler(config, |compiler| {
249 let sopts = &compiler.session().opts;
250 if sopts.describe_lints {
fc512014 251 let mut lint_store = rustc_lint::new_lint_store(
dfeec247
XL
252 sopts.debugging_opts.no_interleave_lints,
253 compiler.session().unstable_options(),
532ac7d7 254 );
fc512014
XL
255 let registered_lints =
256 if let Some(register_lints) = compiler.register_lints() {
257 register_lints(compiler.session(), &mut lint_store);
258 true
259 } else {
260 false
261 };
262 describe_lints(compiler.session(), &lint_store, registered_lints);
dfeec247
XL
263 return;
264 }
5099ac24 265 let should_stop = print_crate_info(
dfeec247
XL
266 &***compiler.codegen_backend(),
267 compiler.session(),
268 None,
c295e0f8
XL
269 compiler.output_dir(),
270 compiler.output_file(),
3c0e092e 271 compiler.temps_dir(),
dfeec247 272 );
2c00a5a8 273
dfeec247
XL
274 if should_stop == Compilation::Stop {
275 return;
276 }
277 early_error(sopts.error_format, "no input filename given")
278 });
279 return Ok(());
2c00a5a8 280 }
dfeec247
XL
281 1 => panic!("make_input should have provided valid inputs"),
282 _ => early_error(
17df50a5 283 config.opts.error_format,
dfeec247
XL
284 &format!(
285 "multiple input filenames provided (first two filenames are `{}` and `{}`)",
286 matches.free[0], matches.free[1],
287 ),
288 ),
289 },
532ac7d7
XL
290 };
291
532ac7d7
XL
292 interface::run_compiler(config, |compiler| {
293 let sess = compiler.session();
5099ac24 294 let should_stop = print_crate_info(
532ac7d7
XL
295 &***compiler.codegen_backend(),
296 sess,
297 Some(compiler.input()),
298 compiler.output_dir(),
299 compiler.output_file(),
3c0e092e 300 compiler.temps_dir(),
dfeec247
XL
301 )
302 .and_then(|| {
5099ac24 303 list_metadata(sess, &*compiler.codegen_backend().metadata_loader(), compiler.input())
74b04a01 304 })
5099ac24 305 .and_then(|| try_process_rlink(sess, compiler));
532ac7d7
XL
306
307 if should_stop == Compilation::Stop {
308 return sess.compile_status();
2c00a5a8
XL
309 }
310
60c5eb7d
XL
311 let linker = compiler.enter(|queries| {
312 let early_exit = || sess.compile_status().map(|_| None);
313 queries.parse()?;
314
315 if let Some(ppm) = &sess.opts.pretty {
316 if ppm.needs_ast_map() {
136023e0 317 let expanded_crate = queries.expansion()?.peek().0.clone();
60c5eb7d 318 queries.global_ctxt()?.peek_mut().enter(|tcx| {
60c5eb7d
XL
319 pretty::print_after_hir_lowering(
320 tcx,
321 compiler.input(),
136023e0 322 &*expanded_crate,
60c5eb7d
XL
323 *ppm,
324 compiler.output_file().as_ref().map(|p| &**p),
325 );
326 Ok(())
327 })?;
328 } else {
329 let krate = queries.parse()?.take();
330 pretty::print_after_parsing(
331 sess,
c295e0f8 332 compiler.input(),
60c5eb7d
XL
333 &krate,
334 *ppm,
532ac7d7
XL
335 compiler.output_file().as_ref().map(|p| &**p),
336 );
60c5eb7d 337 }
f035d41b 338 trace!("finished pretty-printing");
60c5eb7d 339 return early_exit();
2c00a5a8 340 }
3b2f2976 341
60c5eb7d
XL
342 if callbacks.after_parsing(compiler, queries) == Compilation::Stop {
343 return early_exit();
344 }
94b46f34 345
dfeec247
XL
346 if sess.opts.debugging_opts.parse_only
347 || sess.opts.debugging_opts.show_span.is_some()
348 || sess.opts.debugging_opts.ast_json_noexpand
349 {
350 return early_exit();
60c5eb7d 351 }
94b46f34 352
60c5eb7d
XL
353 {
354 let (_, lint_store) = &*queries.register_plugins()?.peek();
0531ce1d 355
60c5eb7d
XL
356 // Lint plugins are registered; now we can process command line flags.
357 if sess.opts.describe_lints {
c295e0f8 358 describe_lints(sess, lint_store, true);
60c5eb7d
XL
359 return early_exit();
360 }
e74abb32 361 }
1a4d82fc 362
60c5eb7d
XL
363 queries.expansion()?;
364 if callbacks.after_expansion(compiler, queries) == Compilation::Stop {
365 return early_exit();
366 }
416331ca 367
60c5eb7d 368 queries.prepare_outputs()?;
85aaf69f 369
60c5eb7d
XL
370 if sess.opts.output_types.contains_key(&OutputType::DepInfo)
371 && sess.opts.output_types.len() == 1
372 {
373 return early_exit();
374 }
85aaf69f 375
60c5eb7d 376 queries.global_ctxt()?;
85aaf69f 377
dfeec247
XL
378 if sess.opts.debugging_opts.no_analysis || sess.opts.debugging_opts.ast_json {
379 return early_exit();
60c5eb7d 380 }
2c00a5a8 381
17df50a5
XL
382 queries.global_ctxt()?.peek_mut().enter(|tcx| {
383 let result = tcx.analysis(());
384 if sess.opts.debugging_opts.save_analysis {
385 let crate_name = queries.crate_name()?.peek().clone();
dfeec247 386 sess.time("save_analysis", || {
60c5eb7d
XL
387 save::process_crate(
388 tcx,
60c5eb7d 389 &crate_name,
c295e0f8 390 compiler.input(),
60c5eb7d
XL
391 None,
392 DumpHandler::new(
dfeec247
XL
393 compiler.output_dir().as_ref().map(|p| &**p),
394 &crate_name,
395 ),
60c5eb7d
XL
396 )
397 });
17df50a5
XL
398 }
399 result
400 })?;
c30ab7b3 401
60c5eb7d
XL
402 if callbacks.after_analysis(compiler, queries) == Compilation::Stop {
403 return early_exit();
404 }
405
60c5eb7d 406 queries.ongoing_codegen()?;
85aaf69f 407
60c5eb7d
XL
408 if sess.opts.debugging_opts.print_type_sizes {
409 sess.code_stats.print_type_sizes();
410 }
0531ce1d 411
60c5eb7d
XL
412 let linker = queries.linker()?;
413 Ok(Some(linker))
414 })?;
0531ce1d 415
60c5eb7d 416 if let Some(linker) = linker {
dfeec247 417 let _timer = sess.timer("link");
60c5eb7d 418 linker.link()?
532ac7d7 419 }
0531ce1d 420
532ac7d7
XL
421 if sess.opts.debugging_opts.perf_stats {
422 sess.print_perf_stats();
423 }
0531ce1d 424
94222f64 425 if sess.opts.debugging_opts.print_fuel.is_some() {
dfeec247
XL
426 eprintln!(
427 "Fuel used by {}: {}",
94222f64 428 sess.opts.debugging_opts.print_fuel.as_ref().unwrap(),
dfeec247
XL
429 sess.print_fuel.load(SeqCst)
430 );
532ac7d7 431 }
0531ce1d 432
532ac7d7
XL
433 Ok(())
434 })
85aaf69f
SL
435}
436
83c7162d
XL
437#[cfg(unix)]
438pub fn set_sigpipe_handler() {
439 unsafe {
440 // Set the SIGPIPE signal handler, so that an EPIPE
441 // will cause rustc to terminate, as expected.
0bf4aa26 442 assert_ne!(libc::signal(libc::SIGPIPE, libc::SIG_DFL), libc::SIG_ERR);
83c7162d
XL
443 }
444}
445
446#[cfg(windows)]
447pub fn set_sigpipe_handler() {}
448
85aaf69f 449// Extract output directory and file from matches.
c34b1796
AL
450fn make_output(matches: &getopts::Matches) -> (Option<PathBuf>, Option<PathBuf>) {
451 let odir = matches.opt_str("out-dir").map(|o| PathBuf::from(&o));
452 let ofile = matches.opt_str("o").map(|o| PathBuf::from(&o));
85aaf69f
SL
453 (odir, ofile)
454}
455
456// Extract input (string or file and optional path) from matches.
17df50a5
XL
457fn make_input(
458 error_format: ErrorOutputType,
459 free_matches: &[String],
5e7ed085 460) -> Result<Option<(Input, Option<PathBuf>)>, ErrorGuaranteed> {
85aaf69f 461 if free_matches.len() == 1 {
cc61c64b 462 let ifile = &free_matches[0];
85aaf69f 463 if ifile == "-" {
c34b1796 464 let mut src = String::new();
17df50a5
XL
465 if io::stdin().read_to_string(&mut src).is_err() {
466 // Immediately stop compilation if there was an issue reading
467 // the input (for example if the input stream is not UTF-8).
5e7ed085 468 let reported = early_error_no_abort(
17df50a5 469 error_format,
dfeec247 470 "couldn't read from stdin, as it did not contain valid UTF-8",
17df50a5 471 );
5e7ed085 472 return Err(reported);
17df50a5 473 }
e1599b0c 474 if let Ok(path) = env::var("UNSTABLE_RUSTDOC_TEST_PATH") {
dfeec247
XL
475 let line = env::var("UNSTABLE_RUSTDOC_TEST_LINE").expect(
476 "when UNSTABLE_RUSTDOC_TEST_PATH is set \
477 UNSTABLE_RUSTDOC_TEST_LINE also needs to be set",
478 );
479 let line = isize::from_str_radix(&line, 10)
480 .expect("UNSTABLE_RUSTDOC_TEST_LINE needs to be an number");
e1599b0c 481 let file_name = FileName::doc_test_source_code(PathBuf::from(path), line);
17df50a5
XL
482 Ok(Some((Input::Str { name: file_name, input: src }, None)))
483 } else {
484 Ok(Some((Input::Str { name: FileName::anon_source_code(&src), input: src }, None)))
e1599b0c 485 }
85aaf69f 486 } else {
17df50a5 487 Ok(Some((Input::File(PathBuf::from(ifile)), Some(PathBuf::from(ifile)))))
1a4d82fc 488 }
85aaf69f 489 } else {
17df50a5 490 Ok(None)
85aaf69f
SL
491 }
492}
493
17df50a5 494/// Whether to stop or continue compilation.
c34b1796 495#[derive(Copy, Clone, Debug, Eq, PartialEq)]
85aaf69f
SL
496pub enum Compilation {
497 Stop,
498 Continue,
499}
500
501impl Compilation {
502 pub fn and_then<F: FnOnce() -> Compilation>(self, next: F) -> Compilation {
503 match self {
504 Compilation::Stop => Compilation::Stop,
92a42be0 505 Compilation::Continue => next(),
1a4d82fc 506 }
85aaf69f
SL
507 }
508}
1a4d82fc 509
60c5eb7d 510fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) {
136023e0
XL
511 let upper_cased_code = code.to_ascii_uppercase();
512 let normalised = if upper_cased_code.starts_with('E') {
513 upper_cased_code
514 } else {
515 format!("E{0:0>4}", code)
516 };
74b04a01
XL
517 match registry.try_find_description(&normalised) {
518 Ok(Some(description)) => {
041b39d2
XL
519 let mut is_in_code_block = false;
520 let mut text = String::new();
7453a54e 521 // Slice off the leading newline and print.
60c5eb7d 522 for line in description.lines() {
dfeec247
XL
523 let indent_level =
524 line.find(|c: char| !c.is_whitespace()).unwrap_or_else(|| line.len());
041b39d2
XL
525 let dedented_line = &line[indent_level..];
526 if dedented_line.starts_with("```") {
527 is_in_code_block = !is_in_code_block;
60c5eb7d 528 text.push_str(&line[..(indent_level + 3)]);
041b39d2
XL
529 } else if is_in_code_block && dedented_line.starts_with("# ") {
530 continue;
a7813a04 531 } else {
041b39d2
XL
532 text.push_str(line);
533 }
534 text.push('\n');
535 }
041b39d2
XL
536 if stdout_isatty() {
537 show_content_with_pager(&text);
538 } else {
539 print!("{}", text);
540 }
7453a54e 541 }
74b04a01 542 Ok(None) => {
7453a54e
SL
543 early_error(output, &format!("no extended information for {}", code));
544 }
74b04a01
XL
545 Err(InvalidErrorCode) => {
546 early_error(output, &format!("{} is not a valid error code", code));
547 }
7453a54e
SL
548 }
549}
550
5869c6ff 551fn show_content_with_pager(content: &str) {
dfeec247
XL
552 let pager_name = env::var_os("PAGER").unwrap_or_else(|| {
553 if cfg!(windows) { OsString::from("more.com") } else { OsString::from("less") }
041b39d2
XL
554 });
555
556 let mut fallback_to_println = false;
557
558 match Command::new(pager_name).stdin(Stdio::piped()).spawn() {
559 Ok(mut pager) => {
3b2f2976 560 if let Some(pipe) = pager.stdin.as_mut() {
041b39d2
XL
561 if pipe.write_all(content.as_bytes()).is_err() {
562 fallback_to_println = true;
563 }
564 }
565
566 if pager.wait().is_err() {
567 fallback_to_println = true;
568 }
569 }
570 Err(_) => {
571 fallback_to_println = true;
572 }
573 }
574
575 // If pager fails for whatever reason, we should still print the content
576 // to standard output
577 if fallback_to_println {
578 print!("{}", content);
579 }
580}
581
5099ac24
FG
582pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Compilation {
583 if sess.opts.debugging_opts.link_only {
584 if let Input::File(file) = compiler.input() {
585 // FIXME: #![crate_type] and #![crate_name] support not implemented yet
586 sess.init_crate_types(collect_crate_types(sess, &[]));
587 let outputs = compiler.build_output_filenames(sess, &[]);
588 let rlink_data = fs::read(file).unwrap_or_else(|err| {
589 sess.fatal(&format!("failed to read rlink file: {}", err));
590 });
5e7ed085
FG
591 let codegen_results = match CodegenResults::deserialize_rlink(rlink_data) {
592 Ok(codegen) => codegen,
593 Err(error) => {
594 sess.fatal(&format!("Could not deserialize .rlink file: {error}"));
595 }
596 };
5099ac24
FG
597 let result = compiler.codegen_backend().link(sess, codegen_results, &outputs);
598 abort_on_err(result, sess);
74b04a01 599 } else {
5099ac24 600 sess.fatal("rlink must be a file")
74b04a01 601 }
5099ac24
FG
602 Compilation::Stop
603 } else {
604 Compilation::Continue
74b04a01 605 }
5099ac24 606}
74b04a01 607
5099ac24
FG
608pub fn list_metadata(
609 sess: &Session,
610 metadata_loader: &dyn MetadataLoader,
611 input: &Input,
612) -> Compilation {
613 if sess.opts.debugging_opts.ls {
614 match *input {
615 Input::File(ref ifile) => {
616 let path = &(*ifile);
617 let mut v = Vec::new();
618 locator::list_file_metadata(&sess.target, path, metadata_loader, &mut v).unwrap();
619 println!("{}", String::from_utf8(v).unwrap());
620 }
621 Input::Str { .. } => {
622 early_error(ErrorOutputType::default(), "cannot list metadata for stdin");
1a4d82fc
JJ
623 }
624 }
5099ac24 625 return Compilation::Stop;
1a4d82fc
JJ
626 }
627
5099ac24
FG
628 Compilation::Continue
629}
85aaf69f 630
5099ac24
FG
631fn print_crate_info(
632 codegen_backend: &dyn CodegenBackend,
633 sess: &Session,
634 input: Option<&Input>,
635 odir: &Option<PathBuf>,
636 ofile: &Option<PathBuf>,
637 temps_dir: &Option<PathBuf>,
638) -> Compilation {
639 use rustc_session::config::PrintRequest::*;
640 // NativeStaticLibs and LinkArgs are special - printed during linking
641 // (empty iterator returns true)
642 if sess.opts.prints.iter().all(|&p| p == NativeStaticLibs || p == LinkArgs) {
643 return Compilation::Continue;
644 }
645
646 let attrs = match input {
647 None => None,
648 Some(input) => {
649 let result = parse_crate_attrs(sess, input);
650 match result {
651 Ok(attrs) => Some(attrs),
652 Err(mut parse_error) => {
653 parse_error.emit();
654 return Compilation::Stop;
54a0048b
SL
655 }
656 }
5099ac24
FG
657 }
658 };
659 for req in &sess.opts.prints {
660 match *req {
661 TargetList => {
662 let mut targets = rustc_target::spec::TARGETS.iter().copied().collect::<Vec<_>>();
663 targets.sort_unstable();
664 println!("{}", targets.join("\n"));
665 }
666 Sysroot => println!("{}", sess.sysroot.display()),
667 TargetLibdir => println!("{}", sess.target_tlib_path.dir.display()),
668 TargetSpec => println!("{}", sess.target.to_json().pretty()),
669 FileNames | CrateName => {
670 let input = input.unwrap_or_else(|| {
671 early_error(ErrorOutputType::default(), "no input file provided")
672 });
673 let attrs = attrs.as_ref().unwrap();
674 let t_outputs = rustc_interface::util::build_output_filenames(
675 input, odir, ofile, temps_dir, attrs, sess,
676 );
677 let id = rustc_session::output::find_crate_name(sess, attrs, input);
678 if *req == PrintRequest::CrateName {
679 println!("{}", id);
680 continue;
85aaf69f 681 }
5099ac24
FG
682 let crate_types = collect_crate_types(sess, attrs);
683 for &style in &crate_types {
684 let fname =
685 rustc_session::output::filename_for_input(sess, style, &id, &t_outputs);
686 println!("{}", fname.file_name().unwrap().to_string_lossy());
7453a54e 687 }
5099ac24
FG
688 }
689 Cfg => {
690 let mut cfgs = sess
691 .parse_sess
692 .config
693 .iter()
694 .filter_map(|&(name, value)| {
695 // Note that crt-static is a specially recognized cfg
696 // directive that's printed out here as part of
697 // rust-lang/rust#37406, but in general the
698 // `target_feature` cfg is gated under
699 // rust-lang/rust#29717. For now this is just
700 // specifically allowing the crt-static cfg and that's
701 // it, this is intended to get into Cargo and then go
702 // through to build scripts.
703 if (name != sym::target_feature || value != Some(sym::crt_dash_static))
704 && !sess.is_nightly_build()
705 && find_gated_cfg(|cfg_sym| cfg_sym == name).is_some()
706 {
707 return None;
708 }
709
710 if let Some(value) = value {
711 Some(format!("{}=\"{}\"", name, value))
712 } else {
713 Some(name.to_string())
714 }
715 })
716 .collect::<Vec<String>>();
717
718 cfgs.sort();
719 for cfg in cfgs {
720 println!("{}", cfg);
7cac9316 721 }
85aaf69f 722 }
5099ac24
FG
723 RelocationModels
724 | CodeModels
725 | TlsModels
726 | TargetCPUs
727 | StackProtectorStrategies
728 | TargetFeatures => {
729 codegen_backend.print(*req, sess);
730 }
731 // Any output here interferes with Cargo's parsing of other printed output
732 NativeStaticLibs => {}
733 LinkArgs => {}
85aaf69f 734 }
1a4d82fc 735 }
5099ac24 736 Compilation::Stop
1a4d82fc
JJ
737}
738
c1a9b12d 739/// Prints version information
1a4d82fc
JJ
740pub fn version(binary: &str, matches: &getopts::Matches) {
741 let verbose = matches.opt_present("verbose");
742
29967ef6 743 println!("{} {}", binary, util::version_str().unwrap_or("unknown version"));
0bf4aa26 744
1a4d82fc 745 if verbose {
92a42be0
SL
746 fn unw(x: Option<&str>) -> &str {
747 x.unwrap_or("unknown")
748 }
1a4d82fc 749 println!("binary: {}", binary);
29967ef6
XL
750 println!("commit-hash: {}", unw(util::commit_hash_str()));
751 println!("commit-date: {}", unw(util::commit_date_str()));
1a4d82fc 752 println!("host: {}", config::host_triple());
29967ef6 753 println!("release: {}", unw(util::release_str()));
17df50a5
XL
754
755 let debug_flags = matches.opt_strs("Z");
94222f64 756 let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend="));
17df50a5 757 get_codegen_backend(&None, backend_name).print_version();
1a4d82fc
JJ
758 }
759}
760
fc512014 761fn usage(verbose: bool, include_unstable_options: bool, nightly_build: bool) {
dfeec247 762 let groups = if verbose { config::rustc_optgroups() } else { config::rustc_short_optgroups() };
041b39d2
XL
763 let mut options = getopts::Options::new();
764 for option in groups.iter().filter(|x| include_unstable_options || x.is_stable()) {
765 (option.apply)(&mut options);
766 }
0bf4aa26 767 let message = "Usage: rustc [OPTIONS] INPUT";
fc512014 768 let nightly_help = if nightly_build {
48663c56 769 "\n -Z help Print unstable compiler options"
3b2f2976
XL
770 } else {
771 ""
772 };
773 let verbose_help = if verbose {
1a4d82fc
JJ
774 ""
775 } else {
776 "\n --help -v Print the full set of options rustc accepts"
777 };
6a06907d 778 let at_path = if verbose {
e1599b0c
XL
779 " @path Read newline separated options from `path`\n"
780 } else {
781 ""
782 };
dfeec247
XL
783 println!(
784 "{options}{at_path}\nAdditional help:
1a4d82fc 785 -C help Print codegen options
92a42be0 786 -W help \
e1599b0c 787 Print 'lint' options and default settings{nightly}{verbose}\n",
dfeec247
XL
788 options = options.usage(message),
789 at_path = at_path,
790 nightly = nightly_help,
791 verbose = verbose_help
792 );
1a4d82fc
JJ
793}
794
0531ce1d 795fn print_wall_help() {
dfeec247
XL
796 println!(
797 "
0531ce1d
XL
798The flag `-Wall` does not exist in `rustc`. Most useful lints are enabled by
799default. Use `rustc -W help` to see all available lints. It's more common to put
800warning settings in the crate root using `#![warn(LINT_NAME)]` instead of using
801the command line flag directly.
dfeec247
XL
802"
803 );
0531ce1d
XL
804}
805
cdc7bbd5
XL
806/// Write to stdout lint command options, together with a list of all available lints
807pub fn describe_lints(sess: &Session, lint_store: &LintStore, loaded_plugins: bool) {
dfeec247
XL
808 println!(
809 "
1a4d82fc
JJ
810Available lint options:
811 -W <foo> Warn about <foo>
92a42be0
SL
812 -A <foo> \
813 Allow <foo>
1a4d82fc 814 -D <foo> Deny <foo>
92a42be0 815 -F <foo> Forbid <foo> \
8bb4bdeb 816 (deny <foo> and all attempts to override)
1a4d82fc 817
dfeec247
XL
818"
819 );
1a4d82fc 820
e74abb32 821 fn sort_lints(sess: &Session, mut lints: Vec<&'static Lint>) -> Vec<&'static Lint> {
83c7162d 822 // The sort doesn't case-fold but it's doubtful we care.
60c5eb7d 823 lints.sort_by_cached_key(|x: &&Lint| (x.default_level(sess.edition()), x.name));
1a4d82fc
JJ
824 lints
825 }
826
dfeec247
XL
827 fn sort_lint_groups(
828 lints: Vec<(&'static str, Vec<LintId>, bool)>,
829 ) -> Vec<(&'static str, Vec<LintId>)> {
1a4d82fc 830 let mut lints: Vec<_> = lints.into_iter().map(|(x, y, _)| (x, y)).collect();
8faf50e0 831 lints.sort_by_key(|l| l.0);
1a4d82fc
JJ
832 lints
833 }
834
dfeec247
XL
835 let (plugin, builtin): (Vec<_>, _) =
836 lint_store.get_lints().iter().cloned().partition(|&lint| lint.is_plugin);
0531ce1d
XL
837 let plugin = sort_lints(sess, plugin);
838 let builtin = sort_lints(sess, builtin);
1a4d82fc 839
dfeec247 840 let (plugin_groups, builtin_groups): (Vec<_>, _) =
5099ac24 841 lint_store.get_lint_groups().partition(|&(.., p)| p);
1a4d82fc
JJ
842 let plugin_groups = sort_lint_groups(plugin_groups);
843 let builtin_groups = sort_lint_groups(builtin_groups);
844
dfeec247
XL
845 let max_name_len =
846 plugin.iter().chain(&builtin).map(|&s| s.name.chars().count()).max().unwrap_or(0);
85aaf69f 847 let padded = |x: &str| {
8faf50e0 848 let mut s = " ".repeat(max_name_len - x.chars().count());
1a4d82fc
JJ
849 s.push_str(x);
850 s
851 };
852
853 println!("Lint checks provided by rustc:\n");
854 println!(" {} {:7.7} {}", padded("name"), "default", "meaning");
855 println!(" {} {:7.7} {}", padded("----"), "-------", "-------");
856
85aaf69f
SL
857 let print_lints = |lints: Vec<&Lint>| {
858 for lint in lints {
a2a8927a 859 let name = lint.name_lower().replace('_', "-");
cdc7bbd5
XL
860 println!(
861 " {} {:7.7} {}",
862 padded(&name),
863 lint.default_level(sess.edition()).as_str(),
864 lint.desc
865 );
1a4d82fc
JJ
866 }
867 println!("\n");
868 };
869
870 print_lints(builtin);
871
dfeec247
XL
872 let max_name_len = max(
873 "warnings".len(),
874 plugin_groups
875 .iter()
876 .chain(&builtin_groups)
877 .map(|&(s, _)| s.chars().count())
878 .max()
879 .unwrap_or(0),
880 );
9cc50fc6 881
85aaf69f 882 let padded = |x: &str| {
8faf50e0 883 let mut s = " ".repeat(max_name_len - x.chars().count());
1a4d82fc
JJ
884 s.push_str(x);
885 s
886 };
887
888 println!("Lint groups provided by rustc:\n");
c295e0f8
XL
889 println!(" {} sub-lints", padded("name"));
890 println!(" {} ---------", padded("----"));
891 println!(" {} all lints that are set to issue warnings", padded("warnings"));
1a4d82fc 892
dfeec247 893 let print_lint_groups = |lints: Vec<(&'static str, Vec<LintId>)>| {
85aaf69f 894 for (name, to) in lints {
a2a8927a 895 let name = name.to_lowercase().replace('_', "-");
dfeec247
XL
896 let desc = to
897 .into_iter()
a2a8927a 898 .map(|x| x.to_string().replace('_', "-"))
dfeec247
XL
899 .collect::<Vec<String>>()
900 .join(", ");
cc61c64b 901 println!(" {} {}", padded(&name), desc);
1a4d82fc
JJ
902 }
903 println!("\n");
904 };
905
906 print_lint_groups(builtin_groups);
907
908 match (loaded_plugins, plugin.len(), plugin_groups.len()) {
909 (false, 0, _) | (false, _, 0) => {
fc512014 910 println!("Lint tools like Clippy can provide additional lints and lint groups.");
1a4d82fc 911 }
9e0c209e 912 (false, ..) => panic!("didn't load lint plugins but got them anyway!"),
1a4d82fc
JJ
913 (true, 0, 0) => println!("This crate does not load any lint plugins or lint groups."),
914 (true, l, g) => {
915 if l > 0 {
916 println!("Lint checks provided by plugins loaded by this crate:\n");
917 print_lints(plugin);
918 }
919 if g > 0 {
920 println!("Lint groups provided by plugins loaded by this crate:\n");
921 print_lint_groups(plugin_groups);
922 }
923 }
924 }
925}
926
927fn describe_debug_flags() {
48663c56 928 println!("\nAvailable options:\n");
92a42be0 929 print_flag_list("-Z", config::DB_OPTIONS);
1a4d82fc
JJ
930}
931
932fn describe_codegen_flags() {
933 println!("\nAvailable codegen options:\n");
92a42be0
SL
934 print_flag_list("-C", config::CG_OPTIONS);
935}
936
dfeec247
XL
937fn print_flag_list<T>(
938 cmdline_opt: &str,
ba9703b0 939 flag_list: &[(&'static str, T, &'static str, &'static str)],
dfeec247 940) {
ba9703b0 941 let max_len = flag_list.iter().map(|&(name, _, _, _)| name.chars().count()).max().unwrap_or(0);
92a42be0 942
ba9703b0 943 for &(name, _, _, desc) in flag_list {
dfeec247 944 println!(
ba9703b0 945 " {} {:>width$}=val -- {}",
dfeec247 946 cmdline_opt,
a2a8927a 947 name.replace('_', "-"),
dfeec247 948 desc,
ba9703b0 949 width = max_len
dfeec247 950 );
1a4d82fc
JJ
951 }
952}
953
954/// Process command line options. Emits messages as appropriate. If compilation
7453a54e 955/// should continue, returns a getopts::Matches object parsed from args,
9fa01778 956/// otherwise returns `None`.
7453a54e 957///
32a655c1 958/// The compiler's handling of options is a little complicated as it ties into
416331ca
XL
959/// our stability story. The current intention of each compiler option is to
960/// have one of two modes:
7453a54e
SL
961///
962/// 1. An option is stable and can be used everywhere.
416331ca 963/// 2. An option is unstable, and can only be used on nightly.
7453a54e
SL
964///
965/// Like unstable library and language features, however, unstable options have
966/// always required a form of "opt in" to indicate that you're using them. This
967/// provides the easy ability to scan a code base to check to see if anything
968/// unstable is being used. Currently, this "opt in" is the `-Z` "zed" flag.
969///
970/// All options behind `-Z` are considered unstable by default. Other top-level
971/// options can also be considered unstable, and they were unlocked through the
972/// `-Z unstable-options` flag. Note that `-Z` remains to be the root of
973/// instability in both cases, though.
974///
975/// So with all that in mind, the comments below have some more detail about the
976/// contortions done here to get things to work out correctly.
54a0048b 977pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
1a4d82fc 978 // Throw away the first argument, the name of the binary
54a0048b 979 let args = &args[1..];
1a4d82fc
JJ
980
981 if args.is_empty() {
982 // user did not write `-v` nor `-Z unstable-options`, so do not
983 // include that extra information.
fc512014
XL
984 let nightly_build =
985 rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build();
986 usage(false, false, nightly_build);
1a4d82fc
JJ
987 return None;
988 }
989
7453a54e
SL
990 // Parse with *all* options defined in the compiler, we don't worry about
991 // option stability here we just want to parse as much as possible.
041b39d2
XL
992 let mut options = getopts::Options::new();
993 for option in config::rustc_optgroups() {
994 (option.apply)(&mut options);
995 }
136023e0
XL
996 let matches = options.parse(args).unwrap_or_else(|e| {
997 let msg = match e {
998 getopts::Fail::UnrecognizedOption(ref opt) => CG_OPTIONS
999 .iter()
1000 .map(|&(name, ..)| ('C', name))
1001 .chain(DB_OPTIONS.iter().map(|&(name, ..)| ('Z', name)))
a2a8927a 1002 .find(|&(_, name)| *opt == name.replace('_', "-"))
136023e0
XL
1003 .map(|(flag, _)| format!("{}. Did you mean `-{} {}`?", e, flag, opt)),
1004 _ => None,
1005 };
1006 early_error(ErrorOutputType::default(), &msg.unwrap_or_else(|| e.to_string()));
1007 });
1a4d82fc 1008
7453a54e
SL
1009 // For all options we just parsed, we check a few aspects:
1010 //
1011 // * If the option is stable, we're all good
1012 // * If the option wasn't passed, we're all good
1013 // * If `-Z unstable-options` wasn't passed (and we're not a -Z option
1014 // ourselves), then we require the `-Z unstable-options` flag to unlock
1015 // this option that was passed.
1016 // * If we're a nightly compiler, then unstable options are now unlocked, so
1017 // we're good to go.
416331ca 1018 // * Otherwise, if we're an unstable option then we generate an error
7453a54e 1019 // (unstable option being used on stable)
54a0048b 1020 nightly_options::check_nightly_options(&matches, &config::rustc_optgroups());
1a4d82fc
JJ
1021
1022 if matches.opt_present("h") || matches.opt_present("help") {
416331ca 1023 // Only show unstable options in --help if we accept unstable options.
fc512014
XL
1024 let unstable_enabled = nightly_options::is_unstable_enabled(&matches);
1025 let nightly_build = nightly_options::match_is_nightly_build(&matches);
1026 usage(matches.opt_present("verbose"), unstable_enabled, nightly_build);
1a4d82fc
JJ
1027 return None;
1028 }
1029
0531ce1d
XL
1030 // Handle the special case of -Wall.
1031 let wall = matches.opt_strs("W");
1032 if wall.iter().any(|x| *x == "all") {
1033 print_wall_help();
a2a8927a 1034 rustc_errors::FatalError.raise();
0531ce1d
XL
1035 }
1036
1a4d82fc 1037 // Don't handle -W help here, because we might first load plugins.
17df50a5
XL
1038 let debug_flags = matches.opt_strs("Z");
1039 if debug_flags.iter().any(|x| *x == "help") {
1a4d82fc
JJ
1040 describe_debug_flags();
1041 return None;
1042 }
1043
1044 let cg_flags = matches.opt_strs("C");
0bf4aa26 1045
1a4d82fc
JJ
1046 if cg_flags.iter().any(|x| *x == "help") {
1047 describe_codegen_flags();
1048 return None;
1049 }
1050
476ff2be 1051 if cg_flags.iter().any(|x| *x == "no-stack-check") {
dfeec247
XL
1052 early_warn(
1053 ErrorOutputType::default(),
1054 "the --no-stack-check flag is deprecated and does nothing",
1055 );
476ff2be
SL
1056 }
1057
0bf4aa26 1058 if cg_flags.iter().any(|x| *x == "passes=list") {
17df50a5
XL
1059 let backend_name = debug_flags.iter().find_map(|x| {
1060 if x.starts_with("codegen-backend=") {
1061 Some(&x["codegen-backends=".len()..])
1062 } else {
1063 None
1064 }
1065 });
1066 get_codegen_backend(&None, backend_name).print_passes();
1a4d82fc
JJ
1067 return None;
1068 }
1069
1070 if matches.opt_present("version") {
1071 version("rustc", &matches);
1072 return None;
1073 }
1074
1075 Some(matches)
1076}
1077
54a0048b 1078fn parse_crate_attrs<'a>(sess: &'a Session, input: &Input) -> PResult<'a, Vec<ast::Attribute>> {
60c5eb7d 1079 match input {
dfeec247
XL
1080 Input::File(ifile) => rustc_parse::parse_crate_attrs_from_file(ifile, &sess.parse_sess),
1081 Input::Str { name, input } => rustc_parse::parse_crate_attrs_from_source_str(
1082 name.clone(),
1083 input.clone(),
1084 &sess.parse_sess,
1085 ),
54a0048b 1086 }
1a4d82fc
JJ
1087}
1088
9fa01778 1089/// Gets a list of extra command-line flags provided by the user, as strings.
0531ce1d
XL
1090///
1091/// This function is used during ICEs to show more information useful for
1092/// debugging, since some ICEs only happens with non-default compiler flags
1093/// (and the users don't always report them).
1094fn extra_compiler_flags() -> Option<(Vec<String>, bool)> {
5099ac24 1095 let mut args = env::args_os().map(|arg| arg.to_string_lossy().to_string()).peekable();
0531ce1d 1096
0531ce1d
XL
1097 let mut result = Vec::new();
1098 let mut excluded_cargo_defaults = false;
5099ac24
FG
1099 while let Some(arg) = args.next() {
1100 if let Some(a) = ICE_REPORT_COMPILER_FLAGS.iter().find(|a| arg.starts_with(*a)) {
1101 let content = if arg.len() == a.len() {
1102 match args.next() {
1103 Some(arg) => arg.to_string(),
1104 None => continue,
1105 }
1106 } else if arg.get(a.len()..a.len() + 1) == Some("=") {
1107 arg[a.len() + 1..].to_string()
0531ce1d 1108 } else {
5099ac24
FG
1109 arg[a.len()..].to_string()
1110 };
1111 if ICE_REPORT_COMPILER_FLAGS_EXCLUDE.iter().any(|exc| content.starts_with(exc)) {
0531ce1d 1112 excluded_cargo_defaults = true;
5099ac24
FG
1113 } else {
1114 result.push(a.to_string());
1115 match ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE.iter().find(|s| content.starts_with(*s))
1116 {
1117 Some(s) => result.push(s.to_string()),
1118 None => result.push(content),
1119 }
0531ce1d
XL
1120 }
1121 }
1122 }
1123
dfeec247 1124 if !result.is_empty() { Some((result, excluded_cargo_defaults)) } else { None }
0531ce1d
XL
1125}
1126
e1599b0c 1127/// Runs a closure and catches unwinds triggered by fatal errors.
1a4d82fc 1128///
e1599b0c
XL
1129/// The compiler currently unwinds with a special sentinel value to abort
1130/// compilation on fatal errors. This function catches that sentinel and turns
1131/// the panic into a `Result` instead.
5e7ed085 1132pub fn catch_fatal_errors<F: FnOnce() -> R, R>(f: F) -> Result<R, ErrorGuaranteed> {
532ac7d7 1133 catch_unwind(panic::AssertUnwindSafe(f)).map_err(|value| {
dfeec247 1134 if value.is::<rustc_errors::FatalErrorMarker>() {
5e7ed085 1135 ErrorGuaranteed::unchecked_claim_error_was_emitted()
8faf50e0 1136 } else {
e1599b0c
XL
1137 panic::resume_unwind(value);
1138 }
1139 })
1140}
1141
f9f354fc
XL
1142/// Variant of `catch_fatal_errors` for the `interface::Result` return type
1143/// that also computes the exit code.
1144pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 {
1145 let result = catch_fatal_errors(f).and_then(|result| result);
1146 match result {
1147 Ok(()) => EXIT_SUCCESS,
1148 Err(_) => EXIT_FAILURE,
1149 }
1150}
1151
1b1a35ee
XL
1152static DEFAULT_HOOK: SyncLazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> =
1153 SyncLazy::new(|| {
e1599b0c 1154 let hook = panic::take_hook();
136023e0
XL
1155 panic::set_hook(Box::new(|info| {
1156 // Invoke the default handler, which prints the actual panic message and optionally a backtrace
1157 (*DEFAULT_HOOK)(info);
1158
1159 // Separate the output with an empty line
1160 eprintln!();
1161
1162 // Print the ICE message
1163 report_ice(info, BUG_REPORT_URL);
1164 }));
e1599b0c 1165 hook
1b1a35ee 1166 });
e1599b0c 1167
136023e0 1168/// Prints the ICE message, including query stack, but without backtrace.
e1599b0c
XL
1169///
1170/// The message will point the user at `bug_report_url` to report the ICE.
1171///
1172/// When `install_ice_hook` is called, this function will be called as the panic
1173/// hook.
1174pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
dfeec247
XL
1175 let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
1176 rustc_errors::ColorConfig::Auto,
e1599b0c
XL
1177 None,
1178 false,
1179 false,
1180 None,
1181 false,
1182 ));
dfeec247 1183 let handler = rustc_errors::Handler::with_emitter(true, None, emitter);
e1599b0c
XL
1184
1185 // a .span_bug or .bug call has already printed what
1186 // it wants to print.
dfeec247 1187 if !info.payload().is::<rustc_errors::ExplicitBug>() {
5e7ed085
FG
1188 let mut d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
1189 handler.emit_diagnostic(&mut d);
e1599b0c 1190 }
1a4d82fc 1191
e1599b0c
XL
1192 let mut xs: Vec<Cow<'static, str>> = vec![
1193 "the compiler unexpectedly panicked. this is a bug.".into(),
1194 format!("we would appreciate a bug report: {}", bug_report_url).into(),
dfeec247
XL
1195 format!(
1196 "rustc {} running on {}",
29967ef6 1197 util::version_str().unwrap_or("unknown_version"),
dfeec247
XL
1198 config::host_triple()
1199 )
1200 .into(),
e1599b0c 1201 ];
0531ce1d 1202
e1599b0c
XL
1203 if let Some((flags, excluded_cargo_defaults)) = extra_compiler_flags() {
1204 xs.push(format!("compiler flags: {}", flags.join(" ")).into());
0531ce1d 1205
e1599b0c
XL
1206 if excluded_cargo_defaults {
1207 xs.push("some of the compiler flags provided by cargo are hidden".into());
1208 }
1209 }
0531ce1d 1210
e1599b0c 1211 for note in &xs {
c295e0f8 1212 handler.note_without_error(note);
e1599b0c
XL
1213 }
1214
1215 // If backtraces are enabled, also print the query stack
5869c6ff 1216 let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0");
54a0048b 1217
29967ef6
XL
1218 let num_frames = if backtrace { None } else { Some(2) };
1219
6a06907d 1220 interface::try_print_query_stack(&handler, num_frames);
e1599b0c
XL
1221
1222 #[cfg(windows)]
1223 unsafe {
1224 if env::var("RUSTC_BREAK_ON_ICE").is_ok() {
e1599b0c 1225 // Trigger a debugger if we crashed during bootstrap
dfeec247 1226 winapi::um::debugapi::DebugBreak();
8faf50e0 1227 }
e1599b0c
XL
1228 }
1229}
1230
1231/// Installs a panic hook that will print the ICE message on unexpected panics.
1232///
1233/// A custom rustc driver can skip calling this to set up a custom ICE hook.
1234pub fn install_ice_hook() {
5e7ed085 1235 // If the user has not explicitly overridden "RUST_BACKTRACE", then produce
5099ac24
FG
1236 // full backtraces. When a compiler ICE happens, we want to gather
1237 // as much information as possible to present in the issue opened
1238 // by the user. Compiler developers and other rustc users can
1239 // opt in to less-verbose backtraces by manually setting "RUST_BACKTRACE"
1240 // (e.g. `RUST_BACKTRACE=1`)
1241 if std::env::var("RUST_BACKTRACE").is_err() {
1242 std::env::set_var("RUST_BACKTRACE", "full");
1243 }
1b1a35ee 1244 SyncLazy::force(&DEFAULT_HOOK);
1a4d82fc
JJ
1245}
1246
0531ce1d 1247/// This allows tools to enable rust logging without having to magically match rustc's
3dfed10e 1248/// tracing crate version.
0531ce1d 1249pub fn init_rustc_env_logger() {
a2a8927a
XL
1250 if let Err(error) = rustc_log::init_rustc_env_logger() {
1251 early_error(ErrorOutputType::default(), &error.to_string());
1252 }
3dfed10e
XL
1253}
1254
1255/// This allows tools to enable rust logging without having to magically match rustc's
1256/// tracing crate version. In contrast to `init_rustc_env_logger` it allows you to choose an env var
1257/// other than `RUSTC_LOG`.
1258pub fn init_env_logger(env: &str) {
a2a8927a
XL
1259 if let Err(error) = rustc_log::init_env_logger(env) {
1260 early_error(ErrorOutputType::default(), &error.to_string());
1261 }
0531ce1d
XL
1262}
1263
136023e0
XL
1264#[cfg(all(unix, any(target_env = "gnu", target_os = "macos")))]
1265mod signal_handler {
1266 extern "C" {
1267 fn backtrace_symbols_fd(
1268 buffer: *const *mut libc::c_void,
1269 size: libc::c_int,
1270 fd: libc::c_int,
1271 );
1272 }
1273
1274 extern "C" fn print_stack_trace(_: libc::c_int) {
1275 const MAX_FRAMES: usize = 256;
1276 static mut STACK_TRACE: [*mut libc::c_void; MAX_FRAMES] =
1277 [std::ptr::null_mut(); MAX_FRAMES];
1278 unsafe {
1279 let depth = libc::backtrace(STACK_TRACE.as_mut_ptr(), MAX_FRAMES as i32);
1280 if depth == 0 {
1281 return;
1282 }
1283 backtrace_symbols_fd(STACK_TRACE.as_ptr(), depth, 2);
1284 }
1285 }
1286
1287 // When an error signal (such as SIGABRT or SIGSEGV) is delivered to the
1288 // process, print a stack trace and then exit.
1289 pub(super) fn install() {
1290 unsafe {
1291 const ALT_STACK_SIZE: usize = libc::MINSIGSTKSZ + 64 * 1024;
1292 let mut alt_stack: libc::stack_t = std::mem::zeroed();
1293 alt_stack.ss_sp =
1294 std::alloc::alloc(std::alloc::Layout::from_size_align(ALT_STACK_SIZE, 1).unwrap())
1295 as *mut libc::c_void;
1296 alt_stack.ss_size = ALT_STACK_SIZE;
c295e0f8 1297 libc::sigaltstack(&alt_stack, std::ptr::null_mut());
136023e0
XL
1298
1299 let mut sa: libc::sigaction = std::mem::zeroed();
1300 sa.sa_sigaction = print_stack_trace as libc::sighandler_t;
1301 sa.sa_flags = libc::SA_NODEFER | libc::SA_RESETHAND | libc::SA_ONSTACK;
1302 libc::sigemptyset(&mut sa.sa_mask);
1303 libc::sigaction(libc::SIGSEGV, &sa, std::ptr::null_mut());
1304 }
1305 }
1306}
1307
1308#[cfg(not(all(unix, any(target_env = "gnu", target_os = "macos"))))]
1309mod signal_handler {
1310 pub(super) fn install() {}
1311}
1312
f9f354fc 1313pub fn main() -> ! {
5869c6ff
XL
1314 let start_time = Instant::now();
1315 let start_rss = get_resident_set_size();
0531ce1d 1316 init_rustc_env_logger();
136023e0 1317 signal_handler::install();
416331ca 1318 let mut callbacks = TimePassesCallbacks::default();
e1599b0c 1319 install_ice_hook();
f9f354fc 1320 let exit_code = catch_with_exit_code(|| {
dfeec247
XL
1321 let args = env::args_os()
1322 .enumerate()
1323 .map(|(i, arg)| {
1324 arg.into_string().unwrap_or_else(|arg| {
1325 early_error(
1326 ErrorOutputType::default(),
fc512014 1327 &format!("argument {} is not valid Unicode: {:?}", i, arg),
dfeec247
XL
1328 )
1329 })
1330 })
2c00a5a8 1331 .collect::<Vec<_>>();
2a314972 1332 RunCompiler::new(&args, &mut callbacks).run()
f9f354fc 1333 });
5869c6ff
XL
1334
1335 if callbacks.time_passes {
1336 let end_rss = get_resident_set_size();
1337 print_time_passes_entry("total", start_time.elapsed(), start_rss, end_rss);
1338 }
1339
f9f354fc 1340 process::exit(exit_code)
1a4d82fc 1341}