rust/compiler/rustc_driver_impl/src/lib.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1614 lines
60 KiB
Rust
Raw Normal View History

//! The Rust compiler.
//!
//! # Note
//!
//! This API is completely unstable and subject to change.
// tidy-alphabetical-start
#![allow(internal_features)]
#![allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
2020-09-23 19:51:56 +00:00
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
2023-11-13 12:39:17 +00:00
#![doc(rust_logo)]
#![feature(decl_macro)]
#![feature(let_chains)]
#![feature(panic_backtrace_config)]
#![feature(panic_update_hook)]
#![feature(result_flattening)]
#![feature(rustdoc_internals)]
#![warn(unreachable_pub)]
// tidy-alphabetical-end
2018-03-03 05:19:15 +00:00
use std::cmp::max;
use std::collections::BTreeMap;
use std::ffi::OsString;
use std::fmt::Write as _;
2023-09-07 04:16:06 +00:00
use std::fs::{self, File};
2022-10-16 13:55:18 +00:00
use std::io::{self, IsTerminal, Read, Write};
use std::panic::{self, catch_unwind, PanicHookInfo};
use std::path::PathBuf;
use std::process::{self, Command, Stdio};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, OnceLock};
use std::time::{Duration, Instant, SystemTime};
use std::{env, str};
2020-04-27 17:56:11 +00:00
use rustc_ast as ast;
use rustc_codegen_ssa::traits::CodegenBackend;
use rustc_codegen_ssa::{CodegenErrors, CodegenResults};
use rustc_const_eval::CTRL_C_RECEIVED;
use rustc_data_structures::profiling::{
get_resident_set_size, print_time_passes_entry, TimePassesFormat,
};
use rustc_errors::emitter::stderr_destination;
use rustc_errors::registry::Registry;
use rustc_errors::{
markdown, ColorConfig, DiagCtxt, ErrCode, ErrorGuaranteed, FatalError, PResult,
};
2020-10-10 18:27:52 +00:00
use rustc_feature::find_gated_cfg;
use rustc_interface::util::{self, get_codegen_backend};
use rustc_interface::{interface, passes, Linker, Queries};
use rustc_lint::unerased_lint_store;
use rustc_metadata::creader::MetadataLoader;
2016-10-20 04:31:14 +00:00
use rustc_metadata::locator;
use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal};
use rustc_session::config::{
nightly_options, ErrorOutputType, Input, OutFileName, OutputType, UnstableOptions, CG_OPTIONS,
Z_OPTIONS,
};
use rustc_session::getopts::{self, Matches};
use rustc_session::lint::{Lint, LintId};
use rustc_session::output::collect_crate_types;
use rustc_session::{config, filesearch, EarlyDiagCtxt, Session};
use rustc_span::source_map::FileLoader;
use rustc_span::FileName;
2021-06-03 15:45:09 +00:00
use rustc_target::json::ToJson;
use rustc_target::spec::{Target, TargetTriple};
use time::OffsetDateTime;
use tracing::trace;
2023-06-07 18:59:31 +00:00
#[allow(unused_macros)]
macro do_not_use_print($($t:tt)*) {
std::compile_error!(
"Don't use `print` or `println` here, use `safe_print` or `safe_println` instead"
)
}
#[allow(unused_macros)]
macro do_not_use_safe_print($($t:tt)*) {
std::compile_error!("Don't use `safe_print` or `safe_println` here, use `println_info` instead")
}
2023-04-25 00:04:26 +00:00
// This import blocks the use of panicking `print` and `println` in all the code
// below. Please use `safe_print` and `safe_println` to avoid ICE when
// encountering an I/O error during print.
#[allow(unused_imports)]
2023-06-07 18:59:31 +00:00
use {do_not_use_print as print, do_not_use_print as println};
2023-04-25 00:04:26 +00:00
pub mod args;
pub mod pretty;
2023-04-25 00:04:26 +00:00
#[macro_use]
mod print;
mod session_diagnostics;
#[cfg(all(unix, any(target_env = "gnu", target_os = "macos")))]
mod signal_handler;
#[cfg(not(all(unix, any(target_env = "gnu", target_os = "macos"))))]
mod signal_handler {
/// On platforms which don't support our signal handler's requirements,
/// simply use the default signal handler provided by std.
pub(super) fn install() {}
}
use crate::session_diagnostics::{
RLinkEmptyVersionNumber, RLinkEncodingVersionMismatch, RLinkRustcVersionMismatch,
RLinkWrongFileType, RlinkCorruptFile, RlinkNotAFile, RlinkUnableToRead,
};
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[
// tidy-alphabetical-start
crate::DEFAULT_LOCALE_RESOURCE,
rustc_ast_lowering::DEFAULT_LOCALE_RESOURCE,
rustc_ast_passes::DEFAULT_LOCALE_RESOURCE,
rustc_attr::DEFAULT_LOCALE_RESOURCE,
rustc_borrowck::DEFAULT_LOCALE_RESOURCE,
rustc_builtin_macros::DEFAULT_LOCALE_RESOURCE,
rustc_codegen_ssa::DEFAULT_LOCALE_RESOURCE,
rustc_const_eval::DEFAULT_LOCALE_RESOURCE,
rustc_errors::DEFAULT_LOCALE_RESOURCE,
rustc_expand::DEFAULT_LOCALE_RESOURCE,
rustc_hir_analysis::DEFAULT_LOCALE_RESOURCE,
rustc_hir_typeck::DEFAULT_LOCALE_RESOURCE,
rustc_incremental::DEFAULT_LOCALE_RESOURCE,
rustc_infer::DEFAULT_LOCALE_RESOURCE,
rustc_interface::DEFAULT_LOCALE_RESOURCE,
rustc_lint::DEFAULT_LOCALE_RESOURCE,
rustc_metadata::DEFAULT_LOCALE_RESOURCE,
rustc_middle::DEFAULT_LOCALE_RESOURCE,
rustc_mir_build::DEFAULT_LOCALE_RESOURCE,
rustc_mir_dataflow::DEFAULT_LOCALE_RESOURCE,
rustc_mir_transform::DEFAULT_LOCALE_RESOURCE,
rustc_monomorphize::DEFAULT_LOCALE_RESOURCE,
rustc_parse::DEFAULT_LOCALE_RESOURCE,
rustc_passes::DEFAULT_LOCALE_RESOURCE,
rustc_pattern_analysis::DEFAULT_LOCALE_RESOURCE,
rustc_privacy::DEFAULT_LOCALE_RESOURCE,
rustc_query_system::DEFAULT_LOCALE_RESOURCE,
rustc_resolve::DEFAULT_LOCALE_RESOURCE,
rustc_session::DEFAULT_LOCALE_RESOURCE,
rustc_trait_selection::DEFAULT_LOCALE_RESOURCE,
rustc_ty_utils::DEFAULT_LOCALE_RESOURCE,
// tidy-alphabetical-end
];
/// 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;
pub const DEFAULT_BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust/issues/new\
2020-07-04 16:41:21 +00:00
?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md";
pub trait Callbacks {
/// Called before creating the compiler instance
fn config(&mut self, _config: &mut interface::Config) {}
/// Called after parsing the crate root. Submodules are not yet parsed when
/// this callback is called. Return value instructs the compiler whether to
/// continue the compilation afterwards (defaults to `Compilation::Continue`)
fn after_crate_root_parsing<'tcx>(
2019-11-30 09:16:19 +00:00
&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 09:16:19 +00:00
fn after_expansion<'tcx>(
&mut self,
_compiler: &interface::Compiler,
_queries: &'tcx Queries<'tcx>,
) -> Compilation {
Compilation::Continue
2019-07-14 18:24:48 +00:00
}
/// Called after analysis. Return value instructs the compiler whether to
/// continue the compilation afterwards (defaults to `Compilation::Continue`)
2019-11-30 09:16:19 +00:00
fn after_analysis<'tcx>(
&mut self,
_compiler: &interface::Compiler,
_queries: &'tcx Queries<'tcx>,
) -> Compilation {
Compilation::Continue
}
}
#[derive(Default)]
pub struct TimePassesCallbacks {
time_passes: Option<TimePassesFormat>,
}
impl Callbacks for TimePassesCallbacks {
Remove `-Ztime` option. The compiler currently has `-Ztime` and `-Ztime-passes`. I've used `-Ztime-passes` for years but only recently learned about `-Ztime`. What's the difference? Let's look at the `-Zhelp` output: ``` -Z time=val -- measure time of rustc processes (default: no) -Z time-passes=val -- measure time of each rustc pass (default: no) ``` The `-Ztime-passes` description is clear, but the `-Ztime` one is less so. Sounds like it measures the time for the entire process? No. The real difference is that `-Ztime-passes` prints out info about passes, and `-Ztime` does the same, but only for a subset of those passes. More specifically, there is a distinction in the profiling code between a "verbose generic activity" and an "extra verbose generic activity". `-Ztime-passes` prints both kinds, while `-Ztime` only prints the first one. (It took me a close reading of the source code to determine this difference.) In practice this distinction has low value. Perhaps in the past the "extra verbose" output was more voluminous, but now that we only print stats for a pass if it exceeds 5ms or alters the RSS, `-Ztime-passes` is less spammy. Also, a lot of the "extra verbose" cases are for individual lint passes, and you need to also use `-Zno-interleave-lints` to see those anyway. Therefore, this commit removes `-Ztime` and the associated machinery. One thing to note is that the existing "extra verbose" activities all have an extra string argument, so the commit adds the ability to accept an extra argument to the "verbose" activities.
2022-10-06 03:51:45 +00:00
// JUSTIFICATION: the session doesn't exist at this point.
#[allow(rustc::bad_opt_access)]
fn config(&mut self, config: &mut interface::Config) {
// If a --print=... option has been given, we don't print the "total"
// time because it will mess up the --print output. See #64339.
Remove `-Ztime` option. The compiler currently has `-Ztime` and `-Ztime-passes`. I've used `-Ztime-passes` for years but only recently learned about `-Ztime`. What's the difference? Let's look at the `-Zhelp` output: ``` -Z time=val -- measure time of rustc processes (default: no) -Z time-passes=val -- measure time of each rustc pass (default: no) ``` The `-Ztime-passes` description is clear, but the `-Ztime` one is less so. Sounds like it measures the time for the entire process? No. The real difference is that `-Ztime-passes` prints out info about passes, and `-Ztime` does the same, but only for a subset of those passes. More specifically, there is a distinction in the profiling code between a "verbose generic activity" and an "extra verbose generic activity". `-Ztime-passes` prints both kinds, while `-Ztime` only prints the first one. (It took me a close reading of the source code to determine this difference.) In practice this distinction has low value. Perhaps in the past the "extra verbose" output was more voluminous, but now that we only print stats for a pass if it exceeds 5ms or alters the RSS, `-Ztime-passes` is less spammy. Also, a lot of the "extra verbose" cases are for individual lint passes, and you need to also use `-Zno-interleave-lints` to see those anyway. Therefore, this commit removes `-Ztime` and the associated machinery. One thing to note is that the existing "extra verbose" activities all have an extra string argument, so the commit adds the ability to accept an extra argument to the "verbose" activities.
2022-10-06 03:51:45 +00:00
//
self.time_passes = (config.opts.prints.is_empty() && config.opts.unstable_opts.time_passes)
.then(|| config.opts.unstable_opts.time_passes_format);
config.opts.trimmed_def_paths = true;
}
}
2019-11-15 18:41:50 +00:00
pub fn diagnostics_registry() -> Registry {
Registry::new(rustc_errors::codes::DIAGNOSTICS)
2019-11-15 18:41:50 +00:00
}
2021-03-28 16:48:52 +00:00
/// This is the primary entry point for rustc.
pub struct RunCompiler<'a> {
at_args: &'a [String],
callbacks: &'a mut (dyn Callbacks + Send),
file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
make_codegen_backend:
Option<Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>>,
using_internal_features: Arc<std::sync::atomic::AtomicBool>,
}
impl<'a> RunCompiler<'a> {
pub fn new(at_args: &'a [String], callbacks: &'a mut (dyn Callbacks + Send)) -> Self {
Self {
at_args,
callbacks,
file_loader: None,
make_codegen_backend: None,
using_internal_features: Arc::default(),
}
}
2021-03-28 16:48:52 +00:00
/// Set a custom codegen backend.
///
/// Has no uses within this repository, but is used by bjorn3 for "the
/// hotswapping branch of cg_clif" for "setting the codegen backend from a
/// custom driver where the custom codegen backend has arbitrary data."
/// (See #102759.)
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
}
2021-03-28 16:48:52 +00:00
/// Load files from sources other than the file system.
///
/// Has no uses within this repository, but may be used in the future by
/// bjorn3 for "hooking rust-analyzer's VFS into rustc at some point for
/// running rustc without having to save". (See #102759.)
pub fn set_file_loader(
&mut self,
file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
) -> &mut Self {
self.file_loader = file_loader;
self
}
2021-03-28 16:48:52 +00:00
/// Set the session-global flag that checks whether internal features have been used,
/// suppressing the message about submitting an issue in ICEs when enabled.
#[must_use]
pub fn set_using_internal_features(mut self, using_internal_features: Arc<AtomicBool>) -> Self {
self.using_internal_features = using_internal_features;
self
}
2021-03-28 16:48:52 +00:00
/// Parse args and run the compiler.
pub fn run(self) -> interface::Result<()> {
run_compiler(
self.at_args,
self.callbacks,
self.file_loader,
self.make_codegen_backend,
self.using_internal_features,
)
}
}
fn run_compiler(
at_args: &[String],
callbacks: &mut (dyn Callbacks + Send),
file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
make_codegen_backend: Option<
Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>,
>,
using_internal_features: Arc<std::sync::atomic::AtomicBool>,
) -> interface::Result<()> {
let mut default_early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());
// Throw away the first argument, the name of the binary.
// In case of at_args being empty, as might be the case by
// passing empty argument array to execve under some platforms,
// just use an empty slice.
//
// This situation was possible before due to arg_expand_all being
// called before removing the argument, enabling a crash by calling
// the compiler with @empty_file as argv[0] and no more arguments.
let at_args = at_args.get(1..).unwrap_or_default();
let args = args::arg_expand_all(&default_early_dcx, at_args)?;
let Some(matches) = handle_options(&default_early_dcx, &args) else { return Ok(()) };
2018-04-25 22:49:52 +00:00
let sopts = config::build_session_options(&mut default_early_dcx, &matches);
// fully initialize ice path static once unstable options are available as context
let ice_file = ice_path_with_config(Some(&sopts.unstable_opts)).clone();
if let Some(ref code) = matches.opt_str("explain") {
handle_explain(&default_early_dcx, diagnostics_registry(), code, sopts.color);
return Ok(());
}
let (odir, ofile) = make_output(&matches);
2021-03-28 16:36:53 +00:00
let mut config = interface::Config {
opts: sopts,
crate_cfg: matches.opt_strs("cfg"),
crate_check_cfg: matches.opt_strs("check-cfg"),
2021-03-28 16:36:53 +00:00
input: Input::File(PathBuf::new()),
output_file: ofile,
output_dir: odir,
ice_file,
2021-03-28 16:36:53 +00:00
file_loader,
locale_resources: DEFAULT_LOCALE_RESOURCES,
2021-03-28 16:36:53 +00:00
lint_caps: Default::default(),
psess_created: None,
hash_untracked_state: None,
2021-03-28 16:36:53 +00:00
register_lints: None,
override_queries: None,
make_codegen_backend,
registry: diagnostics_registry(),
using_internal_features,
expanded_args: args,
2021-03-28 16:36:53 +00:00
};
let has_input = match make_input(&default_early_dcx, &matches.free) {
Err(reported) => return Err(reported),
Ok(Some(input)) => {
2021-03-28 16:36:53 +00:00
config.input = input;
true // has input: normal compilation
2021-03-28 16:36:53 +00:00
}
Ok(None) => match matches.free.as_slice() {
[] => false, // no input: we will exit early
[_] => panic!("make_input should have provided valid inputs"),
[fst, snd, ..] => default_early_dcx.early_fatal(format!(
"multiple input filenames provided (first two filenames are `{fst}` and `{snd}`)"
)),
},
};
drop(default_early_dcx);
callbacks.config(&mut config);
interface::run_compiler(config, |compiler| {
let sess = &compiler.sess;
let codegen_backend = &*compiler.codegen_backend;
// This is used for early exits unrelated to errors. E.g. when just
// printing some information without compiling, or exiting immediately
// after parsing, etc.
let early_exit = || {
if let Some(guar) = sess.dcx().has_errors() { Err(guar) } else { Ok(()) }
};
// This implements `-Whelp`. It should be handled very early, like
// `--help`/`-Zhelp`/`-Chelp`. This is the earliest it can run, because
// it must happen after lints are registered, during session creation.
if sess.opts.describe_lints {
describe_lints(sess);
return early_exit();
}
if print_crate_info(codegen_backend, sess, has_input) == Compilation::Stop {
return early_exit();
}
if !has_input {
#[allow(rustc::diagnostic_outside_of_impl)]
sess.dcx().fatal("no input filename given"); // this is fatal
}
if !sess.opts.unstable_opts.ls.is_empty() {
list_metadata(sess, &*codegen_backend.metadata_loader());
return early_exit();
}
if sess.opts.unstable_opts.link_only {
process_rlink(sess, compiler);
return early_exit();
}
let linker = compiler.enter(|queries| {
let early_exit = || early_exit().map(|_| None);
2024-08-27 18:04:47 +00:00
// Parse the crate root source code (doesn't parse submodules yet)
// Everything else is parsed during macro expansion.
queries.parse()?;
2024-08-27 18:04:47 +00:00
// If pretty printing is requested: Figure out the representation, print it and exit
if let Some(pp_mode) = sess.opts.pretty {
if pp_mode.needs_ast_map() {
queries.global_ctxt()?.enter(|tcx| {
2023-03-06 10:56:53 +00:00
tcx.ensure().early_lint_checks(());
2024-08-27 18:04:47 +00:00
pretty::print(sess, pp_mode, pretty::PrintExtra::NeedsAstMap { tcx });
Ok(())
})?;
2024-06-22 09:14:26 +00:00
queries.global_ctxt()?.enter(|tcx| {
passes::write_dep_info(tcx);
});
} else {
let krate = queries.parse()?;
pretty::print(
sess,
2024-08-27 18:04:47 +00:00
pp_mode,
pretty::PrintExtra::AfterParsing { krate: &*krate.borrow() },
);
}
trace!("finished pretty-printing");
2019-11-25 16:29:32 +00:00
return early_exit();
}
if callbacks.after_crate_root_parsing(compiler, queries) == Compilation::Stop {
2019-11-25 16:29:32 +00:00
return early_exit();
}
if sess.opts.unstable_opts.parse_only || sess.opts.unstable_opts.show_span.is_some() {
2019-11-25 16:29:32 +00:00
return early_exit();
}
// Make sure name resolution and macro expansion is run.
queries.global_ctxt()?.enter(|tcx| tcx.resolver_for_lowering());
2019-11-30 09:16:19 +00:00
if callbacks.after_expansion(compiler, queries) == Compilation::Stop {
2019-11-25 16:29:32 +00:00
return early_exit();
}
2024-06-22 09:14:26 +00:00
queries.global_ctxt()?.enter(|tcx| {
passes::write_dep_info(tcx);
});
2023-01-23 10:25:51 +00:00
if sess.opts.output_types.contains_key(&OutputType::DepInfo)
&& sess.opts.output_types.len() == 1
{
2019-11-25 16:29:32 +00:00
return early_exit();
}
2019-07-14 18:24:48 +00:00
if sess.opts.unstable_opts.no_analysis {
2019-11-25 16:29:32 +00:00
return early_exit();
}
queries.global_ctxt()?.enter(|tcx| tcx.analysis(()))?;
if callbacks.after_analysis(compiler, queries) == Compilation::Stop {
2019-11-25 16:29:32 +00:00
return early_exit();
}
queries.global_ctxt()?.enter(|tcx| {
Ok(Some(Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend)?))
})
})?;
2023-11-20 03:13:52 +00:00
// Linking is done outside the `compiler.enter()` so that the
// `GlobalCtxt` within `Queries` can be freed as early as possible.
if let Some(linker) = linker {
2020-01-09 02:48:00 +00:00
let _timer = sess.timer("link");
linker.link(sess, codegen_backend)?
}
if let Some(fuel) = sess.opts.unstable_opts.print_fuel.as_deref() {
eprintln!("Fuel used by {}: {}", fuel, sess.print_fuel.load(Ordering::SeqCst));
}
Ok(())
})
}
// Extract output directory and file from matches.
fn make_output(matches: &getopts::Matches) -> (Option<PathBuf>, Option<OutFileName>) {
let odir = matches.opt_str("out-dir").map(|o| PathBuf::from(&o));
let ofile = matches.opt_str("o").map(|o| match o.as_str() {
"-" => OutFileName::Stdout,
path => OutFileName::Real(PathBuf::from(path)),
});
(odir, ofile)
}
2024-08-27 16:46:44 +00:00
/// Extract input (string or file and optional path) from matches.
/// This handles reading from stdin if `-` is provided.
2021-05-02 15:57:04 +00:00
fn make_input(
2023-12-17 23:57:26 +00:00
early_dcx: &EarlyDiagCtxt,
2021-05-02 15:57:04 +00:00
free_matches: &[String],
) -> Result<Option<Input>, ErrorGuaranteed> {
2024-08-27 16:46:44 +00:00
let [input_file] = free_matches else { return Ok(None) };
if input_file != "-" {
// Normal `Input::File`
return Ok(Some(Input::File(PathBuf::from(input_file))));
}
// read from stdin as `Input::Str`
let mut input = String::new();
if io::stdin().read_to_string(&mut input).is_err() {
// Immediately stop compilation if there was an issue reading
// the input (for example if the input stream is not UTF-8).
let reported =
early_dcx.early_err("couldn't read from stdin, as it did not contain valid UTF-8");
return Err(reported);
}
let name = match env::var("UNSTABLE_RUSTDOC_TEST_PATH") {
Ok(path) => {
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");
2024-08-27 16:46:44 +00:00
FileName::doc_test_source_code(PathBuf::from(path), line)
}
2024-08-27 16:46:44 +00:00
Err(_) => FileName::anon_source_code(&input),
};
Ok(Some(Input::Str { name, input }))
}
/// Whether to stop or continue compilation.
2015-03-30 13:38:44 +00:00
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
2015-02-03 00:40:52 +00:00
pub enum Compilation {
Stop,
Continue,
}
2023-12-17 23:57:26 +00:00
fn handle_explain(early_dcx: &EarlyDiagCtxt, registry: Registry, code: &str, color: ColorConfig) {
// Allow "E0123" or "0123" form.
let upper_cased_code = code.to_ascii_uppercase();
let start = if upper_cased_code.starts_with('E') { 1 } else { 0 };
if let Ok(code) = upper_cased_code[start..].parse::<u32>()
&& let Ok(description) = registry.try_find_description(ErrCode::from_u32(code))
{
let mut is_in_code_block = false;
let mut text = String::new();
// Slice off the leading newline and print.
for line in description.lines() {
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;
2017-07-02 20:27:10 +00:00
} else {
text.push_str(line);
2017-07-02 20:27:10 +00:00
}
text.push('\n');
}
if io::stdout().is_terminal() {
show_md_content_with_pager(&text, color);
} else {
safe_print!("{text}");
}
} else {
early_dcx.early_fatal(format!("{code} is not a valid error code"));
}
}
/// If color is always or auto, print formatted & colorized markdown. If color is never or
/// if formatted printing fails, print the raw text.
///
/// Prefers a pager, falls back standard print
fn show_md_content_with_pager(content: &str, color: ColorConfig) {
let mut fallback_to_println = false;
let pager_name = env::var_os("PAGER").unwrap_or_else(|| {
2022-08-23 16:48:46 +00:00
if cfg!(windows) { OsString::from("more.com") } else { OsString::from("less") }
});
let mut cmd = Command::new(&pager_name);
// FIXME: find if other pagers accept color options
let mut print_formatted = if pager_name == "less" {
cmd.arg("-r");
true
} else {
["bat", "catbat", "delta"].iter().any(|v| *v == pager_name)
};
if color == ColorConfig::Never {
print_formatted = false;
} else if color == ColorConfig::Always {
print_formatted = true;
}
let mdstream = markdown::MdStream::parse_str(content);
let bufwtr = markdown::create_stdout_bufwtr();
let mut mdbuf = bufwtr.buffer();
if mdstream.write_termcolor_buf(&mut mdbuf).is_err() {
print_formatted = false;
}
if let Ok(mut pager) = cmd.stdin(Stdio::piped()).spawn() {
if let Some(pipe) = pager.stdin.as_mut() {
let res = if print_formatted {
pipe.write_all(mdbuf.as_slice())
} else {
pipe.write_all(content.as_bytes())
};
if res.is_err() {
fallback_to_println = true;
}
}
if pager.wait().is_err() {
fallback_to_println = true;
}
} else {
fallback_to_println = true;
}
// If pager fails for whatever reason, we should still print the content
// to standard output
if fallback_to_println {
let fmt_success = match color {
ColorConfig::Auto => io::stdout().is_terminal() && bufwtr.print(&mdbuf).is_ok(),
ColorConfig::Always => bufwtr.print(&mdbuf).is_ok(),
ColorConfig::Never => false,
};
if !fmt_success {
safe_print!("{content}");
}
}
}
fn process_rlink(sess: &Session, compiler: &interface::Compiler) {
assert!(sess.opts.unstable_opts.link_only);
let dcx = sess.dcx();
if let Input::File(file) = &sess.io.input {
let rlink_data = fs::read(file).unwrap_or_else(|err| {
dcx.emit_fatal(RlinkUnableToRead { err });
});
let (codegen_results, outputs) = match CodegenResults::deserialize_rlink(sess, rlink_data) {
Ok((codegen, outputs)) => (codegen, outputs),
Err(err) => {
match err {
CodegenErrors::WrongFileType => dcx.emit_fatal(RLinkWrongFileType),
CodegenErrors::EmptyVersionNumber => dcx.emit_fatal(RLinkEmptyVersionNumber),
CodegenErrors::EncodingVersionMismatch { version_array, rlink_version } => dcx
.emit_fatal(RLinkEncodingVersionMismatch { version_array, rlink_version }),
CodegenErrors::RustcVersionMismatch { rustc_version } => {
dcx.emit_fatal(RLinkRustcVersionMismatch {
rustc_version,
current_version: sess.cfg_version,
})
}
CodegenErrors::CorruptFile => {
2024-05-22 00:12:07 +00:00
dcx.emit_fatal(RlinkCorruptFile { file });
}
};
}
};
if compiler.codegen_backend.link(sess, codegen_results, &outputs).is_err() {
FatalError.raise();
}
} else {
dcx.emit_fatal(RlinkNotAFile {});
}
}
fn list_metadata(sess: &Session, metadata_loader: &dyn MetadataLoader) {
match sess.io.input {
Input::File(ref ifile) => {
let path = &(*ifile);
let mut v = Vec::new();
locator::list_file_metadata(
&sess.target,
path,
metadata_loader,
&mut v,
&sess.opts.unstable_opts.ls,
sess.cfg_version,
)
.unwrap();
safe_println!("{}", String::from_utf8(v).unwrap());
}
Input::Str { .. } => {
#[allow(rustc::diagnostic_outside_of_impl)]
sess.dcx().fatal("cannot list metadata for stdin");
}
}
}
fn print_crate_info(
codegen_backend: &dyn CodegenBackend,
sess: &Session,
parse_attrs: bool,
) -> Compilation {
use rustc_session::config::PrintKind::*;
// This import prevents the following code from using the printing macros
// used by the rest of the module. Within this function, we only write to
// the output specified by `sess.io.output_file`.
#[allow(unused_imports)]
use {do_not_use_safe_print as safe_print, do_not_use_safe_print as safe_println};
// NativeStaticLibs and LinkArgs are special - printed during linking
// (empty iterator returns true)
if sess.opts.prints.iter().all(|p| p.kind == NativeStaticLibs || p.kind == LinkArgs) {
return Compilation::Continue;
}
let attrs = if parse_attrs {
let result = parse_crate_attrs(sess);
match result {
Ok(attrs) => Some(attrs),
Make `DiagnosticBuilder::emit` consuming. This works for most of its call sites. This is nice, because `emit` very much makes sense as a consuming operation -- indeed, `DiagnosticBuilderState` exists to ensure no diagnostic is emitted twice, but it uses runtime checks. For the small number of call sites where a consuming emit doesn't work, the commit adds `DiagnosticBuilder::emit_without_consuming`. (This will be removed in subsequent commits.) Likewise, `emit_unless` becomes consuming. And `delay_as_bug` becomes consuming, while `delay_as_bug_without_consuming` is added (which will also be removed in subsequent commits.) All this requires significant changes to `DiagnosticBuilder`'s chaining methods. Currently `DiagnosticBuilder` method chaining uses a non-consuming `&mut self -> &mut Self` style, which allows chaining to be used when the chain ends in `emit()`, like so: ``` struct_err(msg).span(span).emit(); ``` But it doesn't work when producing a `DiagnosticBuilder` value, requiring this: ``` let mut err = self.struct_err(msg); err.span(span); err ``` This style of chaining won't work with consuming `emit` though. For that, we need to use to a `self -> Self` style. That also would allow `DiagnosticBuilder` production to be chained, e.g.: ``` self.struct_err(msg).span(span) ``` However, removing the `&mut self -> &mut Self` style would require that individual modifications of a `DiagnosticBuilder` go from this: ``` err.span(span); ``` to this: ``` err = err.span(span); ``` There are *many* such places. I have a high tolerance for tedious refactorings, but even I gave up after a long time trying to convert them all. Instead, this commit has it both ways: the existing `&mut self -> Self` chaining methods are kept, and new `self -> Self` chaining methods are added, all of which have a `_mv` suffix (short for "move"). Changes to the existing `forward!` macro lets this happen with very little additional boilerplate code. I chose to add the suffix to the new chaining methods rather than the existing ones, because the number of changes required is much smaller that way. This doubled chainging is a bit clumsy, but I think it is worthwhile because it allows a *lot* of good things to subsequently happen. In this commit, there are many `mut` qualifiers removed in places where diagnostics are emitted without being modified. In subsequent commits: - chaining can be used more, making the code more concise; - more use of chaining also permits the removal of redundant diagnostic APIs like `struct_err_with_code`, which can be replaced easily with `struct_err` + `code_mv`; - `emit_without_diagnostic` can be removed, which simplifies a lot of machinery, removing the need for `DiagnosticBuilderState`.
2024-01-03 01:17:35 +00:00
Err(parse_error) => {
parse_error.emit();
return Compilation::Stop;
2016-02-19 13:43:13 +00:00
}
}
} else {
None
};
for req in &sess.opts.prints {
let mut crate_info = String::new();
macro println_info($($arg:tt)*) {
crate_info.write_fmt(format_args!("{}\n", format_args!($($arg)*))).unwrap()
}
match req.kind {
TargetList => {
let mut targets = rustc_target::spec::TARGETS.to_vec();
targets.sort_unstable();
println_info!("{}", targets.join("\n"));
}
Sysroot => println_info!("{}", sess.sysroot.display()),
TargetLibdir => println_info!("{}", sess.target_tlib_path.dir.display()),
2021-06-03 15:45:09 +00:00
TargetSpec => {
println_info!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap());
2021-06-03 15:45:09 +00:00
}
AllTargetSpecs => {
let mut targets = BTreeMap::new();
for name in rustc_target::spec::TARGETS {
let triple = TargetTriple::from_triple(name);
let target = Target::expect_builtin(&triple);
targets.insert(name, target.to_json());
}
println_info!("{}", serde_json::to_string_pretty(&targets).unwrap());
}
FileNames => {
2023-02-10 08:01:50 +00:00
let Some(attrs) = attrs.as_ref() else {
// no crate attributes, print out an error and exit
return Compilation::Continue;
};
let t_outputs = rustc_interface::util::build_output_filenames(attrs, sess);
let id = rustc_session::output::find_crate_name(sess, attrs);
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_info!("{}", fname.as_path().file_name().unwrap().to_string_lossy());
}
}
CrateName => {
let Some(attrs) = attrs.as_ref() else {
// no crate attributes, print out an error and exit
return Compilation::Continue;
};
let id = rustc_session::output::find_crate_name(sess, attrs);
println_info!("{id}");
}
Cfg => {
let mut cfgs = sess
.psess
.config
.iter()
.filter_map(|&(name, value)| {
// On stable, exclude unstable flags.
if !sess.is_nightly_build()
&& 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>>();
cfgs.sort();
for cfg in cfgs {
println_info!("{cfg}");
}
}
CheckCfg => {
let mut check_cfgs: Vec<String> = Vec::with_capacity(410);
// INSTABILITY: We are sorting the output below.
#[allow(rustc::potential_query_instability)]
for (name, expected_values) in &sess.psess.check_config.expecteds {
use crate::config::ExpectedValues;
match expected_values {
ExpectedValues::Any => check_cfgs.push(format!("{name}=any()")),
ExpectedValues::Some(values) => {
if !values.is_empty() {
check_cfgs.extend(values.iter().map(|value| {
if let Some(value) = value {
format!("{name}=\"{value}\"")
} else {
name.to_string()
}
}))
} else {
check_cfgs.push(format!("{name}="))
}
}
}
}
check_cfgs.sort_unstable();
if !sess.psess.check_config.exhaustive_names {
if !sess.psess.check_config.exhaustive_values {
println_info!("any()=any()");
} else {
println_info!("any()");
}
}
for check_cfg in check_cfgs {
println_info!("{check_cfg}");
}
}
CallingConventions => {
let mut calling_conventions = rustc_target::spec::abi::all_names();
calling_conventions.sort_unstable();
println_info!("{}", calling_conventions.join("\n"));
}
RelocationModels
| CodeModels
| TlsModels
| TargetCPUs
| StackProtectorStrategies
| TargetFeatures => {
codegen_backend.print(req, &mut crate_info, sess);
}
// Any output here interferes with Cargo's parsing of other printed output
NativeStaticLibs => {}
LinkArgs => {}
SplitDebuginfo => {
use rustc_target::spec::SplitDebuginfo::{Off, Packed, Unpacked};
for split in &[Off, Packed, Unpacked] {
if sess.target.options.supported_split_debuginfo.contains(split) {
println_info!("{split}");
}
}
}
DeploymentTarget => {
use rustc_target::spec::current_apple_deployment_target;
if sess.target.is_like_osx {
let (major, minor, patch) = current_apple_deployment_target(&sess.target);
let patch = if patch != 0 { format!(".{patch}") } else { String::new() };
println_info!("deployment_target={major}.{minor}{patch}")
} else {
#[allow(rustc::diagnostic_outside_of_impl)]
sess.dcx().fatal("only Apple targets currently support deployment version info")
}
}
}
req.out.overwrite(&crate_info, sess);
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 14:26:08 +00:00
}
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 14:26:08 +00:00
}
2015-06-26 17:32:27 +00:00
/// Prints version information
///
/// NOTE: this is a macro to support drivers built at a different time than the main `rustc_driver` crate.
pub macro version($early_dcx: expr, $binary: literal, $matches: expr) {
fn unw(x: Option<&str>) -> &str {
x.unwrap_or("unknown")
}
$crate::version_at_macro_invocation(
$early_dcx,
$binary,
$matches,
unw(option_env!("CFG_VERSION")),
unw(option_env!("CFG_VER_HASH")),
unw(option_env!("CFG_VER_DATE")),
unw(option_env!("CFG_RELEASE")),
)
}
#[doc(hidden)] // use the macro instead
pub fn version_at_macro_invocation(
2023-12-17 23:57:26 +00:00
early_dcx: &EarlyDiagCtxt,
binary: &str,
matches: &getopts::Matches,
version: &str,
commit_hash: &str,
commit_date: &str,
release: &str,
) {
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 00:03:39 +00:00
let verbose = matches.opt_present("verbose");
let mut version = version;
let mut release = release;
let tmp;
if let Ok(force_version) = std::env::var("RUSTC_OVERRIDE_VERSION_STRING") {
tmp = force_version;
version = &tmp;
release = &tmp;
}
2023-04-25 00:04:26 +00:00
safe_println!("{binary} {version}");
if verbose {
2023-04-25 00:04:26 +00:00
safe_println!("binary: {binary}");
safe_println!("commit-hash: {commit_hash}");
safe_println!("commit-date: {commit_date}");
safe_println!("host: {}", config::host_triple());
safe_println!("release: {release}");
let debug_flags = matches.opt_strs("Z");
2021-07-29 09:54:39 +00:00
let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend="));
let opts = config::Options::default();
let sysroot = filesearch::materialize_sysroot(opts.maybe_sysroot.clone());
let target = config::build_target_config(early_dcx, &opts, &sysroot);
get_codegen_backend(early_dcx, &sysroot, backend_name, &target).print_version();
}
}
2020-10-10 18:27:52 +00:00
fn usage(verbose: bool, include_unstable_options: bool, nightly_build: bool) {
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";
2020-10-10 18:27:52 +00:00
let nightly_help = if 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 00:03:39 +00:00
""
} else {
"\n --help -v Print the full set of options rustc accepts"
};
let at_path = if verbose {
" @path Read newline separated options from `path`\n"
} else {
""
};
2023-04-25 00:04:26 +00:00
safe_println!(
"{options}{at_path}\nAdditional help:
-C help Print codegen options
2015-11-10 20:48:44 +00:00
-W help \
Print 'lint' options and default settings{nightly}{verbose}\n",
options = options.usage(message),
at_path = at_path,
nightly = nightly_help,
verbose = verbose_help
);
}
fn print_wall_help() {
2023-04-25 00:04:26 +00:00
safe_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.
"
);
}
/// Write to stdout lint command options, together with a list of all available lints
pub fn describe_lints(sess: &Session) {
2023-04-25 00:04:26 +00:00
safe_println!(
"
Available lint options:
-W <foo> Warn about <foo>
-A <foo> Allow <foo>
-D <foo> Deny <foo>
-F <foo> Forbid <foo> (deny <foo> and all attempts to override)
"
);
fn sort_lints(sess: &Session, mut lints: Vec<&'static Lint>) -> Vec<&'static Lint> {
2018-03-30 09:54:14 +00: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
}
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 04:52:45 +00:00
lints.sort_by_key(|l| l.0);
lints
}
let lint_store = unerased_lint_store(sess);
let (loaded, builtin): (Vec<_>, _) =
lint_store.get_lints().iter().cloned().partition(|&lint| lint.is_externally_loaded);
let loaded = sort_lints(sess, loaded);
let builtin = sort_lints(sess, builtin);
let (loaded_groups, builtin_groups): (Vec<_>, _) =
2022-01-05 12:02:16 +00:00
lint_store.get_lint_groups().partition(|&(.., p)| p);
let loaded_groups = sort_lint_groups(loaded_groups);
let builtin_groups = sort_lint_groups(builtin_groups);
2015-11-10 20:48:44 +00:00
let max_name_len =
loaded.iter().chain(&builtin).map(|&s| s.name.chars().count()).max().unwrap_or(0);
let padded = |x: &str| {
2018-04-01 04:48:15 +00:00
let mut s = " ".repeat(max_name_len - x.chars().count());
s.push_str(x);
s
};
2023-04-25 00:04:26 +00:00
safe_println!("Lint checks provided by rustc:\n");
let print_lints = |lints: Vec<&Lint>| {
2023-04-25 00:04:26 +00:00
safe_println!(" {} {:7.7} {}", padded("name"), "default", "meaning");
safe_println!(" {} {:7.7} {}", padded("----"), "-------", "-------");
for lint in lints {
let name = lint.name_lower().replace('_', "-");
2023-04-25 00:04:26 +00:00
safe_println!(
" {} {:7.7} {}",
padded(&name),
lint.default_level(sess.edition()).as_str(),
lint.desc
);
}
2023-04-25 00:04:26 +00:00
safe_println!("\n");
};
print_lints(builtin);
2016-01-06 06:42:19 +00:00
let max_name_len = max(
"warnings".len(),
loaded_groups
2016-01-06 06:42:19 +00:00
.iter()
.chain(&builtin_groups)
.map(|&(s, _)| s.chars().count())
.max()
.unwrap_or(0),
);
let padded = |x: &str| {
2018-04-01 04:48:15 +00:00
let mut s = " ".repeat(max_name_len - x.chars().count());
s.push_str(x);
s
};
2023-04-25 00:04:26 +00:00
safe_println!("Lint groups provided by rustc:\n");
let print_lint_groups = |lints: Vec<(&'static str, Vec<LintId>)>, all_warnings| {
2023-04-25 00:04:26 +00:00
safe_println!(" {} sub-lints", padded("name"));
safe_println!(" {} ---------", padded("----"));
if all_warnings {
2023-04-25 00:04:26 +00:00
safe_println!(" {} all lints that are set to issue warnings", padded("warnings"));
}
for (name, to) in lints {
let name = name.to_lowercase().replace('_', "-");
2015-11-10 20:48:44 +00:00
let desc = to
.into_iter()
.map(|x| x.to_string().replace('_', "-"))
2015-11-10 20:48:44 +00:00
.collect::<Vec<String>>()
.join(", ");
2023-04-25 00:04:26 +00:00
safe_println!(" {} {}", padded(&name), desc);
2014-06-19 00:26:14 +00:00
}
2023-04-25 00:04:26 +00:00
safe_println!("\n");
};
print_lint_groups(builtin_groups, true);
match (sess.registered_lints, loaded.len(), loaded_groups.len()) {
(false, 0, _) | (false, _, 0) => {
safe_println!("Lint tools like Clippy can load additional lints and lint groups.");
}
(false, ..) => panic!("didn't load additional lints but got them anyway!"),
(true, 0, 0) => {
safe_println!("This crate does not load any additional lints or lint groups.")
}
(true, l, g) => {
if l > 0 {
safe_println!("Lint checks loaded by this crate:\n");
print_lints(loaded);
}
if g > 0 {
safe_println!("Lint groups loaded by this crate:\n");
print_lint_groups(loaded_groups, false);
}
2014-06-19 00:26:14 +00:00
}
}
}
/// Show help for flag categories shared between rustdoc and rustc.
///
/// Returns whether a help option was printed.
2023-12-17 23:57:26 +00:00
pub fn describe_flag_categories(early_dcx: &EarlyDiagCtxt, matches: &Matches) -> bool {
// Handle the special case of -Wall.
let wall = matches.opt_strs("W");
if wall.iter().any(|x| *x == "all") {
print_wall_help();
rustc_errors::FatalError.raise();
}
// Don't handle -W help here, because we might first load additional lints.
let debug_flags = matches.opt_strs("Z");
if debug_flags.iter().any(|x| *x == "help") {
describe_debug_flags();
return true;
}
let cg_flags = matches.opt_strs("C");
if cg_flags.iter().any(|x| *x == "help") {
describe_codegen_flags();
return true;
}
if cg_flags.iter().any(|x| *x == "no-stack-check") {
early_dcx.early_warn("the `-Cno-stack-check` flag is deprecated and does nothing");
}
if cg_flags.iter().any(|x| x.starts_with("inline-threshold")) {
early_dcx.early_warn("the `-Cinline-threshold` flag is deprecated and does nothing (consider using `-Cllvm-args=--inline-threshold=...`)");
}
if cg_flags.iter().any(|x| *x == "passes=list") {
let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend="));
let opts = config::Options::default();
let sysroot = filesearch::materialize_sysroot(opts.maybe_sysroot.clone());
let target = config::build_target_config(early_dcx, &opts, &sysroot);
get_codegen_backend(early_dcx, &sysroot, backend_name, &target).print_passes();
return true;
}
false
}
fn describe_debug_flags() {
2023-04-25 00:04:26 +00:00
safe_println!("\nAvailable options:\n");
print_flag_list("-Z", config::Z_OPTIONS);
}
fn describe_codegen_flags() {
2023-04-25 00:04:26 +00:00
safe_println!("\nAvailable codegen options:\n");
print_flag_list("-C", config::CG_OPTIONS);
}
fn print_flag_list<T>(
cmdline_opt: &str,
flag_list: &[(&'static str, T, &'static str, &'static str)],
) {
let max_len = flag_list.iter().map(|&(name, _, _, _)| name.chars().count()).max().unwrap_or(0);
for &(name, _, _, desc) in flag_list {
2023-04-25 00:04:26 +00:00
safe_println!(
" {} {:>width$}=val -- {}",
2015-11-10 20:48:44 +00:00
cmdline_opt,
name.replace('_', "-"),
2015-11-10 20:48:44 +00:00
desc,
width = max_len
2015-11-10 20:48:44 +00:00
);
}
}
/// Process command line options. Emits messages as appropriate. If compilation
/// should continue, returns a getopts::Matches object parsed from args,
2019-02-08 13:53:55 +00:00
/// otherwise returns `None`.
///
2017-01-22 07:45:06 +00: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.
2023-10-17 00:37:58 +00:00
///
/// This does not need to be `pub` for rustc itself, but @chaosite needs it to
/// be public when using rustc as a library, see
/// <https://github.com/rust-lang/rust/commit/2b4c33817a5aaecabf4c6598d41e190080ec119e>
2023-12-17 23:57:26 +00:00
pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option<getopts::Matches> {
if args.is_empty() {
// user did not write `-v` nor `-Z unstable-options`, so do not
// include that extra information.
2020-10-10 18:27:52 +00:00
let nightly_build =
rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build();
usage(false, false, nightly_build);
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();
let optgroups = config::rustc_optgroups();
for option in &optgroups {
(option.apply)(&mut options);
}
let matches = options.parse(args).unwrap_or_else(|e| {
let msg: Option<String> = match e {
getopts::Fail::UnrecognizedOption(ref opt) => CG_OPTIONS
.iter()
.map(|&(name, ..)| ('C', name))
.chain(Z_OPTIONS.iter().map(|&(name, ..)| ('Z', name)))
.find(|&(_, name)| *opt == name.replace('_', "-"))
.map(|(flag, _)| format!("{e}. Did you mean `-{flag} {opt}`?")),
getopts::Fail::ArgumentMissing(ref opt) => {
optgroups.iter().find(|option| option.name == opt).map(|option| {
// Print the help just for the option in question.
let mut options = getopts::Options::new();
(option.apply)(&mut options);
// getopt requires us to pass a function for joining an iterator of
// strings, even though in this case we expect exactly one string.
options.usage_with_format(|it| {
it.fold(format!("{e}\nUsage:"), |a, b| a + "\n" + &b)
})
})
}
_ => None,
};
early_dcx.early_fatal(msg.unwrap_or_else(|| e.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)
2023-12-17 23:57:26 +00:00
nightly_options::check_nightly_options(early_dcx, &matches, &config::rustc_optgroups());
if matches.opt_present("h") || matches.opt_present("help") {
// Only show unstable options in --help if we accept unstable options.
2020-10-10 18:27:52 +00:00
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);
return None;
}
2023-12-17 23:57:26 +00:00
if describe_flag_categories(early_dcx, &matches) {
return None;
}
if matches.opt_present("version") {
2023-12-17 23:57:26 +00:00
version!(early_dcx, "rustc", &matches);
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 00:03:39 +00:00
return None;
}
Some(matches)
}
fn parse_crate_attrs<'a>(sess: &'a Session) -> PResult<'a, ast::AttrVec> {
let mut parser = unwrap_or_emit_fatal(match &sess.io.input {
Input::File(file) => new_parser_from_file(&sess.psess, file, None),
Input::Str { name, input } => {
new_parser_from_source_str(&sess.psess, name.clone(), input.clone())
}
});
parser.parse_inner_attributes()
}
/// 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, FatalError> {
catch_unwind(panic::AssertUnwindSafe(f)).map_err(|value| {
if value.is::<rustc_errors::FatalErrorMarker>() {
FatalError
} else {
panic::resume_unwind(value);
}
})
}
/// 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 {
match catch_fatal_errors(f) {
Ok(Ok(())) => EXIT_SUCCESS,
_ => EXIT_FAILURE,
}
}
2023-10-06 07:15:46 +00:00
static ICE_PATH: OnceLock<Option<PathBuf>> = OnceLock::new();
// This function should only be called from the ICE hook.
//
// The intended behavior is that `run_compiler` will invoke `ice_path_with_config` early in the
// initialization process to properly initialize the ICE_PATH static based on parsed CLI flags.
//
// Subsequent calls to either function will then return the proper ICE path as configured by
// the environment and cli flags
2023-10-06 07:15:46 +00:00
fn ice_path() -> &'static Option<PathBuf> {
ice_path_with_config(None)
}
fn ice_path_with_config(config: Option<&UnstableOptions>) -> &'static Option<PathBuf> {
if ICE_PATH.get().is_some() && config.is_some() && cfg!(debug_assertions) {
tracing::warn!(
"ICE_PATH has already been initialized -- files may be emitted at unintended paths"
)
}
ICE_PATH.get_or_init(|| {
if !rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build() {
return None;
}
let mut path = match std::env::var_os("RUSTC_ICE") {
Some(s) => {
if s == "0" {
// Explicitly opting out of writing ICEs to disk.
return None;
}
if let Some(unstable_opts) = config && unstable_opts.metrics_dir.is_some() {
tracing::warn!("ignoring -Zerror-metrics in favor of RUSTC_ICE for destination of ICE report files");
}
PathBuf::from(s)
}
None => config
.and_then(|unstable_opts| unstable_opts.metrics_dir.to_owned())
.or_else(|| std::env::current_dir().ok())
.unwrap_or_default(),
};
let now: OffsetDateTime = SystemTime::now().into();
let file_now = now
.format(
// Don't use a standard datetime format because Windows doesn't support `:` in paths
&time::format_description::parse("[year]-[month]-[day]T[hour]_[minute]_[second]")
.unwrap(),
)
.unwrap_or_default();
let pid = std::process::id();
path.push(format!("rustc-ice-{file_now}-{pid}.txt"));
Some(path)
})
}
/// Installs a panic hook that will print the ICE message on unexpected panics.
///
/// The hook is intended to be useable even by external tools. You can pass a custom
/// `bug_report_url`, or report arbitrary info in `extra_info`. Note that `extra_info` is called in
/// a context where *the thread is currently panicking*, so it must not panic or the process will
/// abort.
///
/// If you have no extra info to report, pass the empty closure `|_| ()` as the argument to
/// extra_info.
///
/// Returns a flag that can be set to disable the note for submitting a bug. This can be passed to
/// [`RunCompiler::set_using_internal_features`] to let macro expansion set it when encountering
/// internal features.
///
/// A custom rustc driver can skip calling this to set up a custom ICE hook.
2023-12-17 10:48:57 +00:00
pub fn install_ice_hook(
bug_report_url: &'static str,
extra_info: fn(&DiagCtxt),
) -> Arc<AtomicBool> {
// If the user has not explicitly overridden "RUST_BACKTRACE", then produce
// full backtraces. When a compiler ICE happens, we want to gather
// as much information as possible to present in the issue opened
// by the user. Compiler developers and other rustc users can
// opt in to less-verbose backtraces by manually setting "RUST_BACKTRACE"
// (e.g. `RUST_BACKTRACE=1`)
if env::var_os("RUST_BACKTRACE").is_none() {
panic::set_backtrace_style(panic::BacktraceStyle::Full);
}
2022-08-02 00:08:13 +00:00
let using_internal_features = Arc::new(std::sync::atomic::AtomicBool::default());
let using_internal_features_hook = using_internal_features.clone();
2023-09-07 04:16:06 +00:00
panic::update_hook(Box::new(
move |default_hook: &(dyn Fn(&PanicHookInfo<'_>) + Send + Sync + 'static),
info: &PanicHookInfo<'_>| {
2024-03-09 00:26:59 +00:00
// Lock stderr to prevent interleaving of concurrent panics.
let _guard = io::stderr().lock();
2023-09-07 04:16:06 +00:00
// If the error was caused by a broken pipe then this is not a bug.
// Write the error and return immediately. See #98700.
#[cfg(windows)]
if let Some(msg) = info.payload().downcast_ref::<String>() {
if msg.starts_with("failed printing to stdout: ") && msg.ends_with("(os error 232)")
{
// the error code is already going to be reported when the panic unwinds up the stack
let early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());
let _ = early_dcx.early_err(msg.clone());
2023-09-07 04:16:06 +00:00
return;
}
};
2023-09-07 04:16:06 +00:00
// Invoke the default handler, which prints the actual panic message and optionally a backtrace
// Don't do this for delayed bugs, which already emit their own more useful backtrace.
if !info.payload().is::<rustc_errors::DelayedBugPanic>() {
default_hook(info);
// Separate the output with an empty line
eprintln!();
if let Some(ice_path) = ice_path()
&& let Ok(mut out) = File::options().create(true).append(true).open(&ice_path)
{
// The current implementation always returns `Some`.
let location = info.location().unwrap();
let msg = match info.payload().downcast_ref::<&'static str>() {
Some(s) => *s,
None => match info.payload().downcast_ref::<String>() {
Some(s) => &s[..],
None => "Box<dyn Any>",
},
};
let thread = std::thread::current();
let name = thread.name().unwrap_or("<unnamed>");
let _ = write!(
&mut out,
"thread '{name}' panicked at {location}:\n\
{msg}\n\
stack backtrace:\n\
{:#}",
std::backtrace::Backtrace::force_capture()
);
2023-09-07 04:16:06 +00:00
}
}
2023-09-07 04:16:06 +00:00
// Print the ICE message
report_ice(info, bug_report_url, extra_info, &using_internal_features_hook);
2023-09-07 04:16:06 +00:00
},
));
using_internal_features
}
/// Prints the ICE message, including query stack, but without backtrace.
///
/// 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.
fn report_ice(
info: &panic::PanicHookInfo<'_>,
bug_report_url: &str,
2023-12-17 10:48:57 +00:00
extra_info: fn(&DiagCtxt),
using_internal_features: &AtomicBool,
) {
let fallback_bundle =
rustc_errors::fallback_fluent_bundle(crate::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
let emitter = Box::new(rustc_errors::emitter::HumanEmitter::new(
stderr_destination(rustc_errors::ColorConfig::Auto),
fallback_bundle,
));
let dcx = rustc_errors::DiagCtxt::new(emitter);
let dcx = dcx.handle();
// a .span_bug or .bug call has already printed what
// it wants to print.
if !info.payload().is::<rustc_errors::ExplicitBug>()
&& !info.payload().is::<rustc_errors::DelayedBugPanic>()
{
dcx.emit_err(session_diagnostics::Ice);
}
if using_internal_features.load(std::sync::atomic::Ordering::Relaxed) {
dcx.emit_note(session_diagnostics::IceBugReportInternalFeature);
} else {
dcx.emit_note(session_diagnostics::IceBugReport { bug_report_url });
// Only emit update nightly hint for users on nightly builds.
if rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build() {
dcx.emit_note(session_diagnostics::UpdateNightlyNote);
}
}
let version = util::version_str!().unwrap_or("unknown_version");
let triple = config::host_triple();
static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
let file = if let Some(path) = ice_path() {
// Create the ICE dump target file.
match crate::fs::File::options().create(true).append(true).open(&path) {
Ok(mut file) => {
dcx.emit_note(session_diagnostics::IcePath { path: path.clone() });
if FIRST_PANIC.swap(false, Ordering::SeqCst) {
let _ = write!(file, "\n\nrustc version: {version}\nplatform: {triple}");
}
Some(file)
}
Err(err) => {
// The path ICE couldn't be written to disk, provide feedback to the user as to why.
dcx.emit_warn(session_diagnostics::IcePathError {
path: path.clone(),
error: err.to_string(),
env_var: std::env::var_os("RUSTC_ICE")
.map(PathBuf::from)
.map(|env_var| session_diagnostics::IcePathErrorEnv { env_var }),
});
dcx.emit_note(session_diagnostics::IceVersion { version, triple });
None
}
}
} else {
dcx.emit_note(session_diagnostics::IceVersion { version, triple });
None
};
if let Some((flags, excluded_cargo_defaults)) = rustc_session::utils::extra_compiler_flags() {
dcx.emit_note(session_diagnostics::IceFlags { flags: flags.join(" ") });
if excluded_cargo_defaults {
dcx.emit_note(session_diagnostics::IceExcludeCargoDefaults);
}
}
// If backtraces are enabled, also print the query stack
let backtrace = env::var_os("RUST_BACKTRACE").is_some_and(|x| &x != "0");
2020-09-29 14:44:07 +00:00
let num_frames = if backtrace { None } else { Some(2) };
interface::try_print_query_stack(dcx, num_frames, file);
// We don't trust this callback not to panic itself, so run it at the end after we're sure we've
// printed all the relevant info.
extra_info(&dcx);
#[cfg(windows)]
if env::var("RUSTC_BREAK_ON_ICE").is_ok() {
// Trigger a debugger if we crashed during bootstrap
unsafe { windows::Win32::System::Diagnostics::Debug::DebugBreak() };
}
}
/// This allows tools to enable rust logging without having to magically match rustc's
2020-08-14 06:05:01 +00:00
/// tracing crate version.
2023-12-17 23:57:26 +00:00
pub fn init_rustc_env_logger(early_dcx: &EarlyDiagCtxt) {
init_logger(early_dcx, rustc_log::LoggerConfig::from_env("RUSTC_LOG"));
}
/// This allows tools to enable rust logging without having to magically match rustc's
/// tracing crate version. In contrast to `init_rustc_env_logger` it allows you to choose
/// the values directly rather than having to set an environment variable.
2023-12-17 23:57:26 +00:00
pub fn init_logger(early_dcx: &EarlyDiagCtxt, cfg: rustc_log::LoggerConfig) {
if let Err(error) = rustc_log::init_logger(cfg) {
early_dcx.early_fatal(error.to_string());
2022-01-04 00:32:52 +00:00
}
}
/// Install our usual `ctrlc` handler, which sets [`rustc_const_eval::CTRL_C_RECEIVED`].
/// Making this handler optional lets tools can install a different handler, if they wish.
pub fn install_ctrlc_handler() {
#[cfg(not(target_family = "wasm"))]
ctrlc::set_handler(move || {
// Indicate that we have been signaled to stop, then give the rest of the compiler a bit of
// time to check CTRL_C_RECEIVED and run its own shutdown logic, but after a short amount
// of time exit the process. This sleep+exit ensures that even if nobody is checking
// CTRL_C_RECEIVED, the compiler exits reasonably promptly.
CTRL_C_RECEIVED.store(true, Ordering::Relaxed);
std::thread::sleep(Duration::from_millis(100));
std::process::exit(1);
})
.expect("Unable to install ctrlc handler");
}
pub fn main() -> ! {
let start_time = Instant::now();
let start_rss = get_resident_set_size();
let early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());
init_rustc_env_logger(&early_dcx);
signal_handler::install();
let mut callbacks = TimePassesCallbacks::default();
let using_internal_features = install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ());
install_ctrlc_handler();
let exit_code = catch_with_exit_code(|| {
RunCompiler::new(&args::raw_args(&early_dcx)?, &mut callbacks)
.set_using_internal_features(using_internal_features)
.run()
});
if let Some(format) = callbacks.time_passes {
let end_rss = get_resident_set_size();
print_time_passes_entry("total", start_time.elapsed(), start_rss, end_rss, format);
}
process::exit(exit_code)
}