lib.rs 47.1 KB
Newer Older
1 2 3 4 5 6
//! The Rust compiler.
//!
//! # Note
//!
//! This API is completely unstable and subject to change.

E
Erik Hofmayer 已提交
7
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
8
#![feature(nll)]
9
#![feature(once_cell)]
M
Mark Rousskov 已提交
10
#![recursion_limit = "256"]
11

J
Jose Narvaez 已提交
12
#[macro_use]
G
Gurpreet Singh 已提交
13
extern crate tracing;
14

15 16
pub extern crate rustc_plugin_impl as plugin;

U
Ujjwal Sharma 已提交
17
use rustc_ast as ast;
18
use rustc_codegen_ssa::{traits::CodegenBackend, CodegenResults};
19
use rustc_data_structures::profiling::print_time_passes_entry;
20
use rustc_data_structures::sync::SeqCst;
21 22
use rustc_errors::registry::{InvalidErrorCode, Registry};
use rustc_errors::{ErrorReported, PResult};
23
use rustc_feature::find_gated_cfg;
24
use rustc_hir::def_id::LOCAL_CRATE;
25
use rustc_interface::util::{self, collect_crate_types, get_builtin_codegen_backend};
M
Mark Rousskov 已提交
26
use rustc_interface::{interface, Queries};
27
use rustc_lint::LintStore;
M
Mark Rousskov 已提交
28
use rustc_metadata::locator;
29 30
use rustc_middle::middle::cstore::MetadataLoader;
use rustc_middle::ty::TyCtxt;
M
Mark Rousskov 已提交
31 32
use rustc_save_analysis as save;
use rustc_save_analysis::DumpHandler;
V
Victor Ding 已提交
33
use rustc_serialize::json::{self, ToJson};
34
use rustc_session::config::nightly_options;
D
Dan Aloni 已提交
35
use rustc_session::config::{ErrorOutputType, Input, OutputType, PrintRequest, TrimmedDefPaths};
L
Luca Barbieri 已提交
36
use rustc_session::getopts;
37 38 39
use rustc_session::lint::{Lint, LintId};
use rustc_session::{config, DiagnosticOutput, Session};
use rustc_session::{early_error, early_warn};
40 41
use rustc_span::source_map::{FileLoader, FileName};
use rustc_span::symbol::sym;
42

43
use std::borrow::Cow;
44
use std::cmp::max;
45
use std::default::Default;
A
Alex Crichton 已提交
46
use std::env;
47
use std::ffi::OsString;
V
Victor Ding 已提交
48
use std::fs;
49
use std::io::{self, Read, Write};
50
use std::lazy::SyncLazy;
51
use std::mem;
52
use std::panic::{self, catch_unwind};
53
use std::path::PathBuf;
54
use std::process::{self, Command, Stdio};
55
use std::str;
56
use std::time::Instant;
N
Nick Cameron 已提交
57

58
mod args;
M
Mark Rousskov 已提交
59
pub mod pretty;
60

61
/// Exit status code used for successful compilation and help output.
62
pub const EXIT_SUCCESS: i32 = 0;
63

64
/// Exit status code used for compilation failures and invalid flags.
65
pub const EXIT_FAILURE: i32 = 1;
66

67 68
const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust/issues/new\
    ?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md";
69 70 71 72 73 74

const ICE_REPORT_COMPILER_FLAGS: &[&str] = &["Z", "C", "crate-type"];

const ICE_REPORT_COMPILER_FLAGS_EXCLUDE: &[&str] = &["metadata", "extra-filename"];

const ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE: &[&str] = &["incremental"];
75

76
pub fn abort_on_err<T>(result: Result<T, ErrorReported>, sess: &Session) -> T {
77
    match result {
78
        Err(..) => {
79 80 81
            sess.abort_if_errors();
            panic!("error reported but abort_if_errors didn't abort???");
        }
82 83 84 85
        Ok(x) => x,
    }
}

86 87 88
pub trait Callbacks {
    /// Called before creating the compiler instance
    fn config(&mut self, _config: &mut interface::Config) {}
89 90
    /// Called after parsing. Return value instructs the compiler whether to
    /// continue the compilation afterwards (defaults to `Compilation::Continue`)
R
Ralf Jung 已提交
91 92 93 94 95
    fn after_parsing<'tcx>(
        &mut self,
        _compiler: &interface::Compiler,
        _queries: &'tcx Queries<'tcx>,
    ) -> Compilation {
96
        Compilation::Continue
97
    }
98 99
    /// Called after expansion. Return value instructs the compiler whether to
    /// continue the compilation afterwards (defaults to `Compilation::Continue`)
R
Ralf Jung 已提交
100 101 102 103 104
    fn after_expansion<'tcx>(
        &mut self,
        _compiler: &interface::Compiler,
        _queries: &'tcx Queries<'tcx>,
    ) -> Compilation {
105
        Compilation::Continue
106
    }
107 108
    /// Called after analysis. Return value instructs the compiler whether to
    /// continue the compilation afterwards (defaults to `Compilation::Continue`)
R
Ralf Jung 已提交
109 110 111 112 113
    fn after_analysis<'tcx>(
        &mut self,
        _compiler: &interface::Compiler,
        _queries: &'tcx Queries<'tcx>,
    ) -> Compilation {
114
        Compilation::Continue
115
    }
N
Nick Cameron 已提交
116 117
}

118 119 120 121 122 123 124
#[derive(Default)]
pub struct TimePassesCallbacks {
    time_passes: bool,
}

impl Callbacks for TimePassesCallbacks {
    fn config(&mut self, config: &mut interface::Config) {
125 126
        // If a --prints=... option has been given, we don't print the "total"
        // time because it will mess up the --prints output. See #64339.
M
Mark Rousskov 已提交
127 128
        self.time_passes = config.opts.prints.is_empty()
            && (config.opts.debugging_opts.time_passes || config.opts.debugging_opts.time);
129
        config.opts.trimmed_def_paths = TrimmedDefPaths::GoodPath;
130 131 132
    }
}

133 134 135 136
pub fn diagnostics_registry() -> Registry {
    Registry::new(&rustc_error_codes::DIAGNOSTICS)
}

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
pub struct RunCompiler<'a, 'b> {
    at_args: &'a [String],
    callbacks: &'b mut (dyn Callbacks + Send),
    file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
    emitter: Option<Box<dyn Write + Send>>,
    make_codegen_backend:
        Option<Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>>,
}

impl<'a, 'b> RunCompiler<'a, 'b> {
    pub fn new(at_args: &'a [String], callbacks: &'b mut (dyn Callbacks + Send)) -> Self {
        Self { at_args, callbacks, file_loader: None, emitter: None, make_codegen_backend: None }
    }
    pub fn set_make_codegen_backend(
        &mut self,
        make_codegen_backend: Option<
            Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>,
        >,
    ) -> &mut Self {
        self.make_codegen_backend = make_codegen_backend;
        self
    }
    pub fn set_emitter(&mut self, emitter: Option<Box<dyn Write + Send>>) -> &mut Self {
        self.emitter = emitter;
        self
    }
    pub fn set_file_loader(
        &mut self,
        file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
    ) -> &mut Self {
        self.file_loader = file_loader;
        self
    }
    pub fn run(self) -> interface::Result<()> {
        run_compiler(
            self.at_args,
            self.callbacks,
            self.file_loader,
            self.emitter,
            self.make_codegen_backend,
        )
    }
}
180 181
// Parse args and run the compiler. This is the primary entry point for rustc.
// The FileLoader provides a way to load files from sources other than the file system.
182
fn run_compiler(
183
    at_args: &[String],
184 185
    callbacks: &mut (dyn Callbacks + Send),
    file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
M
Mark Rousskov 已提交
186
    emitter: Option<Box<dyn Write + Send>>,
187 188 189
    make_codegen_backend: Option<
        Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>,
    >,
190
) -> interface::Result<()> {
191 192 193 194
    let mut args = Vec::new();
    for arg in at_args {
        match args::arg_expand(arg.clone()) {
            Ok(arg) => args.extend(arg),
M
Mark Rousskov 已提交
195 196 197 198
            Err(err) => early_error(
                ErrorOutputType::default(),
                &format!("Failed to load argument file: {}", err),
            ),
199 200
        }
    }
K
Kazantcev Andrey 已提交
201
    let diagnostic_output = emitter.map_or(DiagnosticOutput::Default, DiagnosticOutput::Raw);
202
    let matches = match handle_options(&args) {
203
        Some(matches) => matches,
204
        None => return Ok(()),
205
    };
J
John Kåre Alsaker 已提交
206

207
    let sopts = config::build_session_options(&matches);
208
    let cfg = interface::parse_cfgspecs(matches.opt_strs("cfg"));
N
Nick Cameron 已提交
209

210 211 212 213 214
    // We wrap `make_codegen_backend` in another `Option` such that `dummy_config` can take
    // ownership of it when necessary, while also allowing the non-dummy config to take ownership
    // when `dummy_config` is not used.
    let mut make_codegen_backend = Some(make_codegen_backend);

215 216 217 218 219 220 221 222 223 224 225 226
    let mut dummy_config = |sopts, cfg, diagnostic_output| {
        let mut config = interface::Config {
            opts: sopts,
            crate_cfg: cfg,
            input: Input::File(PathBuf::new()),
            input_path: None,
            output_file: None,
            output_dir: None,
            file_loader: None,
            diagnostic_output,
            stderr: None,
            lint_caps: Default::default(),
227
            register_lints: None,
228
            override_queries: None,
229
            make_codegen_backend: make_codegen_backend.take().unwrap(),
230
            registry: diagnostics_registry(),
231 232 233 234
        };
        callbacks.config(&mut config);
        config
    };
235

236
    if let Some(ref code) = matches.opt_str("explain") {
237
        handle_explain(diagnostics_registry(), code, sopts.error_format);
238 239
        return Ok(());
    }
240 241

    let (odir, ofile) = make_output(&matches);
242
    let (input, input_file_path, input_err) = match make_input(&matches.free) {
243
        Some(v) => v,
M
Mark Rousskov 已提交
244 245 246 247 248 249
        None => match matches.free.len() {
            0 => {
                let config = dummy_config(sopts, cfg, diagnostic_output);
                interface::run_compiler(config, |compiler| {
                    let sopts = &compiler.session().opts;
                    if sopts.describe_lints {
250
                        let mut lint_store = rustc_lint::new_lint_store(
M
Mark Rousskov 已提交
251 252
                            sopts.debugging_opts.no_interleave_lints,
                            compiler.session().unstable_options(),
253
                        );
254 255 256 257 258 259 260 261
                        let registered_lints =
                            if let Some(register_lints) = compiler.register_lints() {
                                register_lints(compiler.session(), &mut lint_store);
                                true
                            } else {
                                false
                            };
                        describe_lints(compiler.session(), &lint_store, registered_lints);
M
Mark Rousskov 已提交
262 263 264 265 266 267 268 269 270
                        return;
                    }
                    let should_stop = RustcDefaultCalls::print_crate_info(
                        &***compiler.codegen_backend(),
                        compiler.session(),
                        None,
                        &odir,
                        &ofile,
                    );
N
Nick Cameron 已提交
271

M
Mark Rousskov 已提交
272 273 274 275 276 277
                    if should_stop == Compilation::Stop {
                        return;
                    }
                    early_error(sopts.error_format, "no input filename given")
                });
                return Ok(());
278
            }
M
Mark Rousskov 已提交
279 280 281 282 283 284 285 286 287
            1 => panic!("make_input should have provided valid inputs"),
            _ => early_error(
                sopts.error_format,
                &format!(
                    "multiple input filenames provided (first two filenames are `{}` and `{}`)",
                    matches.free[0], matches.free[1],
                ),
            ),
        },
288
    };
289

290 291 292
    if let Some(err) = input_err {
        // Immediately stop compilation if there was an issue reading
        // the input (for example if the input stream is not UTF-8).
293 294 295 296
        interface::run_compiler(dummy_config(sopts, cfg, diagnostic_output), |compiler| {
            compiler.session().err(&err.to_string());
        });
        return Err(ErrorReported);
297 298
    }

299 300 301 302 303 304 305 306 307 308 309
    let mut config = interface::Config {
        opts: sopts,
        crate_cfg: cfg,
        input,
        input_path: input_file_path,
        output_file: ofile,
        output_dir: odir,
        file_loader,
        diagnostic_output,
        stderr: None,
        lint_caps: Default::default(),
310
        register_lints: None,
311
        override_queries: None,
312
        make_codegen_backend: make_codegen_backend.unwrap(),
313
        registry: diagnostics_registry(),
314 315 316 317 318 319 320 321 322 323 324 325
    };

    callbacks.config(&mut config);

    interface::run_compiler(config, |compiler| {
        let sess = compiler.session();
        let should_stop = RustcDefaultCalls::print_crate_info(
            &***compiler.codegen_backend(),
            sess,
            Some(compiler.input()),
            compiler.output_dir(),
            compiler.output_file(),
M
Mark Rousskov 已提交
326 327 328 329 330 331 332 333
        )
        .and_then(|| {
            RustcDefaultCalls::list_metadata(
                sess,
                &*compiler.codegen_backend().metadata_loader(),
                &matches,
                compiler.input(),
            )
V
Victor Ding 已提交
334 335
        })
        .and_then(|| RustcDefaultCalls::try_process_rlink(sess, compiler));
336 337 338 339 340

        if should_stop == Compilation::Stop {
            return sess.compile_status();
        }

341
        let linker = compiler.enter(|queries| {
C
Camille GILLOT 已提交
342
            let early_exit = || sess.compile_status().map(|_| None);
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
            queries.parse()?;

            if let Some(ppm) = &sess.opts.pretty {
                if ppm.needs_ast_map() {
                    queries.global_ctxt()?.peek_mut().enter(|tcx| {
                        let expanded_crate = queries.expansion()?.take().0;
                        pretty::print_after_hir_lowering(
                            tcx,
                            compiler.input(),
                            &expanded_crate,
                            *ppm,
                            compiler.output_file().as_ref().map(|p| &**p),
                        );
                        Ok(())
                    })?;
                } else {
                    let krate = queries.parse()?.take();
                    pretty::print_after_parsing(
                        sess,
                        &compiler.input(),
                        &krate,
364
                        *ppm,
365 366
                        compiler.output_file().as_ref().map(|p| &**p),
                    );
367
                }
J
Joshua Nelson 已提交
368
                trace!("finished pretty-printing");
C
Camille GILLOT 已提交
369
                return early_exit();
370
            }
371

R
Ralf Jung 已提交
372
            if callbacks.after_parsing(compiler, queries) == Compilation::Stop {
C
Camille GILLOT 已提交
373
                return early_exit();
374
            }
N
Nick Cameron 已提交
375

M
Mark Rousskov 已提交
376 377 378 379 380
            if sess.opts.debugging_opts.parse_only
                || sess.opts.debugging_opts.show_span.is_some()
                || sess.opts.debugging_opts.ast_json_noexpand
            {
                return early_exit();
381
            }
382

383 384
            {
                let (_, lint_store) = &*queries.register_plugins()?.peek();
385

386 387 388
                // Lint plugins are registered; now we can process command line flags.
                if sess.opts.describe_lints {
                    describe_lints(&sess, &lint_store, true);
C
Camille GILLOT 已提交
389
                    return early_exit();
390 391 392 393
                }
            }

            queries.expansion()?;
R
Ralf Jung 已提交
394
            if callbacks.after_expansion(compiler, queries) == Compilation::Stop {
C
Camille GILLOT 已提交
395
                return early_exit();
396
            }
397

398 399 400 401 402
            queries.prepare_outputs()?;

            if sess.opts.output_types.contains_key(&OutputType::DepInfo)
                && sess.opts.output_types.len() == 1
            {
C
Camille GILLOT 已提交
403
                return early_exit();
404
            }
405

406
            queries.global_ctxt()?;
407

408
            // Drop AST after creating GlobalCtxt to free memory
409 410 411 412
            {
                let _timer = sess.prof.generic_activity("drop_ast");
                mem::drop(queries.expansion()?.take());
            }
413

M
Mark Rousskov 已提交
414 415
            if sess.opts.debugging_opts.no_analysis || sess.opts.debugging_opts.ast_json {
                return early_exit();
416
            }
417

418 419 420 421 422
            if sess.opts.debugging_opts.save_analysis {
                let crate_name = queries.crate_name()?.peek().clone();
                queries.global_ctxt()?.peek_mut().enter(|tcx| {
                    let result = tcx.analysis(LOCAL_CRATE);

423
                    sess.time("save_analysis", || {
424 425 426 427 428
                        save::process_crate(
                            tcx,
                            &crate_name,
                            &compiler.input(),
                            None,
C
Tidy.  
Camille GILLOT 已提交
429
                            DumpHandler::new(
M
Mark Rousskov 已提交
430 431 432
                                compiler.output_dir().as_ref().map(|p| &**p),
                                &crate_name,
                            ),
433 434
                        )
                    });
435

436 437 438
                    result
                })?;
            }
439

440
            queries.global_ctxt()?.peek_mut().enter(|tcx| tcx.analysis(LOCAL_CRATE))?;
441

R
Ralf Jung 已提交
442
            if callbacks.after_analysis(compiler, queries) == Compilation::Stop {
C
Camille GILLOT 已提交
443
                return early_exit();
444
            }
445

446
            queries.ongoing_codegen()?;
447

448 449 450
            if sess.opts.debugging_opts.print_type_sizes {
                sess.code_stats.print_type_sizes();
            }
451

452 453
            let linker = queries.linker()?;
            Ok(Some(linker))
454
        })?;
455

456
        if let Some(linker) = linker {
J
John Kåre Alsaker 已提交
457
            let _timer = sess.timer("link");
458 459 460
            linker.link()?
        }

461 462 463 464 465
        if sess.opts.debugging_opts.perf_stats {
            sess.print_perf_stats();
        }

        if sess.print_fuel_crate.is_some() {
M
Mark Rousskov 已提交
466 467
            eprintln!(
                "Fuel used by {}: {}",
468
                sess.print_fuel_crate.as_ref().unwrap(),
M
Mark Rousskov 已提交
469 470
                sess.print_fuel.load(SeqCst)
            );
471
        }
472

473 474
        Ok(())
    })
475 476
}

477 478 479 480 481
#[cfg(unix)]
pub fn set_sigpipe_handler() {
    unsafe {
        // Set the SIGPIPE signal handler, so that an EPIPE
        // will cause rustc to terminate, as expected.
L
ljedrz 已提交
482
        assert_ne!(libc::signal(libc::SIGPIPE, libc::SIG_DFL), libc::SIG_ERR);
483 484 485 486 487 488
    }
}

#[cfg(windows)]
pub fn set_sigpipe_handler() {}

489
// Extract output directory and file from matches.
A
Alex Crichton 已提交
490
fn make_output(matches: &getopts::Matches) -> (Option<PathBuf>, Option<PathBuf>) {
A
Aaron Turon 已提交
491 492
    let odir = matches.opt_str("out-dir").map(|o| PathBuf::from(&o));
    let ofile = matches.opt_str("o").map(|o| PathBuf::from(&o));
493 494
    (odir, ofile)
}
495

496
// Extract input (string or file and optional path) from matches.
497
fn make_input(free_matches: &[String]) -> Option<(Input, Option<PathBuf>, Option<io::Error>)> {
498
    if free_matches.len() == 1 {
499
        let ifile = &free_matches[0];
500
        if ifile == "-" {
501
            let mut src = String::new();
502
            let err = if io::stdin().read_to_string(&mut src).is_err() {
M
Mark Rousskov 已提交
503 504 505 506
                Some(io::Error::new(
                    io::ErrorKind::InvalidData,
                    "couldn't read from stdin, as it did not contain valid UTF-8",
                ))
507 508 509
            } else {
                None
            };
510
            if let Ok(path) = env::var("UNSTABLE_RUSTDOC_TEST_PATH") {
M
Mark Rousskov 已提交
511 512 513 514 515 516
                let line = env::var("UNSTABLE_RUSTDOC_TEST_LINE").expect(
                    "when UNSTABLE_RUSTDOC_TEST_PATH is set \
                                    UNSTABLE_RUSTDOC_TEST_LINE also needs to be set",
                );
                let line = isize::from_str_radix(&line, 10)
                    .expect("UNSTABLE_RUSTDOC_TEST_LINE needs to be an number");
517 518 519
                let file_name = FileName::doc_test_source_code(PathBuf::from(path), line);
                return Some((Input::Str { name: file_name, input: src }, None, err));
            }
M
Mark Rousskov 已提交
520
            Some((Input::Str { name: FileName::anon_source_code(&src), input: src }, None, err))
521
        } else {
M
Mark Rousskov 已提交
522
            Some((Input::File(PathBuf::from(ifile)), Some(PathBuf::from(ifile)), None))
523 524 525
        }
    } else {
        None
526
    }
527
}
528

N
Nick Cameron 已提交
529
// Whether to stop or continue compilation.
N
Niko Matsakis 已提交
530
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
N
Nick Cameron 已提交
531 532 533 534 535 536 537 538 539
pub enum Compilation {
    Stop,
    Continue,
}

impl Compilation {
    pub fn and_then<F: FnOnce() -> Compilation>(self, next: F) -> Compilation {
        match self {
            Compilation::Stop => Compilation::Stop,
J
Jose Narvaez 已提交
540
            Compilation::Continue => next(),
N
Nick Cameron 已提交
541 542 543 544
        }
    }
}

545
/// CompilerCalls instance for a regular rustc build.
N
Niko Matsakis 已提交
546
#[derive(Copy, Clone)]
547 548
pub struct RustcDefaultCalls;

549
fn stdout_isatty() -> bool {
550
    atty::is(atty::Stream::Stdout)
551 552
}

553
fn stderr_isatty() -> bool {
554
    atty::is(atty::Stream::Stderr)
555 556
}

557
fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) {
M
Mark Rousskov 已提交
558
    let normalised =
559
        if code.starts_with('E') { code.to_string() } else { format!("E{0:0>4}", code) };
560 561
    match registry.try_find_description(&normalised) {
        Ok(Some(description)) => {
562
            let mut is_in_code_block = false;
563
            let mut text = String::new();
564
            // Slice off the leading newline and print.
565
            for line in description.lines() {
M
Mark Rousskov 已提交
566 567
                let indent_level =
                    line.find(|c: char| !c.is_whitespace()).unwrap_or_else(|| line.len());
568 569 570
                let dedented_line = &line[indent_level..];
                if dedented_line.starts_with("```") {
                    is_in_code_block = !is_in_code_block;
571
                    text.push_str(&line[..(indent_level + 3)]);
572 573
                } else if is_in_code_block && dedented_line.starts_with("# ") {
                    continue;
574
                } else {
575
                    text.push_str(line);
576
                }
577
                text.push('\n');
578
            }
C
Cengiz Can 已提交
579 580 581 582 583
            if stdout_isatty() {
                show_content_with_pager(&text);
            } else {
                print!("{}", text);
            }
584
        }
585
        Ok(None) => {
586 587
            early_error(output, &format!("no extended information for {}", code));
        }
588 589 590
        Err(InvalidErrorCode) => {
            early_error(output, &format!("{} is not a valid error code", code));
        }
591 592 593
    }
}

594
fn show_content_with_pager(content: &str) {
M
Mark Rousskov 已提交
595 596
    let pager_name = env::var_os("PAGER").unwrap_or_else(|| {
        if cfg!(windows) { OsString::from("more.com") } else { OsString::from("less") }
597 598 599 600 601 602
    });

    let mut fallback_to_println = false;

    match Command::new(pager_name).stdin(Stdio::piped()).spawn() {
        Ok(mut pager) => {
603
            if let Some(pipe) = pager.stdin.as_mut() {
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
                if pipe.write_all(content.as_bytes()).is_err() {
                    fallback_to_println = true;
                }
            }

            if pager.wait().is_err() {
                fallback_to_println = true;
            }
        }
        Err(_) => {
            fallback_to_println = true;
        }
    }

    // If pager fails for whatever reason, we should still print the content
    // to standard output
    if fallback_to_println {
C
Cengiz Can 已提交
621
        print!("{}", content);
622 623 624
    }
}

625
impl RustcDefaultCalls {
V
Victor Ding 已提交
626 627 628 629
    fn process_rlink(sess: &Session, compiler: &interface::Compiler) -> Result<(), ErrorReported> {
        if let Input::File(file) = compiler.input() {
            // FIXME: #![crate_type] and #![crate_name] support not implemented yet
            let attrs = vec![];
630
            sess.init_crate_types(collect_crate_types(sess, &attrs));
V
Victor Ding 已提交
631 632 633 634 635 636 637
            let outputs = compiler.build_output_filenames(&sess, &attrs);
            let rlink_data = fs::read_to_string(file).unwrap_or_else(|err| {
                sess.fatal(&format!("failed to read rlink file: {}", err));
            });
            let codegen_results: CodegenResults = json::decode(&rlink_data).unwrap_or_else(|err| {
                sess.fatal(&format!("failed to decode rlink: {}", err));
            });
B
bjorn3 已提交
638
            compiler.codegen_backend().link(&sess, codegen_results, &outputs)
V
Victor Ding 已提交
639
        } else {
640
            sess.fatal("rlink must be a file")
V
Victor Ding 已提交
641 642 643 644 645 646 647 648 649 650 651 652 653
        }
    }

    pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Compilation {
        if sess.opts.debugging_opts.link_only {
            let result = RustcDefaultCalls::process_rlink(sess, compiler);
            abort_on_err(result, sess);
            Compilation::Stop
        } else {
            Compilation::Continue
        }
    }

M
Mark Rousskov 已提交
654 655 656 657 658 659
    pub fn list_metadata(
        sess: &Session,
        metadata_loader: &dyn MetadataLoader,
        matches: &getopts::Matches,
        input: &Input,
    ) -> Compilation {
660
        let r = matches.opt_strs("Z");
L
ljedrz 已提交
661
        if r.iter().any(|s| *s == "ls") {
M
Matthias Krüger 已提交
662 663
            match *input {
                Input::File(ref ifile) => {
664
                    let path = &(*ifile);
A
Alex Crichton 已提交
665
                    let mut v = Vec::new();
666
                    locator::list_file_metadata(&sess.target, path, metadata_loader, &mut v)
M
Mark Rousskov 已提交
667
                        .unwrap();
A
Alex Crichton 已提交
668
                    println!("{}", String::from_utf8(v).unwrap());
669
                }
M
Matthias Krüger 已提交
670
                Input::Str { .. } => {
671
                    early_error(ErrorOutputType::default(), "cannot list metadata for stdin");
672 673
                }
            }
N
Nick Cameron 已提交
674
            return Compilation::Stop;
675 676
        }

677
        Compilation::Continue
678 679
    }

M
Mark Rousskov 已提交
680 681 682 683 684 685 686
    fn print_crate_info(
        codegen_backend: &dyn CodegenBackend,
        sess: &Session,
        input: Option<&Input>,
        odir: &Option<PathBuf>,
        ofile: &Option<PathBuf>,
    ) -> Compilation {
687
        use rustc_session::config::PrintRequest::*;
K
Kornel 已提交
688 689
        // PrintRequest::NativeStaticLibs is special - printed during linking
        // (empty iterator returns true)
690
        if sess.opts.prints.iter().all(|&p| p == PrintRequest::NativeStaticLibs) {
N
Nick Cameron 已提交
691
            return Compilation::Continue;
692 693
        }

J
Jonas Schievink 已提交
694 695 696 697 698 699 700 701 702 703 704 705 706
        let attrs = match input {
            None => None,
            Some(input) => {
                let result = parse_crate_attrs(sess, input);
                match result {
                    Ok(attrs) => Some(attrs),
                    Err(mut parse_error) => {
                        parse_error.emit();
                        return Compilation::Stop;
                    }
                }
            }
        };
707 708
        for req in &sess.opts.prints {
            match *req {
709
                TargetList => {
710 711
                    let mut targets =
                        rustc_target::spec::TARGETS.iter().copied().collect::<Vec<_>>();
J
Joshua Nelson 已提交
712
                    targets.sort_unstable();
713
                    println!("{}", targets.join("\n"));
M
Mark Rousskov 已提交
714
                }
N
Nicholas Nethercote 已提交
715
                Sysroot => println!("{}", sess.sysroot.display()),
716 717 718 719
                TargetLibdir => println!(
                    "{}",
                    sess.target_tlib_path.as_ref().unwrap_or(&sess.host_tlib_path).dir.display()
                ),
720
                TargetSpec => println!("{}", sess.target.to_json().pretty()),
721
                FileNames | CrateName => {
M
Mark Rousskov 已提交
722 723 724
                    let input = input.unwrap_or_else(|| {
                        early_error(ErrorOutputType::default(), "no input file provided")
                    });
725
                    let attrs = attrs.as_ref().unwrap();
726
                    let t_outputs = rustc_interface::util::build_output_filenames(
M
Mark Rousskov 已提交
727
                        input, odir, ofile, attrs, sess,
728
                    );
729
                    let id = rustc_session::output::find_crate_name(sess, attrs, input);
730 731
                    if *req == PrintRequest::CrateName {
                        println!("{}", id);
J
Jose Narvaez 已提交
732
                        continue;
733
                    }
V
Victor Ding 已提交
734
                    let crate_types = collect_crate_types(sess, attrs);
735
                    for &style in &crate_types {
736 737
                        let fname =
                            rustc_session::output::filename_for_input(sess, style, &id, &t_outputs);
738
                        println!("{}", fname.file_name().unwrap().to_string_lossy());
739 740
                    }
                }
741
                Cfg => {
M
Mark Rousskov 已提交
742 743 744 745
                    let mut cfgs = sess
                        .parse_sess
                        .config
                        .iter()
746
                        .filter_map(|&(name, value)| {
M
Mark Rousskov 已提交
747 748 749 750 751 752 753 754
                            // Note that crt-static is a specially recognized cfg
                            // directive that's printed out here as part of
                            // rust-lang/rust#37406, but in general the
                            // `target_feature` cfg is gated under
                            // rust-lang/rust#29717. For now this is just
                            // specifically allowing the crt-static cfg and that's
                            // it, this is intended to get into Cargo and then go
                            // through to build scripts.
755
                            if (name != sym::target_feature || value != Some(sym::crt_dash_static))
756
                                && !sess.is_nightly_build()
M
Mark Rousskov 已提交
757 758 759 760 761 762 763 764 765 766 767 768
                                && find_gated_cfg(|cfg_sym| cfg_sym == name).is_some()
                            {
                                return None;
                            }

                            if let Some(value) = value {
                                Some(format!("{}=\"{}\"", name, value))
                            } else {
                                Some(name.to_string())
                            }
                        })
                        .collect::<Vec<String>>();
J
Jeffrey Seyfried 已提交
769 770 771 772

                    cfgs.sort();
                    for cfg in cfgs {
                        println!("{}", cfg);
773 774
                    }
                }
775
                RelocationModels | CodeModels | TlsModels | TargetCPUs | TargetFeatures => {
I
Irina Popa 已提交
776
                    codegen_backend.print(*req, sess);
777
                }
778 779
                // Any output here interferes with Cargo's parsing of other printed output
                PrintRequest::NativeStaticLibs => {}
780 781
            }
        }
782
        Compilation::Stop
B
Brian Anderson 已提交
783 784 785
    }
}

786
/// Prints version information
787 788
pub fn version(binary: &str, matches: &getopts::Matches) {
    let verbose = matches.opt_present("verbose");
789

790
    println!("{} {}", binary, util::version_str().unwrap_or("unknown version"));
791

792
    if verbose {
J
Jose Narvaez 已提交
793 794 795
        fn unw(x: Option<&str>) -> &str {
            x.unwrap_or("unknown")
        }
796
        println!("binary: {}", binary);
797 798
        println!("commit-hash: {}", unw(util::commit_hash_str()));
        println!("commit-date: {}", unw(util::commit_date_str()));
799
        println!("host: {}", config::host_triple());
800
        println!("release: {}", unw(util::release_str()));
801
        if cfg!(feature = "llvm") {
802 803
            get_builtin_codegen_backend("llvm")().print_version();
        }
804
    }
N
Nick Cameron 已提交
805 806
}

807
fn usage(verbose: bool, include_unstable_options: bool, nightly_build: bool) {
M
Mark Rousskov 已提交
808
    let groups = if verbose { config::rustc_optgroups() } else { config::rustc_short_optgroups() };
809 810 811 812
    let mut options = getopts::Options::new();
    for option in groups.iter().filter(|x| include_unstable_options || x.is_stable()) {
        (option.apply)(&mut options);
    }
L
ljedrz 已提交
813
    let message = "Usage: rustc [OPTIONS] INPUT";
814
    let nightly_help = if nightly_build {
815
        "\n    -Z help             Print unstable compiler options"
816 817 818 819
    } else {
        ""
    };
    let verbose_help = if verbose {
820 821 822 823
        ""
    } else {
        "\n    --help -v           Print the full set of options rustc accepts"
    };
824
    let at_path = if verbose && nightly_build {
825 826 827 828
        "    @path               Read newline separated options from `path`\n"
    } else {
        ""
    };
M
Mark Rousskov 已提交
829 830
    println!(
        "{options}{at_path}\nAdditional help:
N
Nick Cameron 已提交
831
    -C help             Print codegen options
J
Jose Narvaez 已提交
832
    -W help             \
833
              Print 'lint' options and default settings{nightly}{verbose}\n",
M
Mark Rousskov 已提交
834 835 836 837 838
        options = options.usage(message),
        at_path = at_path,
        nightly = nightly_help,
        verbose = verbose_help
    );
N
Nick Cameron 已提交
839 840
}

P
Phlosioneer 已提交
841
fn print_wall_help() {
M
Mark Rousskov 已提交
842 843
    println!(
        "
844 845 846
The flag `-Wall` does not exist in `rustc`. Most useful lints are enabled by
default. Use `rustc -W help` to see all available lints. It's more common to put
warning settings in the crate root using `#![warn(LINT_NAME)]` instead of using
P
Phlosioneer 已提交
847
the command line flag directly.
M
Mark Rousskov 已提交
848 849
"
    );
P
Phlosioneer 已提交
850 851
}

852
fn describe_lints(sess: &Session, lint_store: &LintStore, loaded_plugins: bool) {
M
Mark Rousskov 已提交
853 854
    println!(
        "
N
Nick Cameron 已提交
855 856
Available lint options:
    -W <foo>           Warn about <foo>
J
Jose Narvaez 已提交
857 858
    -A <foo>           \
              Allow <foo>
N
Nick Cameron 已提交
859
    -D <foo>           Deny <foo>
J
Jose Narvaez 已提交
860
    -F <foo>           Forbid <foo> \
861
              (deny <foo> and all attempts to override)
862

M
Mark Rousskov 已提交
863 864
"
    );
N
Nick Cameron 已提交
865

866
    fn sort_lints(sess: &Session, mut lints: Vec<&'static Lint>) -> Vec<&'static Lint> {
V
varkor 已提交
867
        // The sort doesn't case-fold but it's doubtful we care.
868
        lints.sort_by_cached_key(|x: &&Lint| (x.default_level(sess.edition()), x.name));
869 870 871
        lints
    }

M
Mark Rousskov 已提交
872
    fn sort_lint_groups(
873 874
        lints: Vec<(&'static str, Vec<LintId>, bool)>,
    ) -> Vec<(&'static str, Vec<LintId>)> {
875
        let mut lints: Vec<_> = lints.into_iter().map(|(x, y, _)| (x, y)).collect();
S
Shotaro Yamada 已提交
876
        lints.sort_by_key(|l| l.0);
877 878 879
        lints
    }

M
Mark Rousskov 已提交
880 881
    let (plugin, builtin): (Vec<_>, _) =
        lint_store.get_lints().iter().cloned().partition(|&lint| lint.is_plugin);
882 883
    let plugin = sort_lints(sess, plugin);
    let builtin = sort_lints(sess, builtin);
884

M
Mark Rousskov 已提交
885 886
    let (plugin_groups, builtin_groups): (Vec<_>, _) =
        lint_store.get_lint_groups().iter().cloned().partition(|&(.., p)| p);
887 888 889
    let plugin_groups = sort_lint_groups(plugin_groups);
    let builtin_groups = sort_lint_groups(builtin_groups);

M
Mark Rousskov 已提交
890 891
    let max_name_len =
        plugin.iter().chain(&builtin).map(|&s| s.name.chars().count()).max().unwrap_or(0);
892
    let padded = |x: &str| {
S
Shotaro Yamada 已提交
893
        let mut s = " ".repeat(max_name_len - x.chars().count());
894 895
        s.push_str(x);
        s
896
    };
N
Nick Cameron 已提交
897

898
    println!("Lint checks provided by rustc:\n");
A
Alex Crichton 已提交
899 900
    println!("    {}  {:7.7}  {}", padded("name"), "default", "meaning");
    println!("    {}  {:7.7}  {}", padded("----"), "-------", "-------");
901

902
    let print_lints = |lints: Vec<&Lint>| {
903
        for lint in lints {
904
            let name = lint.name_lower().replace("_", "-");
M
Mark Rousskov 已提交
905
            println!("    {}  {:7.7}  {}", padded(&name), lint.default_level.as_str(), lint.desc);
906 907 908 909 910 911
        }
        println!("\n");
    };

    print_lints(builtin);

M
Mark Rousskov 已提交
912 913 914 915 916 917 918 919 920
    let max_name_len = max(
        "warnings".len(),
        plugin_groups
            .iter()
            .chain(&builtin_groups)
            .map(|&(s, _)| s.chars().count())
            .max()
            .unwrap_or(0),
    );
921

922
    let padded = |x: &str| {
S
Shotaro Yamada 已提交
923
        let mut s = " ".repeat(max_name_len - x.chars().count());
924 925
        s.push_str(x);
        s
926 927 928 929 930
    };

    println!("Lint groups provided by rustc:\n");
    println!("    {}  {}", padded("name"), "sub-lints");
    println!("    {}  {}", padded("----"), "---------");
931
    println!("    {}  {}", padded("warnings"), "all lints that are set to issue warnings");
932

933
    let print_lint_groups = |lints: Vec<(&'static str, Vec<LintId>)>| {
934
        for (name, to) in lints {
935
            let name = name.to_lowercase().replace("_", "-");
M
Mark Rousskov 已提交
936 937 938 939 940
            let desc = to
                .into_iter()
                .map(|x| x.to_string().replace("_", "-"))
                .collect::<Vec<String>>()
                .join(", ");
941
            println!("    {}  {}", padded(&name), desc);
K
Keegan McAllister 已提交
942
        }
943 944 945 946 947 948 949
        println!("\n");
    };

    print_lint_groups(builtin_groups);

    match (loaded_plugins, plugin.len(), plugin_groups.len()) {
        (false, 0, _) | (false, _, 0) => {
950
            println!("Lint tools like Clippy can provide additional lints and lint groups.");
951
        }
V
Vadim Petrochenkov 已提交
952
        (false, ..) => panic!("didn't load lint plugins but got them anyway!"),
953 954 955 956 957 958 959 960 961 962
        (true, 0, 0) => println!("This crate does not load any lint plugins or lint groups."),
        (true, l, g) => {
            if l > 0 {
                println!("Lint checks provided by plugins loaded by this crate:\n");
                print_lints(plugin);
            }
            if g > 0 {
                println!("Lint groups provided by plugins loaded by this crate:\n");
                print_lint_groups(plugin_groups);
            }
K
Keegan McAllister 已提交
963 964
        }
    }
N
Nick Cameron 已提交
965 966 967
}

fn describe_debug_flags() {
968
    println!("\nAvailable options:\n");
969
    print_flag_list("-Z", config::DB_OPTIONS);
N
Nick Cameron 已提交
970 971 972 973
}

fn describe_codegen_flags() {
    println!("\nAvailable codegen options:\n");
974 975 976
    print_flag_list("-C", config::CG_OPTIONS);
}

M
Mark Rousskov 已提交
977 978
fn print_flag_list<T>(
    cmdline_opt: &str,
979
    flag_list: &[(&'static str, T, &'static str, &'static str)],
M
Mark Rousskov 已提交
980
) {
981
    let max_len = flag_list.iter().map(|&(name, _, _, _)| name.chars().count()).max().unwrap_or(0);
982

983
    for &(name, _, _, desc) in flag_list {
M
Mark Rousskov 已提交
984
        println!(
985
            "    {} {:>width$}=val -- {}",
M
Mark Rousskov 已提交
986 987 988
            cmdline_opt,
            name.replace("_", "-"),
            desc,
989
            width = max_len
M
Mark Rousskov 已提交
990
        );
N
Nick Cameron 已提交
991 992 993
    }
}

994
/// Process command line options. Emits messages as appropriate. If compilation
995
/// should continue, returns a getopts::Matches object parsed from args,
A
Alexander Regueiro 已提交
996
/// otherwise returns `None`.
997
///
K
king6cong 已提交
998
/// The compiler's handling of options is a little complicated as it ties into
999 1000
/// our stability story. The current intention of each compiler option is to
/// have one of two modes:
1001 1002
///
/// 1. An option is stable and can be used everywhere.
1003
/// 2. An option is unstable, and can only be used on nightly.
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016
///
/// Like unstable library and language features, however, unstable options have
/// always required a form of "opt in" to indicate that you're using them. This
/// provides the easy ability to scan a code base to check to see if anything
/// unstable is being used. Currently, this "opt in" is the `-Z` "zed" flag.
///
/// All options behind `-Z` are considered unstable by default. Other top-level
/// options can also be considered unstable, and they were unlocked through the
/// `-Z unstable-options` flag. Note that `-Z` remains to be the root of
/// instability in both cases, though.
///
/// So with all that in mind, the comments below have some more detail about the
/// contortions done here to get things to work out correctly.
1017
pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
1018
    // Throw away the first argument, the name of the binary
1019
    let args = &args[1..];
N
Nick Cameron 已提交
1020

1021
    if args.is_empty() {
1022 1023
        // user did not write `-v` nor `-Z unstable-options`, so do not
        // include that extra information.
1024 1025 1026
        let nightly_build =
            rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build();
        usage(false, false, nightly_build);
1027 1028
        return None;
    }
N
Nick Cameron 已提交
1029

1030 1031
    // Parse with *all* options defined in the compiler, we don't worry about
    // option stability here we just want to parse as much as possible.
1032 1033 1034 1035
    let mut options = getopts::Options::new();
    for option in config::rustc_optgroups() {
        (option.apply)(&mut options);
    }
M
Mark Rousskov 已提交
1036 1037 1038
    let matches = options
        .parse(args)
        .unwrap_or_else(|f| early_error(ErrorOutputType::default(), &f.to_string()));
1039

1040 1041 1042 1043 1044 1045 1046 1047 1048
    // For all options we just parsed, we check a few aspects:
    //
    // * If the option is stable, we're all good
    // * If the option wasn't passed, we're all good
    // * If `-Z unstable-options` wasn't passed (and we're not a -Z option
    //   ourselves), then we require the `-Z unstable-options` flag to unlock
    //   this option that was passed.
    // * If we're a nightly compiler, then unstable options are now unlocked, so
    //   we're good to go.
1049
    // * Otherwise, if we're an unstable option then we generate an error
1050
    //   (unstable option being used on stable)
1051
    nightly_options::check_nightly_options(&matches, &config::rustc_optgroups());
1052

N
Nick Cameron 已提交
1053
    if matches.opt_present("h") || matches.opt_present("help") {
1054
        // Only show unstable options in --help if we accept unstable options.
1055 1056 1057
        let unstable_enabled = nightly_options::is_unstable_enabled(&matches);
        let nightly_build = nightly_options::match_is_nightly_build(&matches);
        usage(matches.opt_present("verbose"), unstable_enabled, nightly_build);
N
Nick Cameron 已提交
1058 1059 1060
        return None;
    }

P
Phlosioneer 已提交
1061 1062 1063 1064 1065 1066
    // Handle the special case of -Wall.
    let wall = matches.opt_strs("W");
    if wall.iter().any(|x| *x == "all") {
        print_wall_help();
        return None;
    }
N
Nick Cameron 已提交
1067

1068
    // Don't handle -W help here, because we might first load plugins.
N
Nick Cameron 已提交
1069
    let r = matches.opt_strs("Z");
1070
    if r.iter().any(|x| *x == "help") {
N
Nick Cameron 已提交
1071 1072 1073 1074 1075
        describe_debug_flags();
        return None;
    }

    let cg_flags = matches.opt_strs("C");
1076

1077
    if cg_flags.iter().any(|x| *x == "help") {
N
Nick Cameron 已提交
1078 1079 1080 1081
        describe_codegen_flags();
        return None;
    }

1082
    if cg_flags.iter().any(|x| *x == "no-stack-check") {
M
Mark Rousskov 已提交
1083 1084 1085 1086
        early_warn(
            ErrorOutputType::default(),
            "the --no-stack-check flag is deprecated and does nothing",
        );
1087 1088
    }

L
ljedrz 已提交
1089
    if cg_flags.iter().any(|x| *x == "passes=list") {
1090
        if cfg!(feature = "llvm") {
1091 1092
            get_builtin_codegen_backend("llvm")().print_passes();
        }
N
Nick Cameron 已提交
1093 1094 1095
        return None;
    }

1096
    if matches.opt_present("version") {
1097 1098
        version("rustc", &matches);
        return None;
N
Nick Cameron 已提交
1099 1100 1101 1102 1103
    }

    Some(matches)
}

1104
fn parse_crate_attrs<'a>(sess: &'a Session, input: &Input) -> PResult<'a, Vec<ast::Attribute>> {
1105
    match input {
M
Mark Rousskov 已提交
1106 1107 1108 1109 1110 1111
        Input::File(ifile) => rustc_parse::parse_crate_attrs_from_file(ifile, &sess.parse_sess),
        Input::Str { name, input } => rustc_parse::parse_crate_attrs_from_source_str(
            name.clone(),
            input.clone(),
            &sess.parse_sess,
        ),
1112
    }
N
Nick Cameron 已提交
1113 1114
}

A
Alexander Regueiro 已提交
1115
/// Gets a list of extra command-line flags provided by the user, as strings.
1116 1117 1118 1119 1120
///
/// This function is used during ICEs to show more information useful for
/// debugging, since some ICEs only happens with non-default compiler flags
/// (and the users don't always report them).
fn extra_compiler_flags() -> Option<(Vec<String>, bool)> {
1121
    let args = env::args_os().map(|arg| arg.to_string_lossy().to_string()).collect::<Vec<_>>();
1122

1123 1124 1125 1126 1127 1128
    // Avoid printing help because of empty args. This can suggest the compiler
    // itself is not the program root (consider RLS).
    if args.len() < 2 {
        return None;
    }

1129
    let matches = handle_options(&args)?;
1130 1131 1132 1133 1134 1135 1136
    let mut result = Vec::new();
    let mut excluded_cargo_defaults = false;
    for flag in ICE_REPORT_COMPILER_FLAGS {
        let prefix = if flag.len() == 1 { "-" } else { "--" };

        for content in &matches.opt_strs(flag) {
            // Split always returns the first element
M
Mark Rousskov 已提交
1137
            let name = if let Some(first) = content.split('=').next() { first } else { &content };
1138

M
Mark Rousskov 已提交
1139 1140
            let content =
                if ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE.contains(&name) { name } else { content };
1141 1142 1143 1144 1145 1146 1147 1148 1149

            if !ICE_REPORT_COMPILER_FLAGS_EXCLUDE.contains(&name) {
                result.push(format!("{}{} {}", prefix, flag, content));
            } else {
                excluded_cargo_defaults = true;
            }
        }
    }

M
Mark Rousskov 已提交
1150
    if !result.is_empty() { Some((result, excluded_cargo_defaults)) } else { None }
1151 1152
}

1153
/// Runs a closure and catches unwinds triggered by fatal errors.
N
Nick Cameron 已提交
1154
///
1155
/// The compiler currently unwinds with a special sentinel value to abort
1156 1157 1158
/// compilation on fatal errors. This function catches that sentinel and turns
/// the panic into a `Result` instead.
pub fn catch_fatal_errors<F: FnOnce() -> R, R>(f: F) -> Result<R, ErrorReported> {
1159
    catch_unwind(panic::AssertUnwindSafe(f)).map_err(|value| {
1160
        if value.is::<rustc_errors::FatalErrorMarker>() {
1161
            ErrorReported
1162
        } else {
1163 1164 1165 1166
            panic::resume_unwind(value);
        }
    })
}
N
Nick Cameron 已提交
1167

1168 1169 1170 1171 1172 1173 1174 1175 1176 1177
/// Variant of `catch_fatal_errors` for the `interface::Result` return type
/// that also computes the exit code.
pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 {
    let result = catch_fatal_errors(f).and_then(|result| result);
    match result {
        Ok(()) => EXIT_SUCCESS,
        Err(_) => EXIT_FAILURE,
    }
}

1178 1179
static DEFAULT_HOOK: SyncLazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> =
    SyncLazy::new(|| {
1180
        let hook = panic::take_hook();
1181
        panic::set_hook(Box::new(|info| report_ice(info, BUG_REPORT_URL)));
1182
        hook
1183
    });
1184

1185 1186 1187 1188 1189 1190 1191
/// Prints the ICE message, including backtrace and query stack.
///
/// The message will point the user at `bug_report_url` to report the ICE.
///
/// When `install_ice_hook` is called, this function will be called as the panic
/// hook.
pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
1192
    // Invoke the default handler, which prints the actual panic message and optionally a backtrace
1193 1194
    (*DEFAULT_HOOK)(info);

J
Jonas Schievink 已提交
1195
    // Separate the output with an empty line
1196 1197
    eprintln!();

1198 1199
    let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
        rustc_errors::ColorConfig::Auto,
1200 1201 1202 1203
        None,
        false,
        false,
        None,
1204
        false,
1205
    ));
1206
    let handler = rustc_errors::Handler::with_emitter(true, None, emitter);
1207 1208 1209

    // a .span_bug or .bug call has already printed what
    // it wants to print.
1210 1211
    if !info.payload().is::<rustc_errors::ExplicitBug>() {
        let d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
1212
        handler.emit_diagnostic(&d);
1213
    }
1214

1215 1216
    let mut xs: Vec<Cow<'static, str>> = vec![
        "the compiler unexpectedly panicked. this is a bug.".into(),
1217
        format!("we would appreciate a bug report: {}", bug_report_url).into(),
M
Mark Rousskov 已提交
1218 1219
        format!(
            "rustc {} running on {}",
1220
            util::version_str().unwrap_or("unknown_version"),
M
Mark Rousskov 已提交
1221 1222 1223
            config::host_triple()
        )
        .into(),
1224
    ];
1225

1226 1227
    if let Some((flags, excluded_cargo_defaults)) = extra_compiler_flags() {
        xs.push(format!("compiler flags: {}", flags.join(" ")).into());
1228

1229 1230
        if excluded_cargo_defaults {
            xs.push("some of the compiler flags provided by cargo are hidden".into());
1231
        }
1232 1233 1234
    }

    for note in &xs {
1235
        handler.note_without_error(&note);
1236
    }
1237 1238

    // If backtraces are enabled, also print the query stack
1239
    let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0");
1240

H
hosseind75 已提交
1241 1242 1243
    let num_frames = if backtrace { None } else { Some(2) };

    TyCtxt::try_print_query_stack(&handler, num_frames);
1244 1245 1246 1247 1248

    #[cfg(windows)]
    unsafe {
        if env::var("RUSTC_BREAK_ON_ICE").is_ok() {
            // Trigger a debugger if we crashed during bootstrap
1249
            winapi::um::debugapi::DebugBreak();
1250 1251
        }
    }
1252 1253
}

1254 1255 1256
/// Installs a panic hook that will print the ICE message on unexpected panics.
///
/// A custom rustc driver can skip calling this to set up a custom ICE hook.
1257
pub fn install_ice_hook() {
1258
    SyncLazy::force(&DEFAULT_HOOK);
N
Nick Cameron 已提交
1259
}
1260

1261
/// This allows tools to enable rust logging without having to magically match rustc's
G
Gurpreet Singh 已提交
1262
/// tracing crate version.
1263
pub fn init_rustc_env_logger() {
1264 1265 1266 1267
    init_env_logger("RUSTC_LOG")
}

/// This allows tools to enable rust logging without having to magically match rustc's
G
Gurpreet Singh 已提交
1268
/// tracing crate version. In contrast to `init_rustc_env_logger` it allows you to choose an env var
1269 1270
/// other than `RUSTC_LOG`.
pub fn init_env_logger(env: &str) {
1271 1272 1273 1274 1275 1276
    // Don't register a dispatcher if there's no filter to print anything
    match std::env::var(env) {
        Err(_) => return,
        Ok(s) if s.is_empty() => return,
        Ok(_) => {}
    }
1277 1278 1279 1280
    let color_logs = match std::env::var(String::from(env) + "_COLOR") {
        Ok(value) => match value.as_ref() {
            "always" => true,
            "never" => false,
1281
            "auto" => stderr_isatty(),
C
Camelid 已提交
1282 1283 1284 1285 1286 1287 1288
            _ => early_error(
                ErrorOutputType::default(),
                &format!(
                    "invalid log color value '{}': expected one of always, never, or auto",
                    value
                ),
            ),
1289
        },
1290
        Err(std::env::VarError::NotPresent) => stderr_isatty(),
C
Camelid 已提交
1291 1292 1293 1294
        Err(std::env::VarError::NotUnicode(_value)) => early_error(
            ErrorOutputType::default(),
            "non-Unicode log color value: expected one of always, never, or auto",
        ),
1295
    };
1296 1297
    let filter = tracing_subscriber::EnvFilter::from_env(env);
    let layer = tracing_tree::HierarchicalLayer::default()
1298
        .with_writer(io::stderr)
1299
        .with_indent_lines(true)
1300
        .with_ansi(color_logs)
1301 1302 1303 1304 1305
        .with_targets(true)
        .with_wraparound(10)
        .with_verbose_exit(true)
        .with_verbose_entry(true)
        .with_indent_amount(2);
1306 1307
    #[cfg(parallel_compiler)]
    let layer = layer.with_thread_ids(true).with_thread_names(true);
1308 1309 1310 1311

    use tracing_subscriber::layer::SubscriberExt;
    let subscriber = tracing_subscriber::Registry::default().with(filter).with(layer);
    tracing::subscriber::set_global_default(subscriber).unwrap();
1312 1313
}

1314
pub fn main() -> ! {
1315
    let start = Instant::now();
1316
    init_rustc_env_logger();
1317
    let mut callbacks = TimePassesCallbacks::default();
1318
    install_ice_hook();
1319
    let exit_code = catch_with_exit_code(|| {
M
Mark Rousskov 已提交
1320 1321 1322 1323 1324 1325
        let args = env::args_os()
            .enumerate()
            .map(|(i, arg)| {
                arg.into_string().unwrap_or_else(|arg| {
                    early_error(
                        ErrorOutputType::default(),
C
Camelid 已提交
1326
                        &format!("argument {} is not valid Unicode: {:?}", i, arg),
M
Mark Rousskov 已提交
1327 1328 1329
                    )
                })
            })
B
bjorn3 已提交
1330
            .collect::<Vec<_>>();
1331
        RunCompiler::new(&args, &mut callbacks).run()
1332
    });
1333 1334
    // The extra `\t` is necessary to align this label with the others.
    print_time_passes_entry(callbacks.time_passes, "\ttotal", start.elapsed());
1335
    process::exit(exit_code)
1336
}