rust/src/librustc_driver/lib.rs

1274 lines
44 KiB
Rust
Raw Normal View History

//! The Rust compiler.
//!
//! # Note
//!
//! This API is completely unstable and subject to change.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
#![feature(nll)]
2019-12-22 23:42:04 +01:00
#![recursion_limit = "256"]
2018-03-03 06:19:15 +01:00
2015-11-10 21:48:44 +01:00
#[macro_use]
extern crate log;
#[macro_use]
extern crate lazy_static;
pub extern crate rustc_plugin_impl as plugin;
use rustc_ast::ast;
use rustc_codegen_ssa::{traits::CodegenBackend, CodegenResults};
use rustc_data_structures::profiling::print_time_passes_entry;
use rustc_data_structures::sync::SeqCst;
2020-03-31 21:18:30 +02:00
use rustc_errors::registry::{InvalidErrorCode, Registry};
use rustc_errors::{ErrorReported, PResult};
2019-11-30 02:50:47 +01:00
use rustc_feature::{find_gated_cfg, UnstableFeatures};
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_interface::util::{collect_crate_types, get_builtin_codegen_backend};
2019-12-22 23:42:04 +01:00
use rustc_interface::{interface, Queries};
use rustc_lint::LintStore;
2019-12-22 23:42:04 +01:00
use rustc_metadata::locator;
2020-03-29 17:19:48 +02:00
use rustc_middle::middle::cstore::MetadataLoader;
use rustc_middle::ty::TyCtxt;
2019-12-22 23:42:04 +01:00
use rustc_save_analysis as save;
use rustc_save_analysis::DumpHandler;
use rustc_serialize::json::{self, ToJson};
use rustc_session::config::nightly_options;
use rustc_session::config::{ErrorOutputType, Input, OutputType, PrintRequest};
use rustc_session::getopts;
use rustc_session::lint::{Lint, LintId};
use rustc_session::{config, DiagnosticOutput, Session};
use rustc_session::{early_error, early_warn};
use rustc_span::source_map::{FileLoader, FileName};
use rustc_span::symbol::sym;
use std::borrow::Cow;
use std::cmp::max;
use std::default::Default;
std: Add a new `env` module This is an implementation of [RFC 578][rfc] which adds a new `std::env` module to replace most of the functionality in the current `std::os` module. More details can be found in the RFC itself, but as a summary the following methods have all been deprecated: [rfc]: https://github.com/rust-lang/rfcs/pull/578 * `os::args_as_bytes` => `env::args` * `os::args` => `env::args` * `os::consts` => `env::consts` * `os::dll_filename` => no replacement, use `env::consts` directly * `os::page_size` => `env::page_size` * `os::make_absolute` => use `env::current_dir` + `join` instead * `os::getcwd` => `env::current_dir` * `os::change_dir` => `env::set_current_dir` * `os::homedir` => `env::home_dir` * `os::tmpdir` => `env::temp_dir` * `os::join_paths` => `env::join_paths` * `os::split_paths` => `env::split_paths` * `os::self_exe_name` => `env::current_exe` * `os::self_exe_path` => use `env::current_exe` + `pop` * `os::set_exit_status` => `env::set_exit_status` * `os::get_exit_status` => `env::get_exit_status` * `os::env` => `env::vars` * `os::env_as_bytes` => `env::vars` * `os::getenv` => `env::var` or `env::var_string` * `os::getenv_as_bytes` => `env::var` * `os::setenv` => `env::set_var` * `os::unsetenv` => `env::remove_var` Many function signatures have also been tweaked for various purposes, but the main changes were: * `Vec`-returning APIs now all return iterators instead * All APIs are now centered around `OsString` instead of `Vec<u8>` or `String`. There is currently on convenience API, `env::var_string`, which can be used to get the value of an environment variable as a unicode `String`. All old APIs are `#[deprecated]` in-place and will remain for some time to allow for migrations. The semantics of the APIs have been tweaked slightly with regard to dealing with invalid unicode (panic instead of replacement). The new `std::env` module is all contained within the `env` feature, so crates must add the following to access the new APIs: #![feature(env)] [breaking-change]
2015-01-27 21:20:58 +01:00
use std::env;
use std::ffi::OsString;
use std::fs;
2015-03-11 23:24:14 +01:00
use std::io::{self, Read, Write};
use std::mem;
use std::panic::{self, catch_unwind};
use std::path::PathBuf;
use std::process::{self, Command, Stdio};
2015-03-11 23:24:14 +01:00
use std::str;
use std::time::Instant;
mod args;
2019-12-22 23:42:04 +01:00
pub mod pretty;
/// Exit status code used for successful compilation and help output.
pub const EXIT_SUCCESS: i32 = 0;
/// Exit status code used for compilation failures and invalid flags.
pub const EXIT_FAILURE: i32 = 1;
const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.\
md#bug-reports";
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"];
pub fn abort_on_err<T>(result: Result<T, ErrorReported>, sess: &Session) -> T {
match result {
Err(..) => {
sess.abort_if_errors();
panic!("error reported but abort_if_errors didn't abort???");
}
Ok(x) => x,
}
}
pub trait Callbacks {
/// Called before creating the compiler instance
fn config(&mut self, _config: &mut interface::Config) {}
/// Called after parsing. Return value instructs the compiler whether to
/// continue the compilation afterwards (defaults to `Compilation::Continue`)
2019-11-30 10:16:19 +01:00
fn after_parsing<'tcx>(
&mut self,
_compiler: &interface::Compiler,
_queries: &'tcx Queries<'tcx>,
) -> Compilation {
Compilation::Continue
}
/// Called after expansion. Return value instructs the compiler whether to
/// continue the compilation afterwards (defaults to `Compilation::Continue`)
2019-11-30 10:16:19 +01:00
fn after_expansion<'tcx>(
&mut self,
_compiler: &interface::Compiler,
_queries: &'tcx Queries<'tcx>,
) -> Compilation {
Compilation::Continue
2019-07-14 20:24:48 +02:00
}
/// Called after analysis. Return value instructs the compiler whether to
/// continue the compilation afterwards (defaults to `Compilation::Continue`)
2019-11-30 10:16:19 +01:00
fn after_analysis<'tcx>(
&mut self,
_compiler: &interface::Compiler,
_queries: &'tcx Queries<'tcx>,
) -> Compilation {
Compilation::Continue
}
}
#[derive(Default)]
pub struct TimePassesCallbacks {
time_passes: bool,
}
impl Callbacks for TimePassesCallbacks {
fn config(&mut self, config: &mut interface::Config) {
// If a --prints=... option has been given, we don't print the "total"
// time because it will mess up the --prints output. See #64339.
2019-12-22 23:42:04 +01:00
self.time_passes = config.opts.prints.is_empty()
&& (config.opts.debugging_opts.time_passes || config.opts.debugging_opts.time);
}
}
2019-11-15 19:41:50 +01:00
pub fn diagnostics_registry() -> Registry {
Registry::new(&rustc_error_codes::DIAGNOSTICS)
}
// 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.
pub fn run_compiler(
at_args: &[String],
callbacks: &mut (dyn Callbacks + Send),
file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
2019-12-22 23:42:04 +01:00
emitter: Option<Box<dyn Write + Send>>,
) -> interface::Result<()> {
let mut args = Vec::new();
for arg in at_args {
match args::arg_expand(arg.clone()) {
Ok(arg) => args.extend(arg),
2019-12-22 23:42:04 +01:00
Err(err) => early_error(
ErrorOutputType::default(),
&format!("Failed to load argument file: {}", err),
),
}
}
2019-12-22 23:42:04 +01:00
let diagnostic_output =
emitter.map(|emitter| DiagnosticOutput::Raw(emitter)).unwrap_or(DiagnosticOutput::Default);
let matches = match handle_options(&args) {
Some(matches) => matches,
None => return Ok(()),
};
2018-04-26 00:49:52 +02:00
let sopts = config::build_session_options(&matches);
2019-10-11 23:48:16 +02:00
let cfg = interface::parse_cfgspecs(matches.opt_strs("cfg"));
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,
crate_name: None,
lint_caps: Default::default(),
register_lints: None,
override_queries: None,
2019-11-15 19:41:50 +01:00
registry: diagnostics_registry(),
};
callbacks.config(&mut config);
config
};
if let Some(ref code) = matches.opt_str("explain") {
2019-11-15 19:41:50 +01:00
handle_explain(diagnostics_registry(), code, sopts.error_format);
return Ok(());
}
let (odir, ofile) = make_output(&matches);
let (input, input_file_path, input_err) = match make_input(&matches.free) {
Some(v) => v,
2019-12-22 23:42:04 +01:00
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 {
let lint_store = rustc_lint::new_lint_store(
sopts.debugging_opts.no_interleave_lints,
compiler.session().unstable_options(),
);
2019-12-22 23:42:04 +01:00
describe_lints(compiler.session(), &lint_store, false);
return;
}
let should_stop = RustcDefaultCalls::print_crate_info(
&***compiler.codegen_backend(),
compiler.session(),
None,
&odir,
&ofile,
);
2019-12-22 23:42:04 +01:00
if should_stop == Compilation::Stop {
return;
}
early_error(sopts.error_format, "no input filename given")
});
return Ok(());
}
2019-12-22 23:42:04 +01:00
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],
),
),
},
};
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).
interface::run_compiler(dummy_config(sopts, cfg, diagnostic_output), |compiler| {
compiler.session().err(&err.to_string());
});
return Err(ErrorReported);
}
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,
crate_name: None,
lint_caps: Default::default(),
register_lints: None,
override_queries: None,
2019-11-15 19:41:50 +01:00
registry: diagnostics_registry(),
};
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(),
2019-12-22 23:42:04 +01:00
)
.and_then(|| {
RustcDefaultCalls::list_metadata(
sess,
&*compiler.codegen_backend().metadata_loader(),
&matches,
compiler.input(),
)
})
.and_then(|| RustcDefaultCalls::try_process_rlink(sess, compiler));
if should_stop == Compilation::Stop {
return sess.compile_status();
}
let linker = compiler.enter(|queries| {
2019-11-25 17:29:32 +01:00
let early_exit = || sess.compile_status().map(|_| None);
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,
*ppm,
compiler.output_file().as_ref().map(|p| &**p),
);
}
2019-11-25 17:29:32 +01:00
return early_exit();
}
2019-11-30 10:16:19 +01:00
if callbacks.after_parsing(compiler, queries) == Compilation::Stop {
2019-11-25 17:29:32 +01:00
return early_exit();
}
2019-12-22 23:42:04 +01:00
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();
}
{
let (_, lint_store) = &*queries.register_plugins()?.peek();
// Lint plugins are registered; now we can process command line flags.
if sess.opts.describe_lints {
describe_lints(&sess, &lint_store, true);
2019-11-25 17:29:32 +01:00
return early_exit();
}
}
queries.expansion()?;
2019-11-30 10:16:19 +01:00
if callbacks.after_expansion(compiler, queries) == Compilation::Stop {
2019-11-25 17:29:32 +01:00
return early_exit();
}
queries.prepare_outputs()?;
if sess.opts.output_types.contains_key(&OutputType::DepInfo)
&& sess.opts.output_types.len() == 1
{
2019-11-25 17:29:32 +01:00
return early_exit();
}
2019-07-14 20:24:48 +02:00
queries.global_ctxt()?;
2019-12-22 23:42:04 +01:00
if sess.opts.debugging_opts.no_analysis || sess.opts.debugging_opts.ast_json {
return early_exit();
}
if sess.opts.debugging_opts.save_analysis {
let expanded_crate = &queries.expansion()?.peek().0;
let crate_name = queries.crate_name()?.peek().clone();
queries.global_ctxt()?.peek_mut().enter(|tcx| {
let result = tcx.analysis(LOCAL_CRATE);
sess.time("save_analysis", || {
save::process_crate(
tcx,
&expanded_crate,
&crate_name,
&compiler.input(),
None,
2019-11-24 16:41:43 +01:00
DumpHandler::new(
2019-12-22 23:42:04 +01:00
compiler.output_dir().as_ref().map(|p| &**p),
&crate_name,
),
)
});
result
// AST will be dropped *after* the `after_analysis` callback
// (needed by the RLS)
})?;
} else {
// Drop AST after creating GlobalCtxt to free memory
2020-01-09 03:48:00 +01:00
let _timer = sess.prof.generic_activity("drop_ast");
mem::drop(queries.expansion()?.take());
}
queries.global_ctxt()?.peek_mut().enter(|tcx| tcx.analysis(LOCAL_CRATE))?;
2019-11-30 10:16:19 +01:00
if callbacks.after_analysis(compiler, queries) == Compilation::Stop {
2019-11-25 17:29:32 +01:00
return early_exit();
}
if sess.opts.debugging_opts.save_analysis {
mem::drop(queries.expansion()?.take());
}
queries.ongoing_codegen()?;
if sess.opts.debugging_opts.print_type_sizes {
sess.code_stats.print_type_sizes();
}
let linker = queries.linker()?;
Ok(Some(linker))
})?;
if let Some(linker) = linker {
2020-01-09 03:48:00 +01:00
let _timer = sess.timer("link");
linker.link()?
}
if sess.opts.debugging_opts.perf_stats {
sess.print_perf_stats();
}
if sess.print_fuel_crate.is_some() {
2019-12-22 23:42:04 +01:00
eprintln!(
"Fuel used by {}: {}",
sess.print_fuel_crate.as_ref().unwrap(),
2019-12-22 23:42:04 +01:00
sess.print_fuel.load(SeqCst)
);
}
Ok(())
})
}
#[cfg(unix)]
pub fn set_sigpipe_handler() {
unsafe {
// Set the SIGPIPE signal handler, so that an EPIPE
// will cause rustc to terminate, as expected.
2018-10-12 14:01:25 +02:00
assert_ne!(libc::signal(libc::SIGPIPE, libc::SIG_DFL), libc::SIG_ERR);
}
}
#[cfg(windows)]
pub fn set_sigpipe_handler() {}
// Extract output directory and file from matches.
fn make_output(matches: &getopts::Matches) -> (Option<PathBuf>, Option<PathBuf>) {
let odir = matches.opt_str("out-dir").map(|o| PathBuf::from(&o));
let ofile = matches.opt_str("o").map(|o| PathBuf::from(&o));
(odir, ofile)
}
// Extract input (string or file and optional path) from matches.
fn make_input(free_matches: &[String]) -> Option<(Input, Option<PathBuf>, Option<io::Error>)> {
if free_matches.len() == 1 {
let ifile = &free_matches[0];
if ifile == "-" {
2015-03-11 23:24:14 +01:00
let mut src = String::new();
let err = if io::stdin().read_to_string(&mut src).is_err() {
2019-12-22 23:42:04 +01:00
Some(io::Error::new(
io::ErrorKind::InvalidData,
"couldn't read from stdin, as it did not contain valid UTF-8",
))
} else {
None
};
if let Ok(path) = env::var("UNSTABLE_RUSTDOC_TEST_PATH") {
2019-12-22 23:42:04 +01:00
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");
let file_name = FileName::doc_test_source_code(PathBuf::from(path), line);
return Some((Input::Str { name: file_name, input: src }, None, err));
}
2019-12-22 23:42:04 +01:00
Some((Input::Str { name: FileName::anon_source_code(&src), input: src }, None, err))
} else {
2019-12-22 23:42:04 +01:00
Some((Input::File(PathBuf::from(ifile)), Some(PathBuf::from(ifile)), None))
}
} else {
None
}
}
2015-02-03 01:40:52 +01:00
// Whether to stop or continue compilation.
2015-03-30 15:38:44 +02:00
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
2015-02-03 01:40:52 +01:00
pub enum Compilation {
Stop,
Continue,
}
impl Compilation {
pub fn and_then<F: FnOnce() -> Compilation>(self, next: F) -> Compilation {
match self {
Compilation::Stop => Compilation::Stop,
2015-11-10 21:48:44 +01:00
Compilation::Continue => next(),
2015-02-03 01:40:52 +01:00
}
}
}
/// CompilerCalls instance for a regular rustc build.
2015-03-30 15:38:44 +02:00
#[derive(Copy, Clone)]
pub struct RustcDefaultCalls;
2017-07-03 10:45:31 +02:00
// FIXME remove these and use winapi 0.3 instead
// Duplicates: bootstrap/compile.rs, librustc_errors/emitter.rs
#[cfg(unix)]
fn stdout_isatty() -> bool {
unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 }
}
2017-07-03 10:45:31 +02:00
#[cfg(windows)]
fn stdout_isatty() -> bool {
use winapi::um::consoleapi::GetConsoleMode;
use winapi::um::processenv::GetStdHandle;
use winapi::um::winbase::STD_OUTPUT_HANDLE;
unsafe {
let handle = GetStdHandle(STD_OUTPUT_HANDLE);
let mut out = 0;
GetConsoleMode(handle, &mut out) != 0
}
}
2019-11-15 19:41:50 +01:00
fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) {
2019-12-22 23:42:04 +01:00
let normalised =
if code.starts_with('E') { code.to_string() } else { format!("E{0:0>4}", code) };
match registry.try_find_description(&normalised) {
Ok(Some(description)) => {
let mut is_in_code_block = false;
let mut text = String::new();
// Slice off the leading newline and print.
for line in description.lines() {
2019-12-22 23:42:04 +01:00
let indent_level =
line.find(|c: char| !c.is_whitespace()).unwrap_or_else(|| line.len());
let dedented_line = &line[indent_level..];
if dedented_line.starts_with("```") {
is_in_code_block = !is_in_code_block;
text.push_str(&line[..(indent_level + 3)]);
} else if is_in_code_block && dedented_line.starts_with("# ") {
continue;
2016-05-01 18:13:36 +02:00
} else {
text.push_str(line);
}
text.push('\n');
}
2017-07-02 22:27:10 +02:00
if stdout_isatty() {
show_content_with_pager(&text);
} else {
print!("{}", text);
}
}
Ok(None) => {
early_error(output, &format!("no extended information for {}", code));
}
Err(InvalidErrorCode) => {
early_error(output, &format!("{} is not a valid error code", code));
}
}
}
fn show_content_with_pager(content: &String) {
2019-12-22 23:42:04 +01:00
let pager_name = env::var_os("PAGER").unwrap_or_else(|| {
if cfg!(windows) { OsString::from("more.com") } else { OsString::from("less") }
});
let mut fallback_to_println = false;
match Command::new(pager_name).stdin(Stdio::piped()).spawn() {
Ok(mut pager) => {
if let Some(pipe) = pager.stdin.as_mut() {
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 {
2017-06-29 09:40:51 +02:00
print!("{}", content);
}
}
impl RustcDefaultCalls {
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![];
sess.crate_types.set(collect_crate_types(sess, &attrs));
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));
});
compiler.codegen_backend().link(&sess, Box::new(codegen_results), &outputs)
} else {
2020-02-27 13:34:08 +01:00
sess.fatal("rlink must be a file")
}
}
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
}
}
2019-12-22 23:42:04 +01:00
pub fn list_metadata(
sess: &Session,
metadata_loader: &dyn MetadataLoader,
matches: &getopts::Matches,
input: &Input,
) -> Compilation {
let r = matches.opt_strs("Z");
if r.iter().any(|s| *s == "ls") {
match input {
&Input::File(ref ifile) => {
let path = &(*ifile);
let mut v = Vec::new();
2019-12-22 23:42:04 +01:00
locator::list_file_metadata(&sess.target.target, path, metadata_loader, &mut v)
.unwrap();
println!("{}", String::from_utf8(v).unwrap());
}
&Input::Str { .. } => {
early_error(ErrorOutputType::default(), "cannot list metadata for stdin");
}
}
2015-02-03 01:40:52 +01:00
return Compilation::Stop;
}
Compilation::Continue
}
2019-12-22 23:42:04 +01:00
fn print_crate_info(
codegen_backend: &dyn CodegenBackend,
sess: &Session,
input: Option<&Input>,
odir: &Option<PathBuf>,
ofile: &Option<PathBuf>,
) -> Compilation {
use rustc_session::config::PrintRequest::*;
2017-08-22 22:20:42 +02:00
// PrintRequest::NativeStaticLibs is special - printed during linking
// (empty iterator returns true)
if sess.opts.prints.iter().all(|&p| p == PrintRequest::NativeStaticLibs) {
2015-02-03 01:40:52 +01:00
return Compilation::Continue;
}
2016-02-19 14:43:13 +01:00
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;
}
}
}
};
for req in &sess.opts.prints {
match *req {
TargetList => {
let mut targets = rustc_target::spec::get_targets().collect::<Vec<String>>();
targets.sort();
println!("{}", targets.join("\n"));
2019-12-22 23:42:04 +01:00
}
Sysroot => println!("{}", sess.sysroot.display()),
TargetLibdir => println!(
"{}",
sess.target_tlib_path.as_ref().unwrap_or(&sess.host_tlib_path).dir.display()
),
TargetSpec => println!("{}", sess.target.target.to_json().pretty()),
FileNames | CrateName => {
2019-12-22 23:42:04 +01:00
let input = input.unwrap_or_else(|| {
early_error(ErrorOutputType::default(), "no input file provided")
});
let attrs = attrs.as_ref().unwrap();
let t_outputs = rustc_interface::util::build_output_filenames(
2019-12-22 23:42:04 +01:00
input, odir, ofile, attrs, sess,
);
let id = rustc_session::output::find_crate_name(Some(sess), attrs, input);
if *req == PrintRequest::CrateName {
println!("{}", id);
2015-11-10 21:48:44 +01:00
continue;
}
let crate_types = collect_crate_types(sess, attrs);
for &style in &crate_types {
let fname =
rustc_session::output::filename_for_input(sess, style, &id, &t_outputs);
println!("{}", fname.file_name().unwrap().to_string_lossy());
}
}
Cfg => {
2019-12-22 23:42:04 +01:00
let allow_unstable_cfg =
UnstableFeatures::from_environment().is_nightly_build();
let mut cfgs = sess
.parse_sess
.config
.iter()
.filter_map(|&(name, ref value)| {
// 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.
let value = value.as_ref().map(|s| s.as_str());
let value = value.as_ref().map(|s| s.as_ref());
if (name != sym::target_feature || value != Some("crt-static"))
&& !allow_unstable_cfg
&& 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>>();
2016-11-15 09:54:27 +01:00
cfgs.sort();
for cfg in cfgs {
println!("{}", cfg);
}
}
RelocationModels | CodeModels | TlsModels | TargetCPUs | TargetFeatures => {
2018-05-08 15:10:16 +02:00
codegen_backend.print(*req, sess);
}
// Any output here interferes with Cargo's parsing of other printed output
PrintRequest::NativeStaticLibs => {}
}
}
Compilation::Stop
Preliminary feature staging This partially implements the feature staging described in the [release channel RFC][rc]. It does not yet fully conform to the RFC as written, but does accomplish its goals sufficiently for the 1.0 alpha release. It has three primary user-visible effects: * On the nightly channel, use of unstable APIs generates a warning. * On the beta channel, use of unstable APIs generates a warning. * On the beta channel, use of feature gates generates a warning. Code that does not trigger these warnings is considered 'stable', modulo pre-1.0 bugs. Disabling the warnings for unstable APIs continues to be done in the existing (i.e. old) style, via `#[allow(...)]`, not that specified in the RFC. I deem this marginally acceptable since any code that must do this is not using the stable dialect of Rust. Use of feature gates is itself gated with the new 'unstable_features' lint, on nightly set to 'allow', and on beta 'warn'. The attribute scheme used here corresponds to an older version of the RFC, with the `#[staged_api]` crate attribute toggling the staging behavior of the stability attributes, but the user impact is only in-tree so I'm not concerned about having to make design changes later (and I may ultimately prefer the scheme here after all, with the `#[staged_api]` crate attribute). Since the Rust codebase itself makes use of unstable features the compiler and build system to a midly elaborate dance to allow it to bootstrap while disobeying these lints (which would otherwise be errors because Rust builds with `-D warnings`). This patch includes one significant hack that causes a regression. Because the `format_args!` macro emits calls to unstable APIs it would trigger the lint. I added a hack to the lint to make it not trigger, but this in turn causes arguments to `println!` not to be checked for feature gates. I don't presently understand macro expansion well enough to fix. This is bug #20661. Closes #16678 [rc]: https://github.com/rust-lang/rfcs/blob/master/text/0507-release-channels.md
2015-01-06 15:26:08 +01:00
}
}
/// Returns a version string such as "0.12.0-dev".
2017-08-19 02:09:55 +02:00
fn release_str() -> Option<&'static str> {
option_env!("CFG_RELEASE")
}
/// Returns the full SHA1 hash of HEAD of the Git repo from which rustc was built.
2017-08-19 02:09:55 +02:00
fn commit_hash_str() -> Option<&'static str> {
option_env!("CFG_VER_HASH")
}
/// Returns the "commit date" of HEAD of the Git repo from which rustc was built as a static string.
2017-08-19 02:09:55 +02:00
fn commit_date_str() -> Option<&'static str> {
option_env!("CFG_VER_DATE")
}
2015-06-26 19:32:27 +02:00
/// Prints version information
rustc: Start "stabilizing" some flags This commit shuffles around some CLI flags of the compiler to some more stable locations with some renamings. The changes made were: * The `-v` flag has been repurposes as the "verbose" flag. The version flag has been renamed to `-V`. * The `-h` screen has been split into two parts. Most top-level options (not all) show with `-h`, and the remaining options (generally obscure) can be shown with `--help -v` which is a "verbose help screen" * The `-V` flag (version flag now) has lost its argument as it is now requested with `rustc -vV` "verbose version". * The `--emit` option has had its `ir` and `bc` variants renamed to `llvm-ir` and `llvm-bc` to emphasize that they are LLVM's IR/bytecode. * The `--emit` option has grown a new variant, `dep-info`, which subsumes the `--dep-info` CLI argument. The `--dep-info` flag is now deprecated. * The `--parse-only`, `--no-trans`, and `--no-analysis` flags have moved behind the `-Z` family of flags. * The `--debuginfo` and `--opt-level` flags were moved behind the top-level `-C` flag. * The `--print-file-name` and `--print-crate-name` flags were moved behind one global `--print` flag which now accepts one of `crate-name`, `file-names`, or `sysroot`. This global `--print` flag is intended to serve as a mechanism for learning various metadata about the compiler itself. No warnings are currently enabled to allow tools like Cargo to have time to migrate to the new flags before spraying warnings to all users.
2014-12-16 01:03:39 +01:00
pub fn version(binary: &str, matches: &getopts::Matches) {
let verbose = matches.opt_present("verbose");
println!("{} {}", binary, option_env!("CFG_VERSION").unwrap_or("unknown version"));
if verbose {
2015-11-10 21:48:44 +01:00
fn unw(x: Option<&str>) -> &str {
x.unwrap_or("unknown")
}
println!("binary: {}", binary);
println!("commit-hash: {}", unw(commit_hash_str()));
println!("commit-date: {}", unw(commit_date_str()));
println!("host: {}", config::host_triple());
println!("release: {}", unw(release_str()));
rustc: Link LLVM directly into rustc again This commit builds on #65501 continue to simplify the build system and compiler now that we no longer have multiple LLVM backends to ship by default. Here this switches the compiler back to what it once was long long ago, which is linking LLVM directly to the compiler rather than dynamically loading it at runtime. The `codegen-backends` directory of the sysroot no longer exists and all relevant support in the build system is removed. Note that `rustc` still supports a dynamically loaded codegen backend as it did previously, it just no longer supports dynamically loaded codegen backends in its own sysroot. Additionally as part of this the `librustc_codegen_llvm` crate now once again explicitly depends on all of its crates instead of implicitly loading them through the sysroot. This involved filling out its `Cargo.toml` and deleting all the now-unnecessary `extern crate` annotations in the header of the crate. (this in turn required adding a number of imports for names of macros too). The end results of this change are: * Rustbuild's build process for the compiler as all the "oh don't forget the codegen backend" checks can be easily removed. * Building `rustc_codegen_llvm` is much simpler since it's simply another compiler crate. * Managing the dependencies of `rustc_codegen_llvm` is much simpler since it's "just another `Cargo.toml` to edit" * The build process should be a smidge faster because there's more parallelism in the main rustc build step rather than splitting `librustc_codegen_llvm` out to its own step. * The compiler is expected to be slightly faster by default because the codegen backend does not need to be dynamically loaded. * Disabling LLVM as part of rustbuild is still supported, supporting multiple codegen backends is still supported, and dynamic loading of a codegen backend is still supported.
2019-10-22 17:51:35 +02:00
get_builtin_codegen_backend("llvm")().print_version();
}
}
fn usage(verbose: bool, include_unstable_options: bool) {
2019-12-22 23:42:04 +01:00
let groups = if verbose { config::rustc_optgroups() } else { config::rustc_short_optgroups() };
let mut options = getopts::Options::new();
for option in groups.iter().filter(|x| include_unstable_options || x.is_stable()) {
(option.apply)(&mut options);
}
let message = "Usage: rustc [OPTIONS] INPUT";
let nightly_help = if nightly_options::is_nightly_build() {
"\n -Z help Print unstable compiler options"
} else {
""
};
let verbose_help = if verbose {
rustc: Start "stabilizing" some flags This commit shuffles around some CLI flags of the compiler to some more stable locations with some renamings. The changes made were: * The `-v` flag has been repurposes as the "verbose" flag. The version flag has been renamed to `-V`. * The `-h` screen has been split into two parts. Most top-level options (not all) show with `-h`, and the remaining options (generally obscure) can be shown with `--help -v` which is a "verbose help screen" * The `-V` flag (version flag now) has lost its argument as it is now requested with `rustc -vV` "verbose version". * The `--emit` option has had its `ir` and `bc` variants renamed to `llvm-ir` and `llvm-bc` to emphasize that they are LLVM's IR/bytecode. * The `--emit` option has grown a new variant, `dep-info`, which subsumes the `--dep-info` CLI argument. The `--dep-info` flag is now deprecated. * The `--parse-only`, `--no-trans`, and `--no-analysis` flags have moved behind the `-Z` family of flags. * The `--debuginfo` and `--opt-level` flags were moved behind the top-level `-C` flag. * The `--print-file-name` and `--print-crate-name` flags were moved behind one global `--print` flag which now accepts one of `crate-name`, `file-names`, or `sysroot`. This global `--print` flag is intended to serve as a mechanism for learning various metadata about the compiler itself. No warnings are currently enabled to allow tools like Cargo to have time to migrate to the new flags before spraying warnings to all users.
2014-12-16 01:03:39 +01:00
""
} else {
"\n --help -v Print the full set of options rustc accepts"
};
let at_path = if verbose && nightly_options::is_nightly_build() {
" @path Read newline separated options from `path`\n"
} else {
""
};
2019-12-22 23:42:04 +01:00
println!(
"{options}{at_path}\nAdditional help:
-C help Print codegen options
2015-11-10 21:48:44 +01:00
-W help \
Print 'lint' options and default settings{nightly}{verbose}\n",
2019-12-22 23:42:04 +01:00
options = options.usage(message),
at_path = at_path,
nightly = nightly_help,
verbose = verbose_help
);
}
fn print_wall_help() {
2019-12-22 23:42:04 +01:00
println!(
"
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
the command line flag directly.
2019-12-22 23:42:04 +01:00
"
);
}
fn describe_lints(sess: &Session, lint_store: &LintStore, loaded_plugins: bool) {
2019-12-22 23:42:04 +01:00
println!(
"
Available lint options:
-W <foo> Warn about <foo>
2015-11-10 21:48:44 +01:00
-A <foo> \
Allow <foo>
-D <foo> Deny <foo>
2015-11-10 21:48:44 +01:00
-F <foo> Forbid <foo> \
(deny <foo> and all attempts to override)
2019-12-22 23:42:04 +01:00
"
);
fn sort_lints(sess: &Session, mut lints: Vec<&'static Lint>) -> Vec<&'static Lint> {
2018-03-30 11:54:14 +02:00
// The sort doesn't case-fold but it's doubtful we care.
lints.sort_by_cached_key(|x: &&Lint| (x.default_level(sess.edition()), x.name));
lints
}
2019-12-22 23:42:04 +01:00
fn sort_lint_groups(
lints: Vec<(&'static str, Vec<LintId>, bool)>,
) -> Vec<(&'static str, Vec<LintId>)> {
let mut lints: Vec<_> = lints.into_iter().map(|(x, y, _)| (x, y)).collect();
2018-04-01 06:52:45 +02:00
lints.sort_by_key(|l| l.0);
lints
}
2019-12-22 23:42:04 +01:00
let (plugin, builtin): (Vec<_>, _) =
lint_store.get_lints().iter().cloned().partition(|&lint| lint.is_plugin);
let plugin = sort_lints(sess, plugin);
let builtin = sort_lints(sess, builtin);
2019-12-22 23:42:04 +01:00
let (plugin_groups, builtin_groups): (Vec<_>, _) =
lint_store.get_lint_groups().iter().cloned().partition(|&(.., p)| p);
let plugin_groups = sort_lint_groups(plugin_groups);
let builtin_groups = sort_lint_groups(builtin_groups);
2019-12-22 23:42:04 +01:00
let max_name_len =
plugin.iter().chain(&builtin).map(|&s| s.name.chars().count()).max().unwrap_or(0);
let padded = |x: &str| {
2018-04-01 06:48:15 +02:00
let mut s = " ".repeat(max_name_len - x.chars().count());
s.push_str(x);
s
};
println!("Lint checks provided by rustc:\n");
println!(" {} {:7.7} {}", padded("name"), "default", "meaning");
println!(" {} {:7.7} {}", padded("----"), "-------", "-------");
let print_lints = |lints: Vec<&Lint>| {
for lint in lints {
let name = lint.name_lower().replace("_", "-");
2019-12-22 23:42:04 +01:00
println!(" {} {:7.7} {}", padded(&name), lint.default_level.as_str(), lint.desc);
}
println!("\n");
};
print_lints(builtin);
2019-12-22 23:42:04 +01:00
let max_name_len = max(
"warnings".len(),
plugin_groups
.iter()
.chain(&builtin_groups)
.map(|&(s, _)| s.chars().count())
.max()
.unwrap_or(0),
);
let padded = |x: &str| {
2018-04-01 06:48:15 +02:00
let mut s = " ".repeat(max_name_len - x.chars().count());
s.push_str(x);
s
};
println!("Lint groups provided by rustc:\n");
println!(" {} {}", padded("name"), "sub-lints");
println!(" {} {}", padded("----"), "---------");
println!(" {} {}", padded("warnings"), "all lints that are set to issue warnings");
let print_lint_groups = |lints: Vec<(&'static str, Vec<LintId>)>| {
for (name, to) in lints {
let name = name.to_lowercase().replace("_", "-");
2019-12-22 23:42:04 +01:00
let desc = to
.into_iter()
.map(|x| x.to_string().replace("_", "-"))
.collect::<Vec<String>>()
.join(", ");
println!(" {} {}", padded(&name), desc);
2014-06-19 02:26:14 +02:00
}
println!("\n");
};
print_lint_groups(builtin_groups);
match (loaded_plugins, plugin.len(), plugin_groups.len()) {
(false, 0, _) | (false, _, 0) => {
2019-12-22 23:42:04 +01:00
println!(
"Compiler plugins can provide additional lints and lint groups. To see a \
listing of these, re-run `rustc -W help` with a crate filename."
);
}
2016-08-26 18:23:42 +02:00
(false, ..) => panic!("didn't load lint plugins but got them anyway!"),
(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);
}
2014-06-19 02:26:14 +02:00
}
}
}
fn describe_debug_flags() {
println!("\nAvailable options:\n");
print_flag_list("-Z", config::DB_OPTIONS);
}
fn describe_codegen_flags() {
println!("\nAvailable codegen options:\n");
print_flag_list("-C", config::CG_OPTIONS);
}
2019-12-22 23:42:04 +01:00
fn print_flag_list<T>(
cmdline_opt: &str,
flag_list: &[(&'static str, T, Option<&'static str>, &'static str)],
) {
let max_len = flag_list
.iter()
.map(|&(name, _, opt_type_desc, _)| {
let extra_len = match opt_type_desc {
Some(..) => 4,
None => 0,
};
name.chars().count() + extra_len
})
.max()
.unwrap_or(0);
for &(name, _, opt_type_desc, desc) in flag_list {
let (width, extra) = match opt_type_desc {
Some(..) => (max_len - 4, "=val"),
2015-11-10 21:48:44 +01:00
None => (max_len, ""),
};
2019-12-22 23:42:04 +01:00
println!(
" {} {:>width$}{} -- {}",
cmdline_opt,
name.replace("_", "-"),
extra,
desc,
width = width
);
}
}
/// Process command line options. Emits messages as appropriate. If compilation
/// should continue, returns a getopts::Matches object parsed from args,
2019-02-08 14:53:55 +01:00
/// otherwise returns `None`.
///
2017-01-22 08:45:06 +01:00
/// The compiler's handling of options is a little complicated as it ties into
/// our stability story. The current intention of each compiler option is to
/// have one of two modes:
///
/// 1. An option is stable and can be used everywhere.
/// 2. An option is unstable, and can only be used on nightly.
///
/// 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.
pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
// Throw away the first argument, the name of the binary
let args = &args[1..];
if args.is_empty() {
// user did not write `-v` nor `-Z unstable-options`, so do not
// include that extra information.
usage(false, false);
return None;
}
// 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.
let mut options = getopts::Options::new();
for option in config::rustc_optgroups() {
(option.apply)(&mut options);
}
2019-12-22 23:42:04 +01:00
let matches = options
.parse(args)
.unwrap_or_else(|f| early_error(ErrorOutputType::default(), &f.to_string()));
// 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.
// * Otherwise, if we're an unstable option then we generate an error
// (unstable option being used on stable)
nightly_options::check_nightly_options(&matches, &config::rustc_optgroups());
if matches.opt_present("h") || matches.opt_present("help") {
// Only show unstable options in --help if we accept unstable options.
usage(matches.opt_present("verbose"), nightly_options::is_unstable_enabled(&matches));
return None;
}
// Handle the special case of -Wall.
let wall = matches.opt_strs("W");
if wall.iter().any(|x| *x == "all") {
print_wall_help();
return None;
}
// Don't handle -W help here, because we might first load plugins.
let r = matches.opt_strs("Z");
if r.iter().any(|x| *x == "help") {
describe_debug_flags();
return None;
}
let cg_flags = matches.opt_strs("C");
if cg_flags.iter().any(|x| *x == "help") {
describe_codegen_flags();
return None;
}
if cg_flags.iter().any(|x| *x == "no-stack-check") {
2019-12-22 23:42:04 +01:00
early_warn(
ErrorOutputType::default(),
"the --no-stack-check flag is deprecated and does nothing",
);
}
if cg_flags.iter().any(|x| *x == "passes=list") {
rustc: Link LLVM directly into rustc again This commit builds on #65501 continue to simplify the build system and compiler now that we no longer have multiple LLVM backends to ship by default. Here this switches the compiler back to what it once was long long ago, which is linking LLVM directly to the compiler rather than dynamically loading it at runtime. The `codegen-backends` directory of the sysroot no longer exists and all relevant support in the build system is removed. Note that `rustc` still supports a dynamically loaded codegen backend as it did previously, it just no longer supports dynamically loaded codegen backends in its own sysroot. Additionally as part of this the `librustc_codegen_llvm` crate now once again explicitly depends on all of its crates instead of implicitly loading them through the sysroot. This involved filling out its `Cargo.toml` and deleting all the now-unnecessary `extern crate` annotations in the header of the crate. (this in turn required adding a number of imports for names of macros too). The end results of this change are: * Rustbuild's build process for the compiler as all the "oh don't forget the codegen backend" checks can be easily removed. * Building `rustc_codegen_llvm` is much simpler since it's simply another compiler crate. * Managing the dependencies of `rustc_codegen_llvm` is much simpler since it's "just another `Cargo.toml` to edit" * The build process should be a smidge faster because there's more parallelism in the main rustc build step rather than splitting `librustc_codegen_llvm` out to its own step. * The compiler is expected to be slightly faster by default because the codegen backend does not need to be dynamically loaded. * Disabling LLVM as part of rustbuild is still supported, supporting multiple codegen backends is still supported, and dynamic loading of a codegen backend is still supported.
2019-10-22 17:51:35 +02:00
get_builtin_codegen_backend("llvm")().print_passes();
return None;
}
if matches.opt_present("version") {
rustc: Start "stabilizing" some flags This commit shuffles around some CLI flags of the compiler to some more stable locations with some renamings. The changes made were: * The `-v` flag has been repurposes as the "verbose" flag. The version flag has been renamed to `-V`. * The `-h` screen has been split into two parts. Most top-level options (not all) show with `-h`, and the remaining options (generally obscure) can be shown with `--help -v` which is a "verbose help screen" * The `-V` flag (version flag now) has lost its argument as it is now requested with `rustc -vV` "verbose version". * The `--emit` option has had its `ir` and `bc` variants renamed to `llvm-ir` and `llvm-bc` to emphasize that they are LLVM's IR/bytecode. * The `--emit` option has grown a new variant, `dep-info`, which subsumes the `--dep-info` CLI argument. The `--dep-info` flag is now deprecated. * The `--parse-only`, `--no-trans`, and `--no-analysis` flags have moved behind the `-Z` family of flags. * The `--debuginfo` and `--opt-level` flags were moved behind the top-level `-C` flag. * The `--print-file-name` and `--print-crate-name` flags were moved behind one global `--print` flag which now accepts one of `crate-name`, `file-names`, or `sysroot`. This global `--print` flag is intended to serve as a mechanism for learning various metadata about the compiler itself. No warnings are currently enabled to allow tools like Cargo to have time to migrate to the new flags before spraying warnings to all users.
2014-12-16 01:03:39 +01:00
version("rustc", &matches);
return None;
}
Some(matches)
}
fn parse_crate_attrs<'a>(sess: &'a Session, input: &Input) -> PResult<'a, Vec<ast::Attribute>> {
match input {
2019-12-22 23:42:04 +01:00
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,
),
}
}
2019-02-08 14:53:55 +01:00
/// Gets a list of extra command-line flags provided by the user, as strings.
///
/// 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)> {
let args = env::args_os().map(|arg| arg.to_string_lossy().to_string()).collect::<Vec<_>>();
// 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;
}
let matches = handle_options(&args)?;
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
2019-12-22 23:42:04 +01:00
let name = if let Some(first) = content.split('=').next() { first } else { &content };
2019-12-22 23:42:04 +01:00
let content =
if ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE.contains(&name) { name } else { content };
if !ICE_REPORT_COMPILER_FLAGS_EXCLUDE.contains(&name) {
result.push(format!("{}{} {}", prefix, flag, content));
} else {
excluded_cargo_defaults = true;
}
}
}
2019-12-22 23:42:04 +01:00
if !result.is_empty() { Some((result, excluded_cargo_defaults)) } else { None }
}
/// Runs a closure and catches unwinds triggered by fatal errors.
///
/// The compiler currently unwinds with a special sentinel value to abort
/// 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> {
catch_unwind(panic::AssertUnwindSafe(f)).map_err(|value| {
if value.is::<rustc_errors::FatalErrorMarker>() {
ErrorReported
} else {
panic::resume_unwind(value);
}
})
}
lazy_static! {
static ref DEFAULT_HOOK: Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static> = {
let hook = panic::take_hook();
panic::set_hook(Box::new(|info| report_ice(info, BUG_REPORT_URL)));
hook
};
}
/// 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) {
// Invoke the default handler, which prints the actual panic message and optionally a backtrace
(*DEFAULT_HOOK)(info);
2019-07-27 16:38:52 +02:00
// Separate the output with an empty line
eprintln!();
let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
rustc_errors::ColorConfig::Auto,
None,
false,
false,
None,
false,
));
let handler = rustc_errors::Handler::with_emitter(true, None, emitter);
// a .span_bug or .bug call has already printed what
// it wants to print.
if !info.payload().is::<rustc_errors::ExplicitBug>() {
let d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
2019-09-07 17:21:17 +02:00
handler.emit_diagnostic(&d);
}
let mut xs: Vec<Cow<'static, str>> = vec![
"the compiler unexpectedly panicked. this is a bug.".into(),
format!("we would appreciate a bug report: {}", bug_report_url).into(),
2019-12-22 23:42:04 +01:00
format!(
"rustc {} running on {}",
option_env!("CFG_VERSION").unwrap_or("unknown_version"),
config::host_triple()
)
.into(),
];
if let Some((flags, excluded_cargo_defaults)) = extra_compiler_flags() {
xs.push(format!("compiler flags: {}", flags.join(" ")).into());
if excluded_cargo_defaults {
xs.push("some of the compiler flags provided by cargo are hidden".into());
}
}
for note in &xs {
2019-09-07 17:21:17 +02:00
handler.note_without_error(&note);
}
// If backtraces are enabled, also print the query stack
let backtrace = env::var_os("RUST_BACKTRACE").map(|x| &x != "0").unwrap_or(false);
if backtrace {
TyCtxt::try_print_query_stack(&handler);
}
#[cfg(windows)]
unsafe {
if env::var("RUSTC_BREAK_ON_ICE").is_ok() {
// Trigger a debugger if we crashed during bootstrap
winapi::um::debugapi::DebugBreak();
}
}
}
/// 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.
pub fn install_ice_hook() {
lazy_static::initialize(&DEFAULT_HOOK);
}
/// This allows tools to enable rust logging without having to magically match rustc's
/// log crate version
pub fn init_rustc_env_logger() {
2019-04-30 10:27:53 +02:00
env_logger::init_from_env("RUSTC_LOG");
}
pub fn main() {
let start = Instant::now();
init_rustc_env_logger();
let mut callbacks = TimePassesCallbacks::default();
install_ice_hook();
let result = catch_fatal_errors(|| {
2019-12-22 23:42:04 +01:00
let args = env::args_os()
.enumerate()
.map(|(i, arg)| {
arg.into_string().unwrap_or_else(|arg| {
early_error(
ErrorOutputType::default(),
&format!("Argument {} is not valid Unicode: {:?}", i, arg),
)
})
})
2018-01-22 18:18:40 +01:00
.collect::<Vec<_>>();
run_compiler(&args, &mut callbacks, None, None)
2019-12-22 23:42:04 +01:00
})
.and_then(|result| result);
let exit_code = match result {
Ok(_) => EXIT_SUCCESS,
Err(_) => EXIT_FAILURE,
};
// The extra `\t` is necessary to align this label with the others.
print_time_passes_entry(callbacks.time_passes, "\ttotal", start.elapsed());
process::exit(exit_code);
}