Rollup merge of #112692 - jieyouxu:better-err-msg-for-unstable-options, r=davidtwco

Provide more context for `rustc +nightly -Zunstable-options` on stable

<img width="724" alt="Screenshot 2023-06-16 123456" src="https://github.com/rust-lang/rust/assets/39484203/1933e172-cb9f-4e51-9540-ade803a88360">

Closes #110090.
This commit is contained in:
Matthias Krüger 2023-06-27 22:10:14 +02:00 committed by GitHub
commit b6144cd843
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 509 additions and 471 deletions

View File

@ -225,10 +225,10 @@ pub(crate) fn write_ir_file(
let res = std::fs::File::create(clif_file_name).and_then(|mut file| write(&mut file)); let res = std::fs::File::create(clif_file_name).and_then(|mut file| write(&mut file));
if let Err(err) = res { if let Err(err) = res {
// Using early_warn as no Session is available here // Using early_warn as no Session is available here
rustc_session::early_warn( let handler = rustc_session::EarlyErrorHandler::new(
rustc_session::config::ErrorOutputType::default(), rustc_session::config::ErrorOutputType::default(),
format!("error writing ir file: {}", err),
); );
handler.early_warn(format!("error writing ir file: {}", err));
} }
} }

View File

@ -3,6 +3,8 @@ use std::fmt;
use std::fs; use std::fs;
use std::io; use std::io;
use rustc_session::EarlyErrorHandler;
fn arg_expand(arg: String) -> Result<Vec<String>, Error> { fn arg_expand(arg: String) -> Result<Vec<String>, Error> {
if let Some(path) = arg.strip_prefix('@') { if let Some(path) = arg.strip_prefix('@') {
let file = match fs::read_to_string(path) { let file = match fs::read_to_string(path) {
@ -21,15 +23,12 @@ fn arg_expand(arg: String) -> Result<Vec<String>, Error> {
/// **Note:** This function doesn't interpret argument 0 in any special way. /// **Note:** This function doesn't interpret argument 0 in any special way.
/// If this function is intended to be used with command line arguments, /// If this function is intended to be used with command line arguments,
/// `argv[0]` must be removed prior to calling it manually. /// `argv[0]` must be removed prior to calling it manually.
pub fn arg_expand_all(at_args: &[String]) -> Vec<String> { pub fn arg_expand_all(handler: &EarlyErrorHandler, at_args: &[String]) -> Vec<String> {
let mut args = Vec::new(); let mut args = Vec::new();
for arg in at_args { for arg in at_args {
match arg_expand(arg.clone()) { match arg_expand(arg.clone()) {
Ok(arg) => args.extend(arg), Ok(arg) => args.extend(arg),
Err(err) => rustc_session::early_error( Err(err) => handler.early_error(format!("Failed to load argument file: {err}")),
rustc_session::config::ErrorOutputType::default(),
format!("Failed to load argument file: {err}"),
),
} }
} }
args args

View File

@ -40,8 +40,7 @@ use rustc_session::config::{
use rustc_session::cstore::MetadataLoader; use rustc_session::cstore::MetadataLoader;
use rustc_session::getopts::{self, Matches}; use rustc_session::getopts::{self, Matches};
use rustc_session::lint::{Lint, LintId}; use rustc_session::lint::{Lint, LintId};
use rustc_session::{config, Session}; use rustc_session::{config, EarlyErrorHandler, Session};
use rustc_session::{early_error, early_error_no_abort, early_warn};
use rustc_span::source_map::{FileLoader, FileName}; use rustc_span::source_map::{FileLoader, FileName};
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_target::json::ToJson; use rustc_target::json::ToJson;
@ -174,6 +173,7 @@ pub trait Callbacks {
/// continue the compilation afterwards (defaults to `Compilation::Continue`) /// continue the compilation afterwards (defaults to `Compilation::Continue`)
fn after_analysis<'tcx>( fn after_analysis<'tcx>(
&mut self, &mut self,
_handler: &EarlyErrorHandler,
_compiler: &interface::Compiler, _compiler: &interface::Compiler,
_queries: &'tcx Queries<'tcx>, _queries: &'tcx Queries<'tcx>,
) -> Compilation { ) -> Compilation {
@ -260,6 +260,8 @@ fn run_compiler(
Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>, Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>,
>, >,
) -> interface::Result<()> { ) -> interface::Result<()> {
let mut early_error_handler = EarlyErrorHandler::new(ErrorOutputType::default());
// Throw away the first argument, the name of the binary. // Throw away the first argument, the name of the binary.
// In case of at_args being empty, as might be the case by // In case of at_args being empty, as might be the case by
// passing empty argument array to execve under some platforms, // passing empty argument array to execve under some platforms,
@ -270,22 +272,22 @@ fn run_compiler(
// the compiler with @empty_file as argv[0] and no more arguments. // the compiler with @empty_file as argv[0] and no more arguments.
let at_args = at_args.get(1..).unwrap_or_default(); let at_args = at_args.get(1..).unwrap_or_default();
let args = args::arg_expand_all(at_args); let args = args::arg_expand_all(&early_error_handler, at_args);
let Some(matches) = handle_options(&args) else { return Ok(()) }; let Some(matches) = handle_options(&early_error_handler, &args) else { return Ok(()) };
let sopts = config::build_session_options(&matches); let sopts = config::build_session_options(&mut early_error_handler, &matches);
// Set parallel mode before thread pool creation, which will create `Lock`s. // Set parallel mode before thread pool creation, which will create `Lock`s.
interface::set_thread_safe_mode(&sopts.unstable_opts); interface::set_thread_safe_mode(&sopts.unstable_opts);
if let Some(ref code) = matches.opt_str("explain") { if let Some(ref code) = matches.opt_str("explain") {
handle_explain(diagnostics_registry(), code, sopts.error_format); handle_explain(&early_error_handler, diagnostics_registry(), code);
return Ok(()); return Ok(());
} }
let cfg = interface::parse_cfgspecs(matches.opt_strs("cfg")); let cfg = interface::parse_cfgspecs(&early_error_handler, matches.opt_strs("cfg"));
let check_cfg = interface::parse_check_cfg(matches.opt_strs("check-cfg")); let check_cfg = interface::parse_check_cfg(&early_error_handler, matches.opt_strs("check-cfg"));
let (odir, ofile) = make_output(&matches); let (odir, ofile) = make_output(&matches);
let mut config = interface::Config { let mut config = interface::Config {
opts: sopts, opts: sopts,
@ -304,7 +306,7 @@ fn run_compiler(
registry: diagnostics_registry(), registry: diagnostics_registry(),
}; };
match make_input(config.opts.error_format, &matches.free) { match make_input(&early_error_handler, &matches.free) {
Err(reported) => return Err(reported), Err(reported) => return Err(reported),
Ok(Some(input)) => { Ok(Some(input)) => {
config.input = input; config.input = input;
@ -314,8 +316,13 @@ fn run_compiler(
Ok(None) => match matches.free.len() { Ok(None) => match matches.free.len() {
0 => { 0 => {
callbacks.config(&mut config); callbacks.config(&mut config);
early_error_handler.abort_if_errors();
interface::run_compiler(config, |compiler| { interface::run_compiler(config, |compiler| {
let sopts = &compiler.session().opts; let sopts = &compiler.session().opts;
let handler = EarlyErrorHandler::new(sopts.error_format);
if sopts.describe_lints { if sopts.describe_lints {
let mut lint_store = let mut lint_store =
rustc_lint::new_lint_store(compiler.session().enable_internal_lints()); rustc_lint::new_lint_store(compiler.session().enable_internal_lints());
@ -329,31 +336,38 @@ fn run_compiler(
describe_lints(compiler.session(), &lint_store, registered_lints); describe_lints(compiler.session(), &lint_store, registered_lints);
return; return;
} }
let should_stop = let should_stop = print_crate_info(
print_crate_info(&**compiler.codegen_backend(), compiler.session(), false); &handler,
&**compiler.codegen_backend(),
compiler.session(),
false,
);
if should_stop == Compilation::Stop { if should_stop == Compilation::Stop {
return; return;
} }
early_error(sopts.error_format, "no input filename given") handler.early_error("no input filename given")
}); });
return Ok(()); return Ok(());
} }
1 => panic!("make_input should have provided valid inputs"), 1 => panic!("make_input should have provided valid inputs"),
_ => early_error( _ => early_error_handler.early_error(format!(
config.opts.error_format, "multiple input filenames provided (first two filenames are `{}` and `{}`)",
format!( matches.free[0], matches.free[1],
"multiple input filenames provided (first two filenames are `{}` and `{}`)", )),
matches.free[0], matches.free[1],
),
),
}, },
}; };
early_error_handler.abort_if_errors();
interface::run_compiler(config, |compiler| { interface::run_compiler(config, |compiler| {
let sess = compiler.session(); let sess = compiler.session();
let should_stop = print_crate_info(&**compiler.codegen_backend(), sess, true) let handler = EarlyErrorHandler::new(sess.opts.error_format);
.and_then(|| list_metadata(sess, &*compiler.codegen_backend().metadata_loader()))
let should_stop = print_crate_info(&handler, &**compiler.codegen_backend(), sess, true)
.and_then(|| {
list_metadata(&handler, sess, &*compiler.codegen_backend().metadata_loader())
})
.and_then(|| try_process_rlink(sess, compiler)); .and_then(|| try_process_rlink(sess, compiler));
if should_stop == Compilation::Stop { if should_stop == Compilation::Stop {
@ -421,7 +435,7 @@ fn run_compiler(
queries.global_ctxt()?.enter(|tcx| tcx.analysis(()))?; queries.global_ctxt()?.enter(|tcx| tcx.analysis(()))?;
if callbacks.after_analysis(compiler, queries) == Compilation::Stop { if callbacks.after_analysis(&handler, compiler, queries) == Compilation::Stop {
return early_exit(); return early_exit();
} }
@ -475,7 +489,7 @@ fn make_output(matches: &getopts::Matches) -> (Option<PathBuf>, Option<OutFileNa
// Extract input (string or file and optional path) from matches. // Extract input (string or file and optional path) from matches.
fn make_input( fn make_input(
error_format: ErrorOutputType, handler: &EarlyErrorHandler,
free_matches: &[String], free_matches: &[String],
) -> Result<Option<Input>, ErrorGuaranteed> { ) -> Result<Option<Input>, ErrorGuaranteed> {
if free_matches.len() == 1 { if free_matches.len() == 1 {
@ -485,8 +499,7 @@ fn make_input(
if io::stdin().read_to_string(&mut src).is_err() { if io::stdin().read_to_string(&mut src).is_err() {
// Immediately stop compilation if there was an issue reading // Immediately stop compilation if there was an issue reading
// the input (for example if the input stream is not UTF-8). // the input (for example if the input stream is not UTF-8).
let reported = early_error_no_abort( let reported = handler.early_error_no_abort(
error_format,
"couldn't read from stdin, as it did not contain valid UTF-8", "couldn't read from stdin, as it did not contain valid UTF-8",
); );
return Err(reported); return Err(reported);
@ -527,7 +540,7 @@ impl Compilation {
} }
} }
fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) { fn handle_explain(handler: &EarlyErrorHandler, registry: Registry, code: &str) {
let upper_cased_code = code.to_ascii_uppercase(); let upper_cased_code = code.to_ascii_uppercase();
let normalised = let normalised =
if upper_cased_code.starts_with('E') { upper_cased_code } else { format!("E{code:0>4}") }; if upper_cased_code.starts_with('E') { upper_cased_code } else { format!("E{code:0>4}") };
@ -557,7 +570,7 @@ fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) {
} }
} }
Err(InvalidErrorCode) => { Err(InvalidErrorCode) => {
early_error(output, format!("{code} is not a valid error code")); handler.early_error(format!("{code} is not a valid error code"));
} }
} }
} }
@ -636,7 +649,11 @@ pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Comp
} }
} }
pub fn list_metadata(sess: &Session, metadata_loader: &dyn MetadataLoader) -> Compilation { pub fn list_metadata(
handler: &EarlyErrorHandler,
sess: &Session,
metadata_loader: &dyn MetadataLoader,
) -> Compilation {
if sess.opts.unstable_opts.ls { if sess.opts.unstable_opts.ls {
match sess.io.input { match sess.io.input {
Input::File(ref ifile) => { Input::File(ref ifile) => {
@ -646,7 +663,7 @@ pub fn list_metadata(sess: &Session, metadata_loader: &dyn MetadataLoader) -> Co
safe_println!("{}", String::from_utf8(v).unwrap()); safe_println!("{}", String::from_utf8(v).unwrap());
} }
Input::Str { .. } => { Input::Str { .. } => {
early_error(ErrorOutputType::default(), "cannot list metadata for stdin"); handler.early_error("cannot list metadata for stdin");
} }
} }
return Compilation::Stop; return Compilation::Stop;
@ -656,6 +673,7 @@ pub fn list_metadata(sess: &Session, metadata_loader: &dyn MetadataLoader) -> Co
} }
fn print_crate_info( fn print_crate_info(
handler: &EarlyErrorHandler,
codegen_backend: &dyn CodegenBackend, codegen_backend: &dyn CodegenBackend,
sess: &Session, sess: &Session,
parse_attrs: bool, parse_attrs: bool,
@ -787,10 +805,8 @@ fn print_crate_info(
.expect("unknown Apple target OS") .expect("unknown Apple target OS")
) )
} else { } else {
early_error( handler
ErrorOutputType::default(), .early_error("only Apple targets currently support deployment version info")
"only Apple targets currently support deployment version info",
)
} }
} }
} }
@ -801,11 +817,12 @@ fn print_crate_info(
/// Prints version information /// Prints version information
/// ///
/// NOTE: this is a macro to support drivers built at a different time than the main `rustc_driver` crate. /// NOTE: this is a macro to support drivers built at a different time than the main `rustc_driver` crate.
pub macro version($binary: literal, $matches: expr) { pub macro version($handler: expr, $binary: literal, $matches: expr) {
fn unw(x: Option<&str>) -> &str { fn unw(x: Option<&str>) -> &str {
x.unwrap_or("unknown") x.unwrap_or("unknown")
} }
$crate::version_at_macro_invocation( $crate::version_at_macro_invocation(
$handler,
$binary, $binary,
$matches, $matches,
unw(option_env!("CFG_VERSION")), unw(option_env!("CFG_VERSION")),
@ -817,6 +834,7 @@ pub macro version($binary: literal, $matches: expr) {
#[doc(hidden)] // use the macro instead #[doc(hidden)] // use the macro instead
pub fn version_at_macro_invocation( pub fn version_at_macro_invocation(
handler: &EarlyErrorHandler,
binary: &str, binary: &str,
matches: &getopts::Matches, matches: &getopts::Matches,
version: &str, version: &str,
@ -837,7 +855,7 @@ pub fn version_at_macro_invocation(
let debug_flags = matches.opt_strs("Z"); let debug_flags = matches.opt_strs("Z");
let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend=")); let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend="));
get_codegen_backend(&None, backend_name).print_version(); get_codegen_backend(handler, &None, backend_name).print_version();
} }
} }
@ -1014,7 +1032,7 @@ Available lint options:
/// Show help for flag categories shared between rustdoc and rustc. /// Show help for flag categories shared between rustdoc and rustc.
/// ///
/// Returns whether a help option was printed. /// Returns whether a help option was printed.
pub fn describe_flag_categories(matches: &Matches) -> bool { pub fn describe_flag_categories(handler: &EarlyErrorHandler, matches: &Matches) -> bool {
// Handle the special case of -Wall. // Handle the special case of -Wall.
let wall = matches.opt_strs("W"); let wall = matches.opt_strs("W");
if wall.iter().any(|x| *x == "all") { if wall.iter().any(|x| *x == "all") {
@ -1036,15 +1054,12 @@ pub fn describe_flag_categories(matches: &Matches) -> bool {
} }
if cg_flags.iter().any(|x| *x == "no-stack-check") { if cg_flags.iter().any(|x| *x == "no-stack-check") {
early_warn( handler.early_warn("the --no-stack-check flag is deprecated and does nothing");
ErrorOutputType::default(),
"the --no-stack-check flag is deprecated and does nothing",
);
} }
if cg_flags.iter().any(|x| *x == "passes=list") { if cg_flags.iter().any(|x| *x == "passes=list") {
let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend=")); let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend="));
get_codegen_backend(&None, backend_name).print_passes(); get_codegen_backend(handler, &None, backend_name).print_passes();
return true; return true;
} }
@ -1101,7 +1116,7 @@ fn print_flag_list<T>(
/// ///
/// So with all that in mind, the comments below have some more detail about the /// 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. /// contortions done here to get things to work out correctly.
pub fn handle_options(args: &[String]) -> Option<getopts::Matches> { pub fn handle_options(handler: &EarlyErrorHandler, args: &[String]) -> Option<getopts::Matches> {
if args.is_empty() { if args.is_empty() {
// user did not write `-v` nor `-Z unstable-options`, so do not // user did not write `-v` nor `-Z unstable-options`, so do not
// include that extra information. // include that extra information.
@ -1127,7 +1142,7 @@ pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
.map(|(flag, _)| format!("{e}. Did you mean `-{flag} {opt}`?")), .map(|(flag, _)| format!("{e}. Did you mean `-{flag} {opt}`?")),
_ => None, _ => None,
}; };
early_error(ErrorOutputType::default(), msg.unwrap_or_else(|| e.to_string())); handler.early_error(msg.unwrap_or_else(|| e.to_string()));
}); });
// For all options we just parsed, we check a few aspects: // For all options we just parsed, we check a few aspects:
@ -1141,7 +1156,7 @@ pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
// we're good to go. // we're good to go.
// * Otherwise, if we're an unstable option then we generate an error // * Otherwise, if we're an unstable option then we generate an error
// (unstable option being used on stable) // (unstable option being used on stable)
nightly_options::check_nightly_options(&matches, &config::rustc_optgroups()); nightly_options::check_nightly_options(handler, &matches, &config::rustc_optgroups());
if matches.opt_present("h") || matches.opt_present("help") { if matches.opt_present("h") || matches.opt_present("help") {
// Only show unstable options in --help if we accept unstable options. // Only show unstable options in --help if we accept unstable options.
@ -1151,12 +1166,12 @@ pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
return None; return None;
} }
if describe_flag_categories(&matches) { if describe_flag_categories(handler, &matches) {
return None; return None;
} }
if matches.opt_present("version") { if matches.opt_present("version") {
version!("rustc", &matches); version!(handler, "rustc", &matches);
return None; return None;
} }
@ -1276,7 +1291,8 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler))
if let Some(msg) = info.payload().downcast_ref::<String>() { if let Some(msg) = info.payload().downcast_ref::<String>() {
if msg.starts_with("failed printing to stdout: ") && msg.ends_with("(os error 232)") { 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 // the error code is already going to be reported when the panic unwinds up the stack
let _ = early_error_no_abort(ErrorOutputType::default(), msg.clone()); let handler = EarlyErrorHandler::new(ErrorOutputType::default());
let _ = handler.early_error_no_abort(msg.clone());
return; return;
} }
}; };
@ -1359,16 +1375,16 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info:
/// This allows tools to enable rust logging without having to magically match rustc's /// This allows tools to enable rust logging without having to magically match rustc's
/// tracing crate version. /// tracing crate version.
pub fn init_rustc_env_logger() { pub fn init_rustc_env_logger(handler: &EarlyErrorHandler) {
init_env_logger("RUSTC_LOG"); init_env_logger(handler, "RUSTC_LOG");
} }
/// This allows tools to enable rust logging without having to magically match rustc's /// 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 an env var /// tracing crate version. In contrast to `init_rustc_env_logger` it allows you to choose an env var
/// other than `RUSTC_LOG`. /// other than `RUSTC_LOG`.
pub fn init_env_logger(env: &str) { pub fn init_env_logger(handler: &EarlyErrorHandler, env: &str) {
if let Err(error) = rustc_log::init_env_logger(env) { if let Err(error) = rustc_log::init_env_logger(env) {
early_error(ErrorOutputType::default(), error.to_string()); handler.early_error(error.to_string());
} }
} }
@ -1424,7 +1440,10 @@ mod signal_handler {
pub fn main() -> ! { pub fn main() -> ! {
let start_time = Instant::now(); let start_time = Instant::now();
let start_rss = get_resident_set_size(); let start_rss = get_resident_set_size();
init_rustc_env_logger();
let handler = EarlyErrorHandler::new(ErrorOutputType::default());
init_rustc_env_logger(&handler);
signal_handler::install(); signal_handler::install();
let mut callbacks = TimePassesCallbacks::default(); let mut callbacks = TimePassesCallbacks::default();
install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ()); install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ());
@ -1433,10 +1452,7 @@ pub fn main() -> ! {
.enumerate() .enumerate()
.map(|(i, arg)| { .map(|(i, arg)| {
arg.into_string().unwrap_or_else(|arg| { arg.into_string().unwrap_or_else(|arg| {
early_error( handler.early_error(format!("argument {i} is not valid Unicode: {arg:?}"))
ErrorOutputType::default(),
format!("argument {i} is not valid Unicode: {arg:?}"),
)
}) })
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();

View File

@ -14,12 +14,11 @@ use rustc_middle::{bug, ty};
use rustc_parse::maybe_new_parser_from_source_str; use rustc_parse::maybe_new_parser_from_source_str;
use rustc_query_impl::QueryCtxt; use rustc_query_impl::QueryCtxt;
use rustc_query_system::query::print_query_stack; use rustc_query_system::query::print_query_stack;
use rustc_session::config::{self, ErrorOutputType, Input, OutFileName, OutputFilenames}; use rustc_session::config::{self, CheckCfg, ExpectedValues, Input, OutFileName, OutputFilenames};
use rustc_session::config::{CheckCfg, ExpectedValues};
use rustc_session::lint;
use rustc_session::parse::{CrateConfig, ParseSess}; use rustc_session::parse::{CrateConfig, ParseSess};
use rustc_session::CompilerIO;
use rustc_session::Session; use rustc_session::Session;
use rustc_session::{early_error, CompilerIO}; use rustc_session::{lint, EarlyErrorHandler};
use rustc_span::source_map::{FileLoader, FileName}; use rustc_span::source_map::{FileLoader, FileName};
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use std::path::PathBuf; use std::path::PathBuf;
@ -66,7 +65,10 @@ pub fn set_thread_safe_mode(sopts: &config::UnstableOptions) {
} }
/// Converts strings provided as `--cfg [cfgspec]` into a `crate_cfg`. /// Converts strings provided as `--cfg [cfgspec]` into a `crate_cfg`.
pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String>)> { pub fn parse_cfgspecs(
handler: &EarlyErrorHandler,
cfgspecs: Vec<String>,
) -> FxHashSet<(String, Option<String>)> {
rustc_span::create_default_session_if_not_set_then(move |_| { rustc_span::create_default_session_if_not_set_then(move |_| {
let cfg = cfgspecs let cfg = cfgspecs
.into_iter() .into_iter()
@ -78,10 +80,10 @@ pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String
macro_rules! error { macro_rules! error {
($reason: expr) => { ($reason: expr) => {
early_error( handler.early_error(format!(
ErrorOutputType::default(), concat!("invalid `--cfg` argument: `{}` (", $reason, ")"),
format!(concat!("invalid `--cfg` argument: `{}` (", $reason, ")"), s), s
); ));
}; };
} }
@ -125,7 +127,7 @@ pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String
} }
/// Converts strings provided as `--check-cfg [specs]` into a `CheckCfg`. /// Converts strings provided as `--check-cfg [specs]` into a `CheckCfg`.
pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg { pub fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) -> CheckCfg {
rustc_span::create_default_session_if_not_set_then(move |_| { rustc_span::create_default_session_if_not_set_then(move |_| {
let mut check_cfg = CheckCfg::default(); let mut check_cfg = CheckCfg::default();
@ -137,10 +139,10 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
macro_rules! error { macro_rules! error {
($reason: expr) => { ($reason: expr) => {
early_error( handler.early_error(format!(
ErrorOutputType::default(), concat!("invalid `--check-cfg` argument: `{}` (", $reason, ")"),
format!(concat!("invalid `--check-cfg` argument: `{}` (", $reason, ")"), s), s
) ))
}; };
} }
@ -294,8 +296,11 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
let registry = &config.registry; let registry = &config.registry;
let handler = EarlyErrorHandler::new(config.opts.error_format);
let temps_dir = config.opts.unstable_opts.temps_dir.as_deref().map(PathBuf::from); let temps_dir = config.opts.unstable_opts.temps_dir.as_deref().map(PathBuf::from);
let (mut sess, codegen_backend) = util::create_session( let (mut sess, codegen_backend) = util::create_session(
&handler,
config.opts, config.opts,
config.crate_cfg, config.crate_cfg,
config.crate_check_cfg, config.crate_check_cfg,

View File

@ -21,8 +21,8 @@ use rustc_session::config::{InstrumentCoverage, Passes};
use rustc_session::lint::Level; use rustc_session::lint::Level;
use rustc_session::search_paths::SearchPath; use rustc_session::search_paths::SearchPath;
use rustc_session::utils::{CanonicalizedPath, NativeLib, NativeLibKind}; use rustc_session::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
use rustc_session::CompilerIO;
use rustc_session::{build_session, getopts, Session}; use rustc_session::{build_session, getopts, Session};
use rustc_session::{CompilerIO, EarlyErrorHandler};
use rustc_span::edition::{Edition, DEFAULT_EDITION}; use rustc_span::edition::{Edition, DEFAULT_EDITION};
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_span::FileName; use rustc_span::FileName;
@ -36,15 +36,18 @@ use std::path::{Path, PathBuf};
type CfgSpecs = FxHashSet<(String, Option<String>)>; type CfgSpecs = FxHashSet<(String, Option<String>)>;
fn build_session_options_and_crate_config(matches: getopts::Matches) -> (Options, CfgSpecs) { fn build_session_options_and_crate_config(
let sessopts = build_session_options(&matches); handler: &mut EarlyErrorHandler,
let cfg = parse_cfgspecs(matches.opt_strs("cfg")); matches: getopts::Matches,
) -> (Options, CfgSpecs) {
let sessopts = build_session_options(handler, &matches);
let cfg = parse_cfgspecs(handler, matches.opt_strs("cfg"));
(sessopts, cfg) (sessopts, cfg)
} }
fn mk_session(matches: getopts::Matches) -> (Session, CfgSpecs) { fn mk_session(handler: &mut EarlyErrorHandler, matches: getopts::Matches) -> (Session, CfgSpecs) {
let registry = registry::Registry::new(&[]); let registry = registry::Registry::new(&[]);
let (sessopts, cfg) = build_session_options_and_crate_config(matches); let (sessopts, cfg) = build_session_options_and_crate_config(handler, matches);
let temps_dir = sessopts.unstable_opts.temps_dir.as_deref().map(PathBuf::from); let temps_dir = sessopts.unstable_opts.temps_dir.as_deref().map(PathBuf::from);
let io = CompilerIO { let io = CompilerIO {
input: Input::Str { name: FileName::Custom(String::new()), input: String::new() }, input: Input::Str { name: FileName::Custom(String::new()), input: String::new() },
@ -52,8 +55,18 @@ fn mk_session(matches: getopts::Matches) -> (Session, CfgSpecs) {
output_file: None, output_file: None,
temps_dir, temps_dir,
}; };
let sess = let sess = build_session(
build_session(sessopts, io, None, registry, vec![], Default::default(), None, None, ""); handler,
sessopts,
io,
None,
registry,
vec![],
Default::default(),
None,
None,
"",
);
(sess, cfg) (sess, cfg)
} }
@ -120,7 +133,8 @@ fn assert_non_crate_hash_different(x: &Options, y: &Options) {
fn test_switch_implies_cfg_test() { fn test_switch_implies_cfg_test() {
rustc_span::create_default_session_globals_then(|| { rustc_span::create_default_session_globals_then(|| {
let matches = optgroups().parse(&["--test".to_string()]).unwrap(); let matches = optgroups().parse(&["--test".to_string()]).unwrap();
let (sess, cfg) = mk_session(matches); let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
let (sess, cfg) = mk_session(&mut handler, matches);
let cfg = build_configuration(&sess, to_crate_config(cfg)); let cfg = build_configuration(&sess, to_crate_config(cfg));
assert!(cfg.contains(&(sym::test, None))); assert!(cfg.contains(&(sym::test, None)));
}); });
@ -131,7 +145,8 @@ fn test_switch_implies_cfg_test() {
fn test_switch_implies_cfg_test_unless_cfg_test() { fn test_switch_implies_cfg_test_unless_cfg_test() {
rustc_span::create_default_session_globals_then(|| { rustc_span::create_default_session_globals_then(|| {
let matches = optgroups().parse(&["--test".to_string(), "--cfg=test".to_string()]).unwrap(); let matches = optgroups().parse(&["--test".to_string(), "--cfg=test".to_string()]).unwrap();
let (sess, cfg) = mk_session(matches); let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
let (sess, cfg) = mk_session(&mut handler, matches);
let cfg = build_configuration(&sess, to_crate_config(cfg)); let cfg = build_configuration(&sess, to_crate_config(cfg));
let mut test_items = cfg.iter().filter(|&&(name, _)| name == sym::test); let mut test_items = cfg.iter().filter(|&&(name, _)| name == sym::test);
assert!(test_items.next().is_some()); assert!(test_items.next().is_some());
@ -143,20 +158,23 @@ fn test_switch_implies_cfg_test_unless_cfg_test() {
fn test_can_print_warnings() { fn test_can_print_warnings() {
rustc_span::create_default_session_globals_then(|| { rustc_span::create_default_session_globals_then(|| {
let matches = optgroups().parse(&["-Awarnings".to_string()]).unwrap(); let matches = optgroups().parse(&["-Awarnings".to_string()]).unwrap();
let (sess, _) = mk_session(matches); let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
let (sess, _) = mk_session(&mut handler, matches);
assert!(!sess.diagnostic().can_emit_warnings()); assert!(!sess.diagnostic().can_emit_warnings());
}); });
rustc_span::create_default_session_globals_then(|| { rustc_span::create_default_session_globals_then(|| {
let matches = let matches =
optgroups().parse(&["-Awarnings".to_string(), "-Dwarnings".to_string()]).unwrap(); optgroups().parse(&["-Awarnings".to_string(), "-Dwarnings".to_string()]).unwrap();
let (sess, _) = mk_session(matches); let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
let (sess, _) = mk_session(&mut handler, matches);
assert!(sess.diagnostic().can_emit_warnings()); assert!(sess.diagnostic().can_emit_warnings());
}); });
rustc_span::create_default_session_globals_then(|| { rustc_span::create_default_session_globals_then(|| {
let matches = optgroups().parse(&["-Adead_code".to_string()]).unwrap(); let matches = optgroups().parse(&["-Adead_code".to_string()]).unwrap();
let (sess, _) = mk_session(matches); let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
let (sess, _) = mk_session(&mut handler, matches);
assert!(sess.diagnostic().can_emit_warnings()); assert!(sess.diagnostic().can_emit_warnings());
}); });
} }
@ -302,35 +320,36 @@ fn test_search_paths_tracking_hash_different_order() {
let mut v3 = Options::default(); let mut v3 = Options::default();
let mut v4 = Options::default(); let mut v4 = Options::default();
let handler = EarlyErrorHandler::new(JSON);
const JSON: ErrorOutputType = ErrorOutputType::Json { const JSON: ErrorOutputType = ErrorOutputType::Json {
pretty: false, pretty: false,
json_rendered: HumanReadableErrorType::Default(ColorConfig::Never), json_rendered: HumanReadableErrorType::Default(ColorConfig::Never),
}; };
// Reference // Reference
v1.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON)); v1.search_paths.push(SearchPath::from_cli_opt(&handler, "native=abc"));
v1.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON)); v1.search_paths.push(SearchPath::from_cli_opt(&handler, "crate=def"));
v1.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON)); v1.search_paths.push(SearchPath::from_cli_opt(&handler, "dependency=ghi"));
v1.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON)); v1.search_paths.push(SearchPath::from_cli_opt(&handler, "framework=jkl"));
v1.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON)); v1.search_paths.push(SearchPath::from_cli_opt(&handler, "all=mno"));
v2.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON)); v2.search_paths.push(SearchPath::from_cli_opt(&handler, "native=abc"));
v2.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON)); v2.search_paths.push(SearchPath::from_cli_opt(&handler, "dependency=ghi"));
v2.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON)); v2.search_paths.push(SearchPath::from_cli_opt(&handler, "crate=def"));
v2.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON)); v2.search_paths.push(SearchPath::from_cli_opt(&handler, "framework=jkl"));
v2.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON)); v2.search_paths.push(SearchPath::from_cli_opt(&handler, "all=mno"));
v3.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON)); v3.search_paths.push(SearchPath::from_cli_opt(&handler, "crate=def"));
v3.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON)); v3.search_paths.push(SearchPath::from_cli_opt(&handler, "framework=jkl"));
v3.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON)); v3.search_paths.push(SearchPath::from_cli_opt(&handler, "native=abc"));
v3.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON)); v3.search_paths.push(SearchPath::from_cli_opt(&handler, "dependency=ghi"));
v3.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON)); v3.search_paths.push(SearchPath::from_cli_opt(&handler, "all=mno"));
v4.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON)); v4.search_paths.push(SearchPath::from_cli_opt(&handler, "all=mno"));
v4.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON)); v4.search_paths.push(SearchPath::from_cli_opt(&handler, "native=abc"));
v4.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON)); v4.search_paths.push(SearchPath::from_cli_opt(&handler, "crate=def"));
v4.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON)); v4.search_paths.push(SearchPath::from_cli_opt(&handler, "dependency=ghi"));
v4.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON)); v4.search_paths.push(SearchPath::from_cli_opt(&handler, "framework=jkl"));
assert_same_hash(&v1, &v2); assert_same_hash(&v1, &v2);
assert_same_hash(&v1, &v3); assert_same_hash(&v1, &v3);
@ -851,7 +870,9 @@ fn test_edition_parsing() {
let options = Options::default(); let options = Options::default();
assert!(options.edition == DEFAULT_EDITION); assert!(options.edition == DEFAULT_EDITION);
let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
let matches = optgroups().parse(&["--edition=2018".to_string()]).unwrap(); let matches = optgroups().parse(&["--edition=2018".to_string()]).unwrap();
let (sessopts, _) = build_session_options_and_crate_config(matches); let (sessopts, _) = build_session_options_and_crate_config(&mut handler, matches);
assert!(sessopts.edition == Edition::Edition2018) assert!(sessopts.edition == Edition::Edition2018)
} }

View File

@ -11,16 +11,16 @@ use rustc_parse::validate_attr;
use rustc_session as session; use rustc_session as session;
use rustc_session::config::CheckCfg; use rustc_session::config::CheckCfg;
use rustc_session::config::{self, CrateType}; use rustc_session::config::{self, CrateType};
use rustc_session::config::{ErrorOutputType, OutFileName, OutputFilenames, OutputTypes}; use rustc_session::config::{OutFileName, OutputFilenames, OutputTypes};
use rustc_session::filesearch::sysroot_candidates; use rustc_session::filesearch::sysroot_candidates;
use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer}; use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer};
use rustc_session::parse::CrateConfig; use rustc_session::parse::CrateConfig;
use rustc_session::{early_error, filesearch, output, Session}; use rustc_session::{filesearch, output, Session};
use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::edition::Edition; use rustc_span::edition::Edition;
use rustc_span::source_map::FileLoader; use rustc_span::source_map::FileLoader;
use rustc_span::symbol::{sym, Symbol}; use rustc_span::symbol::{sym, Symbol};
use session::CompilerIO; use session::{CompilerIO, EarlyErrorHandler};
use std::env; use std::env;
use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
use std::mem; use std::mem;
@ -58,6 +58,7 @@ pub fn add_configuration(
} }
pub fn create_session( pub fn create_session(
handler: &EarlyErrorHandler,
sopts: config::Options, sopts: config::Options,
cfg: FxHashSet<(String, Option<String>)>, cfg: FxHashSet<(String, Option<String>)>,
check_cfg: CheckCfg, check_cfg: CheckCfg,
@ -73,7 +74,11 @@ pub fn create_session(
let codegen_backend = if let Some(make_codegen_backend) = make_codegen_backend { let codegen_backend = if let Some(make_codegen_backend) = make_codegen_backend {
make_codegen_backend(&sopts) make_codegen_backend(&sopts)
} else { } else {
get_codegen_backend(&sopts.maybe_sysroot, sopts.unstable_opts.codegen_backend.as_deref()) get_codegen_backend(
handler,
&sopts.maybe_sysroot,
sopts.unstable_opts.codegen_backend.as_deref(),
)
}; };
// target_override is documented to be called before init(), so this is okay // target_override is documented to be called before init(), so this is okay
@ -88,7 +93,7 @@ pub fn create_session(
) { ) {
Ok(bundle) => bundle, Ok(bundle) => bundle,
Err(e) => { Err(e) => {
early_error(sopts.error_format, format!("failed to load fluent bundle: {e}")); handler.early_error(format!("failed to load fluent bundle: {e}"));
} }
}; };
@ -96,6 +101,7 @@ pub fn create_session(
locale_resources.push(codegen_backend.locale_resource()); locale_resources.push(codegen_backend.locale_resource());
let mut sess = session::build_session( let mut sess = session::build_session(
handler,
sopts, sopts,
io, io,
bundle, bundle,
@ -218,16 +224,16 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
}) })
} }
fn load_backend_from_dylib(path: &Path) -> MakeBackendFn { fn load_backend_from_dylib(handler: &EarlyErrorHandler, path: &Path) -> MakeBackendFn {
let lib = unsafe { Library::new(path) }.unwrap_or_else(|err| { let lib = unsafe { Library::new(path) }.unwrap_or_else(|err| {
let err = format!("couldn't load codegen backend {path:?}: {err}"); let err = format!("couldn't load codegen backend {path:?}: {err}");
early_error(ErrorOutputType::default(), err); handler.early_error(err);
}); });
let backend_sym = unsafe { lib.get::<MakeBackendFn>(b"__rustc_codegen_backend") } let backend_sym = unsafe { lib.get::<MakeBackendFn>(b"__rustc_codegen_backend") }
.unwrap_or_else(|e| { .unwrap_or_else(|e| {
let err = format!("couldn't load codegen backend: {e}"); let err = format!("couldn't load codegen backend: {e}");
early_error(ErrorOutputType::default(), err); handler.early_error(err);
}); });
// Intentionally leak the dynamic library. We can't ever unload it // Intentionally leak the dynamic library. We can't ever unload it
@ -242,6 +248,7 @@ fn load_backend_from_dylib(path: &Path) -> MakeBackendFn {
/// ///
/// A name of `None` indicates that the default backend should be used. /// A name of `None` indicates that the default backend should be used.
pub fn get_codegen_backend( pub fn get_codegen_backend(
handler: &EarlyErrorHandler,
maybe_sysroot: &Option<PathBuf>, maybe_sysroot: &Option<PathBuf>,
backend_name: Option<&str>, backend_name: Option<&str>,
) -> Box<dyn CodegenBackend> { ) -> Box<dyn CodegenBackend> {
@ -251,10 +258,12 @@ pub fn get_codegen_backend(
let default_codegen_backend = option_env!("CFG_DEFAULT_CODEGEN_BACKEND").unwrap_or("llvm"); let default_codegen_backend = option_env!("CFG_DEFAULT_CODEGEN_BACKEND").unwrap_or("llvm");
match backend_name.unwrap_or(default_codegen_backend) { match backend_name.unwrap_or(default_codegen_backend) {
filename if filename.contains('.') => load_backend_from_dylib(filename.as_ref()), filename if filename.contains('.') => {
load_backend_from_dylib(handler, filename.as_ref())
}
#[cfg(feature = "llvm")] #[cfg(feature = "llvm")]
"llvm" => rustc_codegen_llvm::LlvmCodegenBackend::new, "llvm" => rustc_codegen_llvm::LlvmCodegenBackend::new,
backend_name => get_codegen_sysroot(maybe_sysroot, backend_name), backend_name => get_codegen_sysroot(handler, maybe_sysroot, backend_name),
} }
}); });
@ -286,7 +295,11 @@ fn get_rustc_path_inner(bin_path: &str) -> Option<PathBuf> {
}) })
} }
fn get_codegen_sysroot(maybe_sysroot: &Option<PathBuf>, backend_name: &str) -> MakeBackendFn { fn get_codegen_sysroot(
handler: &EarlyErrorHandler,
maybe_sysroot: &Option<PathBuf>,
backend_name: &str,
) -> MakeBackendFn {
// For now we only allow this function to be called once as it'll dlopen a // For now we only allow this function to be called once as it'll dlopen a
// few things, which seems to work best if we only do that once. In // few things, which seems to work best if we only do that once. In
// general this assertion never trips due to the once guard in `get_codegen_backend`, // general this assertion never trips due to the once guard in `get_codegen_backend`,
@ -321,7 +334,7 @@ fn get_codegen_sysroot(maybe_sysroot: &Option<PathBuf>, backend_name: &str) -> M
"failed to find a `codegen-backends` folder \ "failed to find a `codegen-backends` folder \
in the sysroot candidates:\n* {candidates}" in the sysroot candidates:\n* {candidates}"
); );
early_error(ErrorOutputType::default(), err); handler.early_error(err);
}); });
info!("probing {} for a codegen backend", sysroot.display()); info!("probing {} for a codegen backend", sysroot.display());
@ -332,7 +345,7 @@ fn get_codegen_sysroot(maybe_sysroot: &Option<PathBuf>, backend_name: &str) -> M
sysroot.display(), sysroot.display(),
e e
); );
early_error(ErrorOutputType::default(), err); handler.early_error(err);
}); });
let mut file: Option<PathBuf> = None; let mut file: Option<PathBuf> = None;
@ -360,16 +373,16 @@ fn get_codegen_sysroot(maybe_sysroot: &Option<PathBuf>, backend_name: &str) -> M
prev.display(), prev.display(),
path.display() path.display()
); );
early_error(ErrorOutputType::default(), err); handler.early_error(err);
} }
file = Some(path.clone()); file = Some(path.clone());
} }
match file { match file {
Some(ref s) => load_backend_from_dylib(s), Some(ref s) => load_backend_from_dylib(handler, s),
None => { None => {
let err = format!("unsupported builtin codegen backend `{backend_name}`"); let err = format!("unsupported builtin codegen backend `{backend_name}`");
early_error(ErrorOutputType::default(), err); handler.early_error(err);
} }
} }
} }

View File

@ -5,8 +5,8 @@ pub use crate::options::*;
use crate::search_paths::SearchPath; use crate::search_paths::SearchPath;
use crate::utils::{CanonicalizedPath, NativeLib, NativeLibKind}; use crate::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
use crate::{early_error, early_warn, Session};
use crate::{lint, HashStableContext}; use crate::{lint, HashStableContext};
use crate::{EarlyErrorHandler, Session};
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@ -1389,6 +1389,7 @@ pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateCo
} }
pub(super) fn build_target_config( pub(super) fn build_target_config(
handler: &EarlyErrorHandler,
opts: &Options, opts: &Options,
target_override: Option<Target>, target_override: Option<Target>,
sysroot: &Path, sysroot: &Path,
@ -1398,27 +1399,21 @@ pub(super) fn build_target_config(
|t| Ok((t, TargetWarnings::empty())), |t| Ok((t, TargetWarnings::empty())),
); );
let (target, target_warnings) = target_result.unwrap_or_else(|e| { let (target, target_warnings) = target_result.unwrap_or_else(|e| {
early_error( handler.early_error(format!(
opts.error_format, "Error loading target specification: {}. \
format!(
"Error loading target specification: {}. \
Run `rustc --print target-list` for a list of built-in targets", Run `rustc --print target-list` for a list of built-in targets",
e e
), ))
)
}); });
for warning in target_warnings.warning_messages() { for warning in target_warnings.warning_messages() {
early_warn(opts.error_format, warning) handler.early_warn(warning)
} }
if !matches!(target.pointer_width, 16 | 32 | 64) { if !matches!(target.pointer_width, 16 | 32 | 64) {
early_error( handler.early_error(format!(
opts.error_format, "target specification was invalid: unrecognized target-pointer-width {}",
format!( target.pointer_width
"target specification was invalid: unrecognized target-pointer-width {}", ))
target.pointer_width
),
)
} }
target target
@ -1654,8 +1649,8 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
} }
pub fn get_cmd_lint_options( pub fn get_cmd_lint_options(
handler: &EarlyErrorHandler,
matches: &getopts::Matches, matches: &getopts::Matches,
error_format: ErrorOutputType,
) -> (Vec<(String, lint::Level)>, bool, Option<lint::Level>) { ) -> (Vec<(String, lint::Level)>, bool, Option<lint::Level>) {
let mut lint_opts_with_position = vec![]; let mut lint_opts_with_position = vec![];
let mut describe_lints = false; let mut describe_lints = false;
@ -1679,14 +1674,14 @@ pub fn get_cmd_lint_options(
let lint_cap = matches.opt_str("cap-lints").map(|cap| { let lint_cap = matches.opt_str("cap-lints").map(|cap| {
lint::Level::from_str(&cap) lint::Level::from_str(&cap)
.unwrap_or_else(|| early_error(error_format, format!("unknown lint level: `{cap}`"))) .unwrap_or_else(|| handler.early_error(format!("unknown lint level: `{cap}`")))
}); });
(lint_opts, describe_lints, lint_cap) (lint_opts, describe_lints, lint_cap)
} }
/// Parses the `--color` flag. /// Parses the `--color` flag.
pub fn parse_color(matches: &getopts::Matches) -> ColorConfig { pub fn parse_color(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> ColorConfig {
match matches.opt_str("color").as_deref() { match matches.opt_str("color").as_deref() {
Some("auto") => ColorConfig::Auto, Some("auto") => ColorConfig::Auto,
Some("always") => ColorConfig::Always, Some("always") => ColorConfig::Always,
@ -1694,13 +1689,10 @@ pub fn parse_color(matches: &getopts::Matches) -> ColorConfig {
None => ColorConfig::Auto, None => ColorConfig::Auto,
Some(arg) => early_error( Some(arg) => handler.early_error(format!(
ErrorOutputType::default(), "argument for `--color` must be auto, \
format!(
"argument for `--color` must be auto, \
always or never (instead was `{arg}`)" always or never (instead was `{arg}`)"
), )),
),
} }
} }
@ -1743,7 +1735,7 @@ impl JsonUnusedExterns {
/// ///
/// The first value returned is how to render JSON diagnostics, and the second /// The first value returned is how to render JSON diagnostics, and the second
/// is whether or not artifact notifications are enabled. /// is whether or not artifact notifications are enabled.
pub fn parse_json(matches: &getopts::Matches) -> JsonConfig { pub fn parse_json(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> JsonConfig {
let mut json_rendered: fn(ColorConfig) -> HumanReadableErrorType = let mut json_rendered: fn(ColorConfig) -> HumanReadableErrorType =
HumanReadableErrorType::Default; HumanReadableErrorType::Default;
let mut json_color = ColorConfig::Never; let mut json_color = ColorConfig::Never;
@ -1755,10 +1747,7 @@ pub fn parse_json(matches: &getopts::Matches) -> JsonConfig {
// won't actually be emitting any colors and anything colorized is // won't actually be emitting any colors and anything colorized is
// embedded in a diagnostic message anyway. // embedded in a diagnostic message anyway.
if matches.opt_str("color").is_some() { if matches.opt_str("color").is_some() {
early_error( handler.early_error("cannot specify the `--color` option with `--json`");
ErrorOutputType::default(),
"cannot specify the `--color` option with `--json`",
);
} }
for sub_option in option.split(',') { for sub_option in option.split(',') {
@ -1769,10 +1758,7 @@ pub fn parse_json(matches: &getopts::Matches) -> JsonConfig {
"unused-externs" => json_unused_externs = JsonUnusedExterns::Loud, "unused-externs" => json_unused_externs = JsonUnusedExterns::Loud,
"unused-externs-silent" => json_unused_externs = JsonUnusedExterns::Silent, "unused-externs-silent" => json_unused_externs = JsonUnusedExterns::Silent,
"future-incompat" => json_future_incompat = true, "future-incompat" => json_future_incompat = true,
s => early_error( s => handler.early_error(format!("unknown `--json` option `{s}`")),
ErrorOutputType::default(),
format!("unknown `--json` option `{s}`"),
),
} }
} }
} }
@ -1787,6 +1773,7 @@ pub fn parse_json(matches: &getopts::Matches) -> JsonConfig {
/// Parses the `--error-format` flag. /// Parses the `--error-format` flag.
pub fn parse_error_format( pub fn parse_error_format(
handler: &mut EarlyErrorHandler,
matches: &getopts::Matches, matches: &getopts::Matches,
color: ColorConfig, color: ColorConfig,
json_rendered: HumanReadableErrorType, json_rendered: HumanReadableErrorType,
@ -1807,13 +1794,15 @@ pub fn parse_error_format(
Some("pretty-json") => ErrorOutputType::Json { pretty: true, json_rendered }, Some("pretty-json") => ErrorOutputType::Json { pretty: true, json_rendered },
Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short(color)), Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short(color)),
Some(arg) => early_error( Some(arg) => {
ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)), handler.abort_if_error_and_set_error_format(ErrorOutputType::HumanReadable(
format!( HumanReadableErrorType::Default(color),
));
handler.early_error(format!(
"argument for `--error-format` must be `human`, `json` or \ "argument for `--error-format` must be `human`, `json` or \
`short` (instead was `{arg}`)" `short` (instead was `{arg}`)"
), ))
), }
} }
} else { } else {
ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)) ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color))
@ -1826,10 +1815,7 @@ pub fn parse_error_format(
// `--error-format=json`. This means that `--json` is specified we // `--error-format=json`. This means that `--json` is specified we
// should actually be emitting JSON blobs. // should actually be emitting JSON blobs.
_ if !matches.opt_strs("json").is_empty() => { _ if !matches.opt_strs("json").is_empty() => {
early_error( handler.early_error("using `--json` requires also using `--error-format=json`");
ErrorOutputType::default(),
"using `--json` requires also using `--error-format=json`",
);
} }
_ => {} _ => {}
@ -1838,16 +1824,13 @@ pub fn parse_error_format(
error_format error_format
} }
pub fn parse_crate_edition(matches: &getopts::Matches) -> Edition { pub fn parse_crate_edition(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> Edition {
let edition = match matches.opt_str("edition") { let edition = match matches.opt_str("edition") {
Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| { Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| {
early_error( handler.early_error(format!(
ErrorOutputType::default(), "argument for `--edition` must be one of: \
format!(
"argument for `--edition` must be one of: \
{EDITION_NAME_LIST}. (instead was `{arg}`)" {EDITION_NAME_LIST}. (instead was `{arg}`)"
), ))
)
}), }),
None => DEFAULT_EDITION, None => DEFAULT_EDITION,
}; };
@ -1862,39 +1845,42 @@ pub fn parse_crate_edition(matches: &getopts::Matches) -> Edition {
} else { } else {
format!("edition {edition} is unstable and only available with -Z unstable-options") format!("edition {edition} is unstable and only available with -Z unstable-options")
}; };
early_error(ErrorOutputType::default(), msg) handler.early_error(msg)
} }
edition edition
} }
fn check_error_format_stability( fn check_error_format_stability(
handler: &mut EarlyErrorHandler,
unstable_opts: &UnstableOptions, unstable_opts: &UnstableOptions,
error_format: ErrorOutputType, error_format: ErrorOutputType,
json_rendered: HumanReadableErrorType, json_rendered: HumanReadableErrorType,
) { ) {
if !unstable_opts.unstable_options { if !unstable_opts.unstable_options {
if let ErrorOutputType::Json { pretty: true, json_rendered } = error_format { if let ErrorOutputType::Json { pretty: true, json_rendered } = error_format {
early_error( handler.abort_if_error_and_set_error_format(ErrorOutputType::Json {
ErrorOutputType::Json { pretty: false, json_rendered }, pretty: false,
"`--error-format=pretty-json` is unstable", json_rendered,
); });
handler.early_error("`--error-format=pretty-json` is unstable");
} }
if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(_)) = if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(_)) =
error_format error_format
{ {
early_error( handler.abort_if_error_and_set_error_format(ErrorOutputType::Json {
ErrorOutputType::Json { pretty: false, json_rendered }, pretty: false,
"`--error-format=human-annotate-rs` is unstable", json_rendered,
); });
handler.early_error("`--error-format=human-annotate-rs` is unstable");
} }
} }
} }
fn parse_output_types( fn parse_output_types(
handler: &EarlyErrorHandler,
unstable_opts: &UnstableOptions, unstable_opts: &UnstableOptions,
matches: &getopts::Matches, matches: &getopts::Matches,
error_format: ErrorOutputType,
) -> OutputTypes { ) -> OutputTypes {
let mut output_types = BTreeMap::new(); let mut output_types = BTreeMap::new();
if !unstable_opts.parse_only { if !unstable_opts.parse_only {
@ -1908,13 +1894,10 @@ fn parse_output_types(
} }
}; };
let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| { let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| {
early_error( handler.early_error(format!(
error_format, "unknown emission type: `{shorthand}` - expected one of: {display}",
format!( display = OutputType::shorthands_display(),
"unknown emission type: `{shorthand}` - expected one of: {display}", ))
display = OutputType::shorthands_display(),
),
)
}); });
output_types.insert(output_type, path); output_types.insert(output_type, path);
} }
@ -1927,9 +1910,9 @@ fn parse_output_types(
} }
fn should_override_cgus_and_disable_thinlto( fn should_override_cgus_and_disable_thinlto(
handler: &EarlyErrorHandler,
output_types: &OutputTypes, output_types: &OutputTypes,
matches: &getopts::Matches, matches: &getopts::Matches,
error_format: ErrorOutputType,
mut codegen_units: Option<usize>, mut codegen_units: Option<usize>,
) -> (bool, Option<usize>) { ) -> (bool, Option<usize>) {
let mut disable_local_thinlto = false; let mut disable_local_thinlto = false;
@ -1947,15 +1930,12 @@ fn should_override_cgus_and_disable_thinlto(
Some(n) if n > 1 => { Some(n) if n > 1 => {
if matches.opt_present("o") { if matches.opt_present("o") {
for ot in &incompatible { for ot in &incompatible {
early_warn( handler.early_warn(format!(
error_format, "`--emit={ot}` with `-o` incompatible with \
format!(
"`--emit={ot}` with `-o` incompatible with \
`-C codegen-units=N` for N > 1", `-C codegen-units=N` for N > 1",
), ));
);
} }
early_warn(error_format, "resetting to default -C codegen-units=1"); handler.early_warn("resetting to default -C codegen-units=1");
codegen_units = Some(1); codegen_units = Some(1);
disable_local_thinlto = true; disable_local_thinlto = true;
} }
@ -1968,27 +1948,27 @@ fn should_override_cgus_and_disable_thinlto(
} }
if codegen_units == Some(0) { if codegen_units == Some(0) {
early_error(error_format, "value for codegen units must be a positive non-zero integer"); handler.early_error("value for codegen units must be a positive non-zero integer");
} }
(disable_local_thinlto, codegen_units) (disable_local_thinlto, codegen_units)
} }
fn check_thread_count(unstable_opts: &UnstableOptions, error_format: ErrorOutputType) { fn check_thread_count(handler: &EarlyErrorHandler, unstable_opts: &UnstableOptions) {
if unstable_opts.threads == 0 { if unstable_opts.threads == 0 {
early_error(error_format, "value for threads must be a positive non-zero integer"); handler.early_error("value for threads must be a positive non-zero integer");
} }
if unstable_opts.threads > 1 && unstable_opts.fuel.is_some() { if unstable_opts.threads > 1 && unstable_opts.fuel.is_some() {
early_error(error_format, "optimization fuel is incompatible with multiple threads"); handler.early_error("optimization fuel is incompatible with multiple threads");
} }
} }
fn collect_print_requests( fn collect_print_requests(
handler: &EarlyErrorHandler,
cg: &mut CodegenOptions, cg: &mut CodegenOptions,
unstable_opts: &mut UnstableOptions, unstable_opts: &mut UnstableOptions,
matches: &getopts::Matches, matches: &getopts::Matches,
error_format: ErrorOutputType,
) -> Vec<PrintRequest> { ) -> Vec<PrintRequest> {
let mut prints = Vec::<PrintRequest>::new(); let mut prints = Vec::<PrintRequest>::new();
if cg.target_cpu.as_ref().is_some_and(|s| s == "help") { if cg.target_cpu.as_ref().is_some_and(|s| s == "help") {
@ -2028,8 +2008,7 @@ fn collect_print_requests(
if unstable_opts.unstable_options { if unstable_opts.unstable_options {
PrintRequest::TargetSpec PrintRequest::TargetSpec
} else { } else {
early_error( handler.early_error(
error_format,
"the `-Z unstable-options` flag must also be passed to \ "the `-Z unstable-options` flag must also be passed to \
enable the target-spec-json print option", enable the target-spec-json print option",
); );
@ -2039,8 +2018,7 @@ fn collect_print_requests(
if unstable_opts.unstable_options { if unstable_opts.unstable_options {
PrintRequest::AllTargetSpecs PrintRequest::AllTargetSpecs
} else { } else {
early_error( handler.early_error(
error_format,
"the `-Z unstable-options` flag must also be passed to \ "the `-Z unstable-options` flag must also be passed to \
enable the all-target-specs-json print option", enable the all-target-specs-json print option",
); );
@ -2051,10 +2029,9 @@ fn collect_print_requests(
let prints = let prints =
PRINT_REQUESTS.iter().map(|(name, _)| format!("`{name}`")).collect::<Vec<_>>(); PRINT_REQUESTS.iter().map(|(name, _)| format!("`{name}`")).collect::<Vec<_>>();
let prints = prints.join(", "); let prints = prints.join(", ");
early_error( handler.early_error(format!(
error_format, "unknown print request `{req}`. Valid print requests are: {prints}"
format!("unknown print request `{req}`. Valid print requests are: {prints}"), ));
);
} }
} }
})); }));
@ -2063,14 +2040,14 @@ fn collect_print_requests(
} }
pub fn parse_target_triple( pub fn parse_target_triple(
handler: &EarlyErrorHandler,
matches: &getopts::Matches, matches: &getopts::Matches,
error_format: ErrorOutputType,
) -> TargetTriple { ) -> TargetTriple {
match matches.opt_str("target") { match matches.opt_str("target") {
Some(target) if target.ends_with(".json") => { Some(target) if target.ends_with(".json") => {
let path = Path::new(&target); let path = Path::new(&target);
TargetTriple::from_path(path).unwrap_or_else(|_| { TargetTriple::from_path(path).unwrap_or_else(|_| {
early_error(error_format, format!("target file {path:?} does not exist")) handler.early_error(format!("target file {path:?} does not exist"))
}) })
} }
Some(target) => TargetTriple::TargetTriple(target), Some(target) => TargetTriple::TargetTriple(target),
@ -2079,9 +2056,9 @@ pub fn parse_target_triple(
} }
fn parse_opt_level( fn parse_opt_level(
handler: &EarlyErrorHandler,
matches: &getopts::Matches, matches: &getopts::Matches,
cg: &CodegenOptions, cg: &CodegenOptions,
error_format: ErrorOutputType,
) -> OptLevel { ) -> OptLevel {
// The `-O` and `-C opt-level` flags specify the same setting, so we want to be able // The `-O` and `-C opt-level` flags specify the same setting, so we want to be able
// to use them interchangeably. However, because they're technically different flags, // to use them interchangeably. However, because they're technically different flags,
@ -2109,13 +2086,10 @@ fn parse_opt_level(
"s" => OptLevel::Size, "s" => OptLevel::Size,
"z" => OptLevel::SizeMin, "z" => OptLevel::SizeMin,
arg => { arg => {
early_error( handler.early_error(format!(
error_format, "optimization level needs to be \
format!(
"optimization level needs to be \
between 0-3, s or z (instead was `{arg}`)" between 0-3, s or z (instead was `{arg}`)"
), ));
);
} }
} }
} }
@ -2135,23 +2109,23 @@ fn select_debuginfo(matches: &getopts::Matches, cg: &CodegenOptions) -> DebugInf
} }
pub(crate) fn parse_assert_incr_state( pub(crate) fn parse_assert_incr_state(
handler: &EarlyErrorHandler,
opt_assertion: &Option<String>, opt_assertion: &Option<String>,
error_format: ErrorOutputType,
) -> Option<IncrementalStateAssertion> { ) -> Option<IncrementalStateAssertion> {
match opt_assertion { match opt_assertion {
Some(s) if s.as_str() == "loaded" => Some(IncrementalStateAssertion::Loaded), Some(s) if s.as_str() == "loaded" => Some(IncrementalStateAssertion::Loaded),
Some(s) if s.as_str() == "not-loaded" => Some(IncrementalStateAssertion::NotLoaded), Some(s) if s.as_str() == "not-loaded" => Some(IncrementalStateAssertion::NotLoaded),
Some(s) => { Some(s) => {
early_error(error_format, format!("unexpected incremental state assertion value: {s}")) handler.early_error(format!("unexpected incremental state assertion value: {s}"))
} }
None => None, None => None,
} }
} }
fn parse_native_lib_kind( fn parse_native_lib_kind(
handler: &EarlyErrorHandler,
matches: &getopts::Matches, matches: &getopts::Matches,
kind: &str, kind: &str,
error_format: ErrorOutputType,
) -> (NativeLibKind, Option<bool>) { ) -> (NativeLibKind, Option<bool>) {
let (kind, modifiers) = match kind.split_once(':') { let (kind, modifiers) = match kind.split_once(':') {
None => (kind, None), None => (kind, None),
@ -2169,35 +2143,31 @@ fn parse_native_lib_kind(
} else { } else {
", the `-Z unstable-options` flag must also be passed to use it" ", the `-Z unstable-options` flag must also be passed to use it"
}; };
early_error(error_format, format!("library kind `link-arg` is unstable{why}")) handler.early_error(format!("library kind `link-arg` is unstable{why}"))
} }
NativeLibKind::LinkArg NativeLibKind::LinkArg
} }
_ => early_error( _ => handler.early_error(format!(
error_format, "unknown library kind `{kind}`, expected one of: static, dylib, framework, link-arg"
format!( )),
"unknown library kind `{kind}`, expected one of: static, dylib, framework, link-arg"
),
),
}; };
match modifiers { match modifiers {
None => (kind, None), None => (kind, None),
Some(modifiers) => parse_native_lib_modifiers(kind, modifiers, error_format, matches), Some(modifiers) => parse_native_lib_modifiers(handler, kind, modifiers, matches),
} }
} }
fn parse_native_lib_modifiers( fn parse_native_lib_modifiers(
handler: &EarlyErrorHandler,
mut kind: NativeLibKind, mut kind: NativeLibKind,
modifiers: &str, modifiers: &str,
error_format: ErrorOutputType,
matches: &getopts::Matches, matches: &getopts::Matches,
) -> (NativeLibKind, Option<bool>) { ) -> (NativeLibKind, Option<bool>) {
let mut verbatim = None; let mut verbatim = None;
for modifier in modifiers.split(',') { for modifier in modifiers.split(',') {
let (modifier, value) = match modifier.strip_prefix(['+', '-']) { let (modifier, value) = match modifier.strip_prefix(['+', '-']) {
Some(m) => (m, modifier.starts_with('+')), Some(m) => (m, modifier.starts_with('+')),
None => early_error( None => handler.early_error(
error_format,
"invalid linking modifier syntax, expected '+' or '-' prefix \ "invalid linking modifier syntax, expected '+' or '-' prefix \
before one of: bundle, verbatim, whole-archive, as-needed", before one of: bundle, verbatim, whole-archive, as-needed",
), ),
@ -2210,21 +2180,20 @@ fn parse_native_lib_modifiers(
} else { } else {
", the `-Z unstable-options` flag must also be passed to use it" ", the `-Z unstable-options` flag must also be passed to use it"
}; };
early_error(error_format, format!("linking modifier `{modifier}` is unstable{why}")) handler.early_error(format!("linking modifier `{modifier}` is unstable{why}"))
} }
}; };
let assign_modifier = |dst: &mut Option<bool>| { let assign_modifier = |dst: &mut Option<bool>| {
if dst.is_some() { if dst.is_some() {
let msg = format!("multiple `{modifier}` modifiers in a single `-l` option"); let msg = format!("multiple `{modifier}` modifiers in a single `-l` option");
early_error(error_format, msg) handler.early_error(msg)
} else { } else {
*dst = Some(value); *dst = Some(value);
} }
}; };
match (modifier, &mut kind) { match (modifier, &mut kind) {
("bundle", NativeLibKind::Static { bundle, .. }) => assign_modifier(bundle), ("bundle", NativeLibKind::Static { bundle, .. }) => assign_modifier(bundle),
("bundle", _) => early_error( ("bundle", _) => handler.early_error(
error_format,
"linking modifier `bundle` is only compatible with `static` linking kind", "linking modifier `bundle` is only compatible with `static` linking kind",
), ),
@ -2233,8 +2202,7 @@ fn parse_native_lib_modifiers(
("whole-archive", NativeLibKind::Static { whole_archive, .. }) => { ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => {
assign_modifier(whole_archive) assign_modifier(whole_archive)
} }
("whole-archive", _) => early_error( ("whole-archive", _) => handler.early_error(
error_format,
"linking modifier `whole-archive` is only compatible with `static` linking kind", "linking modifier `whole-archive` is only compatible with `static` linking kind",
), ),
@ -2243,28 +2211,24 @@ fn parse_native_lib_modifiers(
report_unstable_modifier(); report_unstable_modifier();
assign_modifier(as_needed) assign_modifier(as_needed)
} }
("as-needed", _) => early_error( ("as-needed", _) => handler.early_error(
error_format,
"linking modifier `as-needed` is only compatible with \ "linking modifier `as-needed` is only compatible with \
`dylib` and `framework` linking kinds", `dylib` and `framework` linking kinds",
), ),
// Note: this error also excludes the case with empty modifier // Note: this error also excludes the case with empty modifier
// string, like `modifiers = ""`. // string, like `modifiers = ""`.
_ => early_error( _ => handler.early_error(format!(
error_format, "unknown linking modifier `{modifier}`, expected one \
format!(
"unknown linking modifier `{modifier}`, expected one \
of: bundle, verbatim, whole-archive, as-needed" of: bundle, verbatim, whole-archive, as-needed"
), )),
),
} }
} }
(kind, verbatim) (kind, verbatim)
} }
fn parse_libs(matches: &getopts::Matches, error_format: ErrorOutputType) -> Vec<NativeLib> { fn parse_libs(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> Vec<NativeLib> {
matches matches
.opt_strs("l") .opt_strs("l")
.into_iter() .into_iter()
@ -2278,7 +2242,7 @@ fn parse_libs(matches: &getopts::Matches, error_format: ErrorOutputType) -> Vec<
let (name, kind, verbatim) = match s.split_once('=') { let (name, kind, verbatim) = match s.split_once('=') {
None => (s, NativeLibKind::Unspecified, None), None => (s, NativeLibKind::Unspecified, None),
Some((kind, name)) => { Some((kind, name)) => {
let (kind, verbatim) = parse_native_lib_kind(matches, kind, error_format); let (kind, verbatim) = parse_native_lib_kind(handler, matches, kind);
(name.to_string(), kind, verbatim) (name.to_string(), kind, verbatim)
} }
}; };
@ -2288,7 +2252,7 @@ fn parse_libs(matches: &getopts::Matches, error_format: ErrorOutputType) -> Vec<
Some((name, new_name)) => (name.to_string(), Some(new_name.to_owned())), Some((name, new_name)) => (name.to_string(), Some(new_name.to_owned())),
}; };
if name.is_empty() { if name.is_empty() {
early_error(error_format, "library name must not be empty"); handler.early_error("library name must not be empty");
} }
NativeLib { name, new_name, kind, verbatim } NativeLib { name, new_name, kind, verbatim }
}) })
@ -2296,9 +2260,9 @@ fn parse_libs(matches: &getopts::Matches, error_format: ErrorOutputType) -> Vec<
} }
pub fn parse_externs( pub fn parse_externs(
handler: &EarlyErrorHandler,
matches: &getopts::Matches, matches: &getopts::Matches,
unstable_opts: &UnstableOptions, unstable_opts: &UnstableOptions,
error_format: ErrorOutputType,
) -> Externs { ) -> Externs {
let is_unstable_enabled = unstable_opts.unstable_options; let is_unstable_enabled = unstable_opts.unstable_options;
let mut externs: BTreeMap<String, ExternEntry> = BTreeMap::new(); let mut externs: BTreeMap<String, ExternEntry> = BTreeMap::new();
@ -2362,8 +2326,7 @@ pub fn parse_externs(
let mut force = false; let mut force = false;
if let Some(opts) = options { if let Some(opts) = options {
if !is_unstable_enabled { if !is_unstable_enabled {
early_error( handler.early_error(
error_format,
"the `-Z unstable-options` flag must also be passed to \ "the `-Z unstable-options` flag must also be passed to \
enable `--extern` options", enable `--extern` options",
); );
@ -2375,15 +2338,14 @@ pub fn parse_externs(
if let ExternLocation::ExactPaths(_) = &entry.location { if let ExternLocation::ExactPaths(_) = &entry.location {
add_prelude = false; add_prelude = false;
} else { } else {
early_error( handler.early_error(
error_format,
"the `noprelude` --extern option requires a file path", "the `noprelude` --extern option requires a file path",
); );
} }
} }
"nounused" => nounused_dep = true, "nounused" => nounused_dep = true,
"force" => force = true, "force" => force = true,
_ => early_error(error_format, format!("unknown --extern option `{opt}`")), _ => handler.early_error(format!("unknown --extern option `{opt}`")),
} }
} }
} }
@ -2402,18 +2364,15 @@ pub fn parse_externs(
} }
fn parse_remap_path_prefix( fn parse_remap_path_prefix(
handler: &EarlyErrorHandler,
matches: &getopts::Matches, matches: &getopts::Matches,
unstable_opts: &UnstableOptions, unstable_opts: &UnstableOptions,
error_format: ErrorOutputType,
) -> Vec<(PathBuf, PathBuf)> { ) -> Vec<(PathBuf, PathBuf)> {
let mut mapping: Vec<(PathBuf, PathBuf)> = matches let mut mapping: Vec<(PathBuf, PathBuf)> = matches
.opt_strs("remap-path-prefix") .opt_strs("remap-path-prefix")
.into_iter() .into_iter()
.map(|remap| match remap.rsplit_once('=') { .map(|remap| match remap.rsplit_once('=') {
None => early_error( None => handler.early_error("--remap-path-prefix must contain '=' between FROM and TO"),
error_format,
"--remap-path-prefix must contain '=' between FROM and TO",
),
Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)), Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)),
}) })
.collect(); .collect();
@ -2429,86 +2388,75 @@ fn parse_remap_path_prefix(
// JUSTIFICATION: before wrapper fn is available // JUSTIFICATION: before wrapper fn is available
#[allow(rustc::bad_opt_access)] #[allow(rustc::bad_opt_access)]
pub fn build_session_options(matches: &getopts::Matches) -> Options { pub fn build_session_options(
let color = parse_color(matches); handler: &mut EarlyErrorHandler,
matches: &getopts::Matches,
) -> Options {
let color = parse_color(handler, matches);
let edition = parse_crate_edition(matches); let edition = parse_crate_edition(handler, matches);
let JsonConfig { let JsonConfig {
json_rendered, json_rendered,
json_artifact_notifications, json_artifact_notifications,
json_unused_externs, json_unused_externs,
json_future_incompat, json_future_incompat,
} = parse_json(matches); } = parse_json(handler, matches);
let error_format = parse_error_format(matches, color, json_rendered); let error_format = parse_error_format(handler, matches, color, json_rendered);
let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_else(|_| { let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_else(|_| {
early_error(error_format, "`--diagnostic-width` must be an positive integer"); handler.early_error("`--diagnostic-width` must be an positive integer");
}); });
let unparsed_crate_types = matches.opt_strs("crate-type"); let unparsed_crate_types = matches.opt_strs("crate-type");
let crate_types = parse_crate_types_from_list(unparsed_crate_types) let crate_types = parse_crate_types_from_list(unparsed_crate_types)
.unwrap_or_else(|e| early_error(error_format, e)); .unwrap_or_else(|e| handler.early_error(e));
let mut unstable_opts = UnstableOptions::build(matches, error_format); let mut unstable_opts = UnstableOptions::build(handler, matches);
let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format); let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(handler, matches);
check_error_format_stability(&unstable_opts, error_format, json_rendered); check_error_format_stability(handler, &unstable_opts, error_format, json_rendered);
if !unstable_opts.unstable_options && json_unused_externs.is_enabled() { if !unstable_opts.unstable_options && json_unused_externs.is_enabled() {
early_error( handler.early_error(
error_format,
"the `-Z unstable-options` flag must also be passed to enable \ "the `-Z unstable-options` flag must also be passed to enable \
the flag `--json=unused-externs`", the flag `--json=unused-externs`",
); );
} }
let output_types = parse_output_types(&unstable_opts, matches, error_format); let output_types = parse_output_types(handler, &unstable_opts, matches);
let mut cg = CodegenOptions::build(matches, error_format); let mut cg = CodegenOptions::build(handler, matches);
let (disable_local_thinlto, mut codegen_units) = should_override_cgus_and_disable_thinlto( let (disable_local_thinlto, mut codegen_units) =
&output_types, should_override_cgus_and_disable_thinlto(handler, &output_types, matches, cg.codegen_units);
matches,
error_format,
cg.codegen_units,
);
check_thread_count(&unstable_opts, error_format); check_thread_count(handler, &unstable_opts);
let incremental = cg.incremental.as_ref().map(PathBuf::from); let incremental = cg.incremental.as_ref().map(PathBuf::from);
let assert_incr_state = parse_assert_incr_state(&unstable_opts.assert_incr_state, error_format); let assert_incr_state = parse_assert_incr_state(handler, &unstable_opts.assert_incr_state);
if unstable_opts.profile && incremental.is_some() { if unstable_opts.profile && incremental.is_some() {
early_error( handler.early_error("can't instrument with gcov profiling when compiling incrementally");
error_format,
"can't instrument with gcov profiling when compiling incrementally",
);
} }
if unstable_opts.profile { if unstable_opts.profile {
match codegen_units { match codegen_units {
Some(1) => {} Some(1) => {}
None => codegen_units = Some(1), None => codegen_units = Some(1),
Some(_) => early_error( Some(_) => handler
error_format, .early_error("can't instrument with gcov profiling with multiple codegen units"),
"can't instrument with gcov profiling with multiple codegen units",
),
} }
} }
if cg.profile_generate.enabled() && cg.profile_use.is_some() { if cg.profile_generate.enabled() && cg.profile_use.is_some() {
early_error( handler.early_error("options `-C profile-generate` and `-C profile-use` are exclusive");
error_format,
"options `-C profile-generate` and `-C profile-use` are exclusive",
);
} }
if unstable_opts.profile_sample_use.is_some() if unstable_opts.profile_sample_use.is_some()
&& (cg.profile_generate.enabled() || cg.profile_use.is_some()) && (cg.profile_generate.enabled() || cg.profile_use.is_some())
{ {
early_error( handler.early_error(
error_format,
"option `-Z profile-sample-use` cannot be used with `-C profile-generate` or `-C profile-use`", "option `-Z profile-sample-use` cannot be used with `-C profile-generate` or `-C profile-use`",
); );
} }
@ -2517,23 +2465,19 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
// precedence. // precedence.
match (cg.symbol_mangling_version, unstable_opts.symbol_mangling_version) { match (cg.symbol_mangling_version, unstable_opts.symbol_mangling_version) {
(Some(smv_c), Some(smv_z)) if smv_c != smv_z => { (Some(smv_c), Some(smv_z)) if smv_c != smv_z => {
early_error( handler.early_error(
error_format,
"incompatible values passed for `-C symbol-mangling-version` \ "incompatible values passed for `-C symbol-mangling-version` \
and `-Z symbol-mangling-version`", and `-Z symbol-mangling-version`",
); );
} }
(Some(SymbolManglingVersion::V0), _) => {} (Some(SymbolManglingVersion::V0), _) => {}
(Some(_), _) if !unstable_opts.unstable_options => { (Some(_), _) if !unstable_opts.unstable_options => {
early_error( handler
error_format, .early_error("`-C symbol-mangling-version=legacy` requires `-Z unstable-options`");
"`-C symbol-mangling-version=legacy` requires `-Z unstable-options`",
);
} }
(None, None) => {} (None, None) => {}
(None, smv) => { (None, smv) => {
early_warn( handler.early_warn(
error_format,
"`-Z symbol-mangling-version` is deprecated; use `-C symbol-mangling-version`", "`-Z symbol-mangling-version` is deprecated; use `-C symbol-mangling-version`",
); );
cg.symbol_mangling_version = smv; cg.symbol_mangling_version = smv;
@ -2545,25 +2489,19 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
// precedence. // precedence.
match (cg.instrument_coverage, unstable_opts.instrument_coverage) { match (cg.instrument_coverage, unstable_opts.instrument_coverage) {
(Some(ic_c), Some(ic_z)) if ic_c != ic_z => { (Some(ic_c), Some(ic_z)) if ic_c != ic_z => {
early_error( handler.early_error(
error_format,
"incompatible values passed for `-C instrument-coverage` \ "incompatible values passed for `-C instrument-coverage` \
and `-Z instrument-coverage`", and `-Z instrument-coverage`",
); );
} }
(Some(InstrumentCoverage::Off | InstrumentCoverage::All), _) => {} (Some(InstrumentCoverage::Off | InstrumentCoverage::All), _) => {}
(Some(_), _) if !unstable_opts.unstable_options => { (Some(_), _) if !unstable_opts.unstable_options => {
early_error( handler.early_error("`-C instrument-coverage=except-*` requires `-Z unstable-options`");
error_format,
"`-C instrument-coverage=except-*` requires `-Z unstable-options`",
);
} }
(None, None) => {} (None, None) => {}
(None, ic) => { (None, ic) => {
early_warn( handler
error_format, .early_warn("`-Z instrument-coverage` is deprecated; use `-C instrument-coverage`");
"`-Z instrument-coverage` is deprecated; use `-C instrument-coverage`",
);
cg.instrument_coverage = ic; cg.instrument_coverage = ic;
} }
_ => {} _ => {}
@ -2571,8 +2509,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
if cg.instrument_coverage.is_some() && cg.instrument_coverage != Some(InstrumentCoverage::Off) { if cg.instrument_coverage.is_some() && cg.instrument_coverage != Some(InstrumentCoverage::Off) {
if cg.profile_generate.enabled() || cg.profile_use.is_some() { if cg.profile_generate.enabled() || cg.profile_use.is_some() {
early_error( handler.early_error(
error_format,
"option `-C instrument-coverage` is not compatible with either `-C profile-use` \ "option `-C instrument-coverage` is not compatible with either `-C profile-use` \
or `-C profile-generate`", or `-C profile-generate`",
); );
@ -2585,8 +2522,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
match cg.symbol_mangling_version { match cg.symbol_mangling_version {
None => cg.symbol_mangling_version = Some(SymbolManglingVersion::V0), None => cg.symbol_mangling_version = Some(SymbolManglingVersion::V0),
Some(SymbolManglingVersion::Legacy) => { Some(SymbolManglingVersion::Legacy) => {
early_warn( handler.early_warn(
error_format,
"-C instrument-coverage requires symbol mangling version `v0`, \ "-C instrument-coverage requires symbol mangling version `v0`, \
but `-C symbol-mangling-version=legacy` was specified", but `-C symbol-mangling-version=legacy` was specified",
); );
@ -2602,10 +2538,9 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
if !cg.embed_bitcode { if !cg.embed_bitcode {
match cg.lto { match cg.lto {
LtoCli::No | LtoCli::Unspecified => {} LtoCli::No | LtoCli::Unspecified => {}
LtoCli::Yes | LtoCli::NoParam | LtoCli::Thin | LtoCli::Fat => early_error( LtoCli::Yes | LtoCli::NoParam | LtoCli::Thin | LtoCli::Fat => {
error_format, handler.early_error("options `-C embed-bitcode=no` and `-C lto` are incompatible")
"options `-C embed-bitcode=no` and `-C lto` are incompatible", }
),
} }
} }
@ -2618,17 +2553,17 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
flag must also be passed to explicitly use it", flag must also be passed to explicitly use it",
flavor.desc() flavor.desc()
); );
early_error(error_format, msg); handler.early_error(msg);
} }
} }
let prints = collect_print_requests(&mut cg, &mut unstable_opts, matches, error_format); let prints = collect_print_requests(handler, &mut cg, &mut unstable_opts, matches);
let cg = cg; let cg = cg;
let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m)); let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m));
let target_triple = parse_target_triple(matches, error_format); let target_triple = parse_target_triple(handler, matches);
let opt_level = parse_opt_level(matches, &cg, error_format); let opt_level = parse_opt_level(handler, matches, &cg);
// The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able // The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able
// to use them interchangeably. See the note above (regarding `-O` and `-C opt-level`) // to use them interchangeably. See the note above (regarding `-O` and `-C opt-level`)
// for more details. // for more details.
@ -2637,28 +2572,28 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
let mut search_paths = vec![]; let mut search_paths = vec![];
for s in &matches.opt_strs("L") { for s in &matches.opt_strs("L") {
search_paths.push(SearchPath::from_cli_opt(s, error_format)); search_paths.push(SearchPath::from_cli_opt(handler, s));
} }
let libs = parse_libs(matches, error_format); let libs = parse_libs(handler, matches);
let test = matches.opt_present("test"); let test = matches.opt_present("test");
if !cg.remark.is_empty() && debuginfo == DebugInfo::None { if !cg.remark.is_empty() && debuginfo == DebugInfo::None {
early_warn(error_format, "-C remark requires \"-C debuginfo=n\" to show source locations"); handler.early_warn("-C remark requires \"-C debuginfo=n\" to show source locations");
} }
let externs = parse_externs(matches, &unstable_opts, error_format); let externs = parse_externs(handler, matches, &unstable_opts);
let crate_name = matches.opt_str("crate-name"); let crate_name = matches.opt_str("crate-name");
let remap_path_prefix = parse_remap_path_prefix(matches, &unstable_opts, error_format); let remap_path_prefix = parse_remap_path_prefix(handler, matches, &unstable_opts);
let pretty = parse_pretty(&unstable_opts, error_format); let pretty = parse_pretty(handler, &unstable_opts);
// query-dep-graph is required if dump-dep-graph is given #106736 // query-dep-graph is required if dump-dep-graph is given #106736
if unstable_opts.dump_dep_graph && !unstable_opts.query_dep_graph { if unstable_opts.dump_dep_graph && !unstable_opts.query_dep_graph {
early_error(error_format, "can't dump dependency graph without `-Z query-dep-graph`"); handler.early_error("can't dump dependency graph without `-Z query-dep-graph`");
} }
// Try to find a directory containing the Rust `src`, for more details see // Try to find a directory containing the Rust `src`, for more details see
@ -2690,7 +2625,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
}; };
let working_dir = std::env::current_dir().unwrap_or_else(|e| { let working_dir = std::env::current_dir().unwrap_or_else(|e| {
early_error(error_format, format!("Current directory is invalid: {e}")); handler.early_error(format!("Current directory is invalid: {e}"));
}); });
let remap = FilePathMapping::new(remap_path_prefix.clone()); let remap = FilePathMapping::new(remap_path_prefix.clone());
@ -2741,7 +2676,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
} }
} }
fn parse_pretty(unstable_opts: &UnstableOptions, efmt: ErrorOutputType) -> Option<PpMode> { fn parse_pretty(handler: &EarlyErrorHandler, unstable_opts: &UnstableOptions) -> Option<PpMode> {
use PpMode::*; use PpMode::*;
let first = match unstable_opts.unpretty.as_deref()? { let first = match unstable_opts.unpretty.as_deref()? {
@ -2760,16 +2695,13 @@ fn parse_pretty(unstable_opts: &UnstableOptions, efmt: ErrorOutputType) -> Optio
"thir-flat" => ThirFlat, "thir-flat" => ThirFlat,
"mir" => Mir, "mir" => Mir,
"mir-cfg" => MirCFG, "mir-cfg" => MirCFG,
name => early_error( name => handler.early_error(format!(
efmt, "argument to `unpretty` must be one of `normal`, `identified`, \
format!(
"argument to `unpretty` must be one of `normal`, `identified`, \
`expanded`, `expanded,identified`, `expanded,hygiene`, \ `expanded`, `expanded,identified`, `expanded,hygiene`, \
`ast-tree`, `ast-tree,expanded`, `hir`, `hir,identified`, \ `ast-tree`, `ast-tree,expanded`, `hir`, `hir,identified`, \
`hir,typed`, `hir-tree`, `thir-tree`, `thir-flat`, `mir` or \ `hir,typed`, `hir-tree`, `thir-tree`, `thir-flat`, `mir` or \
`mir-cfg`; got {name}" `mir-cfg`; got {name}"
), )),
),
}; };
debug!("got unpretty option: {first:?}"); debug!("got unpretty option: {first:?}");
Some(first) Some(first)
@ -2809,8 +2741,8 @@ pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateTy
} }
pub mod nightly_options { pub mod nightly_options {
use super::{ErrorOutputType, OptionStability, RustcOptGroup}; use super::{OptionStability, RustcOptGroup};
use crate::early_error; use crate::EarlyErrorHandler;
use rustc_feature::UnstableFeatures; use rustc_feature::UnstableFeatures;
pub fn is_unstable_enabled(matches: &getopts::Matches) -> bool { pub fn is_unstable_enabled(matches: &getopts::Matches) -> bool {
@ -2826,7 +2758,11 @@ pub mod nightly_options {
UnstableFeatures::from_environment(krate).is_nightly_build() UnstableFeatures::from_environment(krate).is_nightly_build()
} }
pub fn check_nightly_options(matches: &getopts::Matches, flags: &[RustcOptGroup]) { pub fn check_nightly_options(
handler: &EarlyErrorHandler,
matches: &getopts::Matches,
flags: &[RustcOptGroup],
) {
let has_z_unstable_option = matches.opt_strs("Z").iter().any(|x| *x == "unstable-options"); let has_z_unstable_option = matches.opt_strs("Z").iter().any(|x| *x == "unstable-options");
let really_allows_unstable_options = match_is_nightly_build(matches); let really_allows_unstable_options = match_is_nightly_build(matches);
@ -2838,14 +2774,11 @@ pub mod nightly_options {
continue; continue;
} }
if opt.name != "Z" && !has_z_unstable_option { if opt.name != "Z" && !has_z_unstable_option {
early_error( handler.early_error(format!(
ErrorOutputType::default(), "the `-Z unstable-options` flag must also be passed to enable \
format!(
"the `-Z unstable-options` flag must also be passed to enable \
the flag `{}`", the flag `{}`",
opt.name opt.name
), ));
);
} }
if really_allows_unstable_options { if really_allows_unstable_options {
continue; continue;
@ -2856,7 +2789,12 @@ pub mod nightly_options {
"the option `{}` is only accepted on the nightly compiler", "the option `{}` is only accepted on the nightly compiler",
opt.name opt.name
); );
early_error(ErrorOutputType::default(), msg); let _ = handler.early_error_no_abort(msg);
handler.early_note("selecting a toolchain with `+toolchain` arguments require a rustup proxy; see <https://rust-lang.github.io/rustup/concepts/index.html>");
handler.early_help(
"consider switching to a nightly toolchain: `rustup default nightly`",
);
handler.early_note("for more information about Rust's stability policy, see <https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#unstable-features>");
} }
OptionStability::Stable => {} OptionStability::Stable => {}
} }

View File

@ -1,9 +1,8 @@
use crate::config::*; use crate::config::*;
use crate::early_error;
use crate::lint;
use crate::search_paths::SearchPath; use crate::search_paths::SearchPath;
use crate::utils::NativeLib; use crate::utils::NativeLib;
use crate::{lint, EarlyErrorHandler};
use rustc_data_structures::profiling::TimePassesFormat; use rustc_data_structures::profiling::TimePassesFormat;
use rustc_errors::{LanguageIdentifier, TerminalUrl}; use rustc_errors::{LanguageIdentifier, TerminalUrl};
use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, SanitizerSet}; use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, SanitizerSet};
@ -245,10 +244,10 @@ macro_rules! options {
impl $struct_name { impl $struct_name {
pub fn build( pub fn build(
handler: &EarlyErrorHandler,
matches: &getopts::Matches, matches: &getopts::Matches,
error_format: ErrorOutputType,
) -> $struct_name { ) -> $struct_name {
build_options(matches, $stat, $prefix, $outputname, error_format) build_options(handler, matches, $stat, $prefix, $outputname)
} }
fn dep_tracking_hash(&self, for_crate_hash: bool, error_format: ErrorOutputType) -> u64 { fn dep_tracking_hash(&self, for_crate_hash: bool, error_format: ErrorOutputType) -> u64 {
@ -309,11 +308,11 @@ type OptionSetter<O> = fn(&mut O, v: Option<&str>) -> bool;
type OptionDescrs<O> = &'static [(&'static str, OptionSetter<O>, &'static str, &'static str)]; type OptionDescrs<O> = &'static [(&'static str, OptionSetter<O>, &'static str, &'static str)];
fn build_options<O: Default>( fn build_options<O: Default>(
handler: &EarlyErrorHandler,
matches: &getopts::Matches, matches: &getopts::Matches,
descrs: OptionDescrs<O>, descrs: OptionDescrs<O>,
prefix: &str, prefix: &str,
outputname: &str, outputname: &str,
error_format: ErrorOutputType,
) -> O { ) -> O {
let mut op = O::default(); let mut op = O::default();
for option in matches.opt_strs(prefix) { for option in matches.opt_strs(prefix) {
@ -327,15 +326,13 @@ fn build_options<O: Default>(
Some((_, setter, type_desc, _)) => { Some((_, setter, type_desc, _)) => {
if !setter(&mut op, value) { if !setter(&mut op, value) {
match value { match value {
None => early_error( None => handler.early_error(
error_format,
format!( format!(
"{0} option `{1}` requires {2} ({3} {1}=<value>)", "{0} option `{1}` requires {2} ({3} {1}=<value>)",
outputname, key, type_desc, prefix outputname, key, type_desc, prefix
), ),
), ),
Some(value) => early_error( Some(value) => handler.early_error(
error_format,
format!( format!(
"incorrect value `{value}` for {outputname} option `{key}` - {type_desc} was expected" "incorrect value `{value}` for {outputname} option `{key}` - {type_desc} was expected"
), ),
@ -343,7 +340,7 @@ fn build_options<O: Default>(
} }
} }
} }
None => early_error(error_format, format!("unknown {outputname} option: `{key}`")), None => handler.early_error(format!("unknown {outputname} option: `{key}`")),
} }
} }
return op; return op;

View File

@ -1,5 +1,5 @@
use crate::filesearch::make_target_lib_path; use crate::filesearch::make_target_lib_path;
use crate::{config, early_error}; use crate::EarlyErrorHandler;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -46,7 +46,7 @@ impl PathKind {
} }
impl SearchPath { impl SearchPath {
pub fn from_cli_opt(path: &str, output: config::ErrorOutputType) -> Self { pub fn from_cli_opt(handler: &EarlyErrorHandler, path: &str) -> Self {
let (kind, path) = if let Some(stripped) = path.strip_prefix("native=") { let (kind, path) = if let Some(stripped) = path.strip_prefix("native=") {
(PathKind::Native, stripped) (PathKind::Native, stripped)
} else if let Some(stripped) = path.strip_prefix("crate=") { } else if let Some(stripped) = path.strip_prefix("crate=") {
@ -61,7 +61,7 @@ impl SearchPath {
(PathKind::All, path) (PathKind::All, path)
}; };
if path.is_empty() { if path.is_empty() {
early_error(output, "empty search path given via `-L`"); handler.early_error("empty search path given via `-L`");
} }
let dir = PathBuf::from(path); let dir = PathBuf::from(path);

View File

@ -1,10 +1,10 @@
use crate::cgu_reuse_tracker::CguReuseTracker; use crate::cgu_reuse_tracker::CguReuseTracker;
use crate::code_stats::CodeStats; use crate::code_stats::CodeStats;
pub use crate::code_stats::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo}; pub use crate::code_stats::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo};
use crate::config::Input;
use crate::config::{ use crate::config::{
self, CrateType, InstrumentCoverage, OptLevel, OutFileName, OutputType, SwitchWithOptPath, self, CrateType, InstrumentCoverage, OptLevel, OutFileName, OutputType, SwitchWithOptPath,
}; };
use crate::config::{ErrorOutputType, Input};
use crate::errors; use crate::errors;
use crate::parse::{add_feature_diagnostics, ParseSess}; use crate::parse::{add_feature_diagnostics, ParseSess};
use crate::search_paths::{PathKind, SearchPath}; use crate::search_paths::{PathKind, SearchPath};
@ -25,7 +25,7 @@ use rustc_errors::json::JsonEmitter;
use rustc_errors::registry::Registry; use rustc_errors::registry::Registry;
use rustc_errors::{ use rustc_errors::{
error_code, fallback_fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, error_code, fallback_fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage,
ErrorGuaranteed, FluentBundle, IntoDiagnostic, LazyFallbackBundle, MultiSpan, Noted, ErrorGuaranteed, FluentBundle, Handler, IntoDiagnostic, LazyFallbackBundle, MultiSpan, Noted,
TerminalUrl, TerminalUrl,
}; };
use rustc_macros::HashStable_Generic; use rustc_macros::HashStable_Generic;
@ -1382,6 +1382,7 @@ fn default_emitter(
// JUSTIFICATION: literally session construction // JUSTIFICATION: literally session construction
#[allow(rustc::bad_opt_access)] #[allow(rustc::bad_opt_access)]
pub fn build_session( pub fn build_session(
handler: &EarlyErrorHandler,
sopts: config::Options, sopts: config::Options,
io: CompilerIO, io: CompilerIO,
bundle: Option<Lrc<rustc_errors::FluentBundle>>, bundle: Option<Lrc<rustc_errors::FluentBundle>>,
@ -1408,13 +1409,12 @@ pub fn build_session(
None => filesearch::get_or_default_sysroot().expect("Failed finding sysroot"), None => filesearch::get_or_default_sysroot().expect("Failed finding sysroot"),
}; };
let target_cfg = config::build_target_config(&sopts, target_override, &sysroot); let target_cfg = config::build_target_config(handler, &sopts, target_override, &sysroot);
let host_triple = TargetTriple::from_triple(config::host_triple()); let host_triple = TargetTriple::from_triple(config::host_triple());
let (host, target_warnings) = Target::search(&host_triple, &sysroot).unwrap_or_else(|e| { let (host, target_warnings) = Target::search(&host_triple, &sysroot)
early_error(sopts.error_format, format!("Error loading host specification: {e}")) .unwrap_or_else(|e| handler.early_error(format!("Error loading host specification: {e}")));
});
for warning in target_warnings.warning_messages() { for warning in target_warnings.warning_messages() {
early_warn(sopts.error_format, warning) handler.early_warn(warning)
} }
let loader = file_loader.unwrap_or_else(|| Box::new(RealFileLoader)); let loader = file_loader.unwrap_or_else(|| Box::new(RealFileLoader));
@ -1456,7 +1456,7 @@ pub fn build_session(
match profiler { match profiler {
Ok(profiler) => Some(Arc::new(profiler)), Ok(profiler) => Some(Arc::new(profiler)),
Err(e) => { Err(e) => {
early_warn(sopts.error_format, format!("failed to create profiler: {e}")); handler.early_warn(format!("failed to create profiler: {e}"));
None None
} }
} }
@ -1723,7 +1723,64 @@ pub enum IncrCompSession {
InvalidBecauseOfErrors { session_directory: PathBuf }, InvalidBecauseOfErrors { session_directory: PathBuf },
} }
fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler { /// A wrapper around an [`Handler`] that is used for early error emissions.
pub struct EarlyErrorHandler {
handler: Handler,
}
impl EarlyErrorHandler {
pub fn new(output: ErrorOutputType) -> Self {
let emitter = mk_emitter(output);
Self { handler: rustc_errors::Handler::with_emitter(true, None, emitter) }
}
pub fn abort_if_errors(&self) {
self.handler.abort_if_errors()
}
/// Swap out the underlying handler once we acquire the user's preference on error emission
/// format. Any errors prior to that will cause an abort and all stashed diagnostics of the
/// previous handler will be emitted.
pub fn abort_if_error_and_set_error_format(&mut self, output: ErrorOutputType) {
self.handler.abort_if_errors();
let emitter = mk_emitter(output);
self.handler = Handler::with_emitter(true, None, emitter);
}
#[allow(rustc::untranslatable_diagnostic)]
#[allow(rustc::diagnostic_outside_of_impl)]
pub fn early_note(&self, msg: impl Into<DiagnosticMessage>) {
self.handler.struct_note_without_error(msg).emit()
}
#[allow(rustc::untranslatable_diagnostic)]
#[allow(rustc::diagnostic_outside_of_impl)]
pub fn early_help(&self, msg: impl Into<DiagnosticMessage>) {
self.handler.struct_help(msg).emit()
}
#[allow(rustc::untranslatable_diagnostic)]
#[allow(rustc::diagnostic_outside_of_impl)]
#[must_use = "ErrorGuaranteed must be returned from `run_compiler` in order to exit with a non-zero status code"]
pub fn early_error_no_abort(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
self.handler.struct_err(msg).emit()
}
#[allow(rustc::untranslatable_diagnostic)]
#[allow(rustc::diagnostic_outside_of_impl)]
pub fn early_error(&self, msg: impl Into<DiagnosticMessage>) -> ! {
self.handler.struct_fatal(msg).emit()
}
#[allow(rustc::untranslatable_diagnostic)]
#[allow(rustc::diagnostic_outside_of_impl)]
pub fn early_warn(&self, msg: impl Into<DiagnosticMessage>) {
self.handler.struct_warn(msg).emit()
}
}
fn mk_emitter(output: ErrorOutputType) -> Box<dyn Emitter + sync::Send + 'static> {
// FIXME(#100717): early errors aren't translated at the moment, so this is fine, but it will // FIXME(#100717): early errors aren't translated at the moment, so this is fine, but it will
// need to reference every crate that might emit an early error for translation to work. // need to reference every crate that might emit an early error for translation to work.
let fallback_bundle = let fallback_bundle =
@ -1755,27 +1812,5 @@ fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler
TerminalUrl::No, TerminalUrl::No,
)), )),
}; };
rustc_errors::Handler::with_emitter(true, None, emitter) emitter
}
#[allow(rustc::untranslatable_diagnostic)]
#[allow(rustc::diagnostic_outside_of_impl)]
#[must_use = "ErrorGuaranteed must be returned from `run_compiler` in order to exit with a non-zero status code"]
pub fn early_error_no_abort(
output: config::ErrorOutputType,
msg: impl Into<DiagnosticMessage>,
) -> ErrorGuaranteed {
early_error_handler(output).struct_err(msg).emit()
}
#[allow(rustc::untranslatable_diagnostic)]
#[allow(rustc::diagnostic_outside_of_impl)]
pub fn early_error(output: config::ErrorOutputType, msg: impl Into<DiagnosticMessage>) -> ! {
early_error_handler(output).struct_fatal(msg).emit()
}
#[allow(rustc::untranslatable_diagnostic)]
#[allow(rustc::diagnostic_outside_of_impl)]
pub fn early_warn(output: config::ErrorOutputType, msg: impl Into<DiagnosticMessage>) {
early_error_handler(output).struct_warn(msg).emit()
} }

View File

@ -15,6 +15,7 @@ use rustc_session::config::{
use rustc_session::getopts; use rustc_session::getopts;
use rustc_session::lint::Level; use rustc_session::lint::Level;
use rustc_session::search_paths::SearchPath; use rustc_session::search_paths::SearchPath;
use rustc_session::EarlyErrorHandler;
use rustc_span::edition::Edition; use rustc_span::edition::Edition;
use rustc_target::spec::TargetTriple; use rustc_target::spec::TargetTriple;
@ -311,32 +312,33 @@ impl Options {
/// Parses the given command-line for options. If an error message or other early-return has /// Parses the given command-line for options. If an error message or other early-return has
/// been printed, returns `Err` with the exit code. /// been printed, returns `Err` with the exit code.
pub(crate) fn from_matches( pub(crate) fn from_matches(
handler: &mut EarlyErrorHandler,
matches: &getopts::Matches, matches: &getopts::Matches,
args: Vec<String>, args: Vec<String>,
) -> Result<(Options, RenderOptions), i32> { ) -> Result<(Options, RenderOptions), i32> {
// Check for unstable options. // Check for unstable options.
nightly_options::check_nightly_options(matches, &opts()); nightly_options::check_nightly_options(handler, matches, &opts());
if args.is_empty() || matches.opt_present("h") || matches.opt_present("help") { if args.is_empty() || matches.opt_present("h") || matches.opt_present("help") {
crate::usage("rustdoc"); crate::usage("rustdoc");
return Err(0); return Err(0);
} else if matches.opt_present("version") { } else if matches.opt_present("version") {
rustc_driver::version!("rustdoc", matches); rustc_driver::version!(&handler, "rustdoc", matches);
return Err(0); return Err(0);
} }
if rustc_driver::describe_flag_categories(&matches) { if rustc_driver::describe_flag_categories(handler, &matches) {
return Err(0); return Err(0);
} }
let color = config::parse_color(matches); let color = config::parse_color(handler, matches);
let config::JsonConfig { json_rendered, json_unused_externs, .. } = let config::JsonConfig { json_rendered, json_unused_externs, .. } =
config::parse_json(matches); config::parse_json(handler, matches);
let error_format = config::parse_error_format(matches, color, json_rendered); let error_format = config::parse_error_format(handler, matches, color, json_rendered);
let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_default(); let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_default();
let codegen_options = CodegenOptions::build(matches, error_format); let codegen_options = CodegenOptions::build(handler, matches);
let unstable_opts = UnstableOptions::build(matches, error_format); let unstable_opts = UnstableOptions::build(handler, matches);
let diag = new_handler(error_format, None, diagnostic_width, &unstable_opts); let diag = new_handler(error_format, None, diagnostic_width, &unstable_opts);
@ -393,8 +395,7 @@ impl Options {
&& !matches.opt_present("show-coverage") && !matches.opt_present("show-coverage")
&& !nightly_options::is_unstable_enabled(matches) && !nightly_options::is_unstable_enabled(matches)
{ {
rustc_session::early_error( handler.early_error(
error_format,
"the -Z unstable-options flag must be passed to enable --output-format for documentation generation (see https://github.com/rust-lang/rust/issues/76578)", "the -Z unstable-options flag must be passed to enable --output-format for documentation generation (see https://github.com/rust-lang/rust/issues/76578)",
); );
} }
@ -432,7 +433,7 @@ impl Options {
return Err(0); return Err(0);
} }
let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format); let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(handler, matches);
let input = PathBuf::from(if describe_lints { let input = PathBuf::from(if describe_lints {
"" // dummy, this won't be used "" // dummy, this won't be used
@ -446,12 +447,9 @@ impl Options {
&matches.free[0] &matches.free[0]
}); });
let libs = matches let libs =
.opt_strs("L") matches.opt_strs("L").iter().map(|s| SearchPath::from_cli_opt(handler, s)).collect();
.iter() let externs = parse_externs(handler, matches, &unstable_opts);
.map(|s| SearchPath::from_cli_opt(s, error_format))
.collect();
let externs = parse_externs(matches, &unstable_opts, error_format);
let extern_html_root_urls = match parse_extern_html_roots(matches) { let extern_html_root_urls = match parse_extern_html_roots(matches) {
Ok(ex) => ex, Ok(ex) => ex,
Err(err) => { Err(err) => {
@ -589,7 +587,7 @@ impl Options {
} }
} }
let edition = config::parse_crate_edition(matches); let edition = config::parse_crate_edition(handler, matches);
let mut id_map = html::markdown::IdMap::new(); let mut id_map = html::markdown::IdMap::new();
let Some(external_html) = ExternalHtml::load( let Some(external_html) = ExternalHtml::load(
@ -623,7 +621,7 @@ impl Options {
} }
} }
let target = parse_target_triple(matches, error_format); let target = parse_target_triple(handler, matches);
let show_coverage = matches.opt_present("show-coverage"); let show_coverage = matches.opt_present("show-coverage");

View File

@ -13,8 +13,8 @@ use rustc_interface::interface;
use rustc_middle::hir::nested_filter; use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
use rustc_session::config::{self, CrateType, ErrorOutputType, ResolveDocLinks}; use rustc_session::config::{self, CrateType, ErrorOutputType, ResolveDocLinks};
use rustc_session::lint;
use rustc_session::Session; use rustc_session::Session;
use rustc_session::{lint, EarlyErrorHandler};
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_span::{source_map, Span}; use rustc_span::{source_map, Span};
@ -181,6 +181,7 @@ pub(crate) fn new_handler(
/// Parse, resolve, and typecheck the given crate. /// Parse, resolve, and typecheck the given crate.
pub(crate) fn create_config( pub(crate) fn create_config(
handler: &EarlyErrorHandler,
RustdocOptions { RustdocOptions {
input, input,
crate_name, crate_name,
@ -258,8 +259,8 @@ pub(crate) fn create_config(
interface::Config { interface::Config {
opts: sessopts, opts: sessopts,
crate_cfg: interface::parse_cfgspecs(cfgs), crate_cfg: interface::parse_cfgspecs(handler, cfgs),
crate_check_cfg: interface::parse_check_cfg(check_cfgs), crate_check_cfg: interface::parse_check_cfg(handler, check_cfgs),
input, input,
output_file: None, output_file: None,
output_dir: None, output_dir: None,

View File

@ -12,7 +12,7 @@ use rustc_parse::maybe_new_parser_from_source_str;
use rustc_parse::parser::attr::InnerAttrPolicy; use rustc_parse::parser::attr::InnerAttrPolicy;
use rustc_session::config::{self, CrateType, ErrorOutputType}; use rustc_session::config::{self, CrateType, ErrorOutputType};
use rustc_session::parse::ParseSess; use rustc_session::parse::ParseSess;
use rustc_session::{lint, Session}; use rustc_session::{lint, EarlyErrorHandler, Session};
use rustc_span::edition::Edition; use rustc_span::edition::Edition;
use rustc_span::source_map::SourceMap; use rustc_span::source_map::SourceMap;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
@ -85,13 +85,18 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> {
..config::Options::default() ..config::Options::default()
}; };
let early_error_handler = EarlyErrorHandler::new(ErrorOutputType::default());
let mut cfgs = options.cfgs.clone(); let mut cfgs = options.cfgs.clone();
cfgs.push("doc".to_owned()); cfgs.push("doc".to_owned());
cfgs.push("doctest".to_owned()); cfgs.push("doctest".to_owned());
let config = interface::Config { let config = interface::Config {
opts: sessopts, opts: sessopts,
crate_cfg: interface::parse_cfgspecs(cfgs), crate_cfg: interface::parse_cfgspecs(&early_error_handler, cfgs),
crate_check_cfg: interface::parse_check_cfg(options.check_cfgs.clone()), crate_check_cfg: interface::parse_check_cfg(
&early_error_handler,
options.check_cfgs.clone(),
),
input, input,
output_file: None, output_file: None,
output_dir: None, output_dir: None,

View File

@ -79,8 +79,7 @@ use rustc_errors::ErrorGuaranteed;
use rustc_interface::interface; use rustc_interface::interface;
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use rustc_session::config::{make_crate_type_option, ErrorOutputType, RustcOptGroup}; use rustc_session::config::{make_crate_type_option, ErrorOutputType, RustcOptGroup};
use rustc_session::getopts; use rustc_session::{getopts, EarlyErrorHandler};
use rustc_session::{early_error, early_warn};
use crate::clean::utils::DOC_RUST_LANG_ORG_CHANNEL; use crate::clean::utils::DOC_RUST_LANG_ORG_CHANNEL;
@ -155,6 +154,8 @@ pub fn main() {
} }
} }
let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
rustc_driver::install_ice_hook( rustc_driver::install_ice_hook(
"https://github.com/rust-lang/rust/issues/new\ "https://github.com/rust-lang/rust/issues/new\
?labels=C-bug%2C+I-ICE%2C+T-rustdoc&template=ice.md", ?labels=C-bug%2C+I-ICE%2C+T-rustdoc&template=ice.md",
@ -170,11 +171,12 @@ pub fn main() {
// NOTE: The reason this doesn't show double logging when `download-rustc = false` and // NOTE: The reason this doesn't show double logging when `download-rustc = false` and
// `debug_logging = true` is because all rustc logging goes to its version of tracing (the one // `debug_logging = true` is because all rustc logging goes to its version of tracing (the one
// in the sysroot), and all of rustdoc's logging goes to its version (the one in Cargo.toml). // in the sysroot), and all of rustdoc's logging goes to its version (the one in Cargo.toml).
init_logging();
rustc_driver::init_env_logger("RUSTDOC_LOG");
let exit_code = rustc_driver::catch_with_exit_code(|| match get_args() { init_logging(&handler);
Some(args) => main_args(&args), rustc_driver::init_env_logger(&handler, "RUSTDOC_LOG");
let exit_code = rustc_driver::catch_with_exit_code(|| match get_args(&handler) {
Some(args) => main_args(&mut handler, &args),
_ => _ =>
{ {
#[allow(deprecated)] #[allow(deprecated)]
@ -184,22 +186,19 @@ pub fn main() {
process::exit(exit_code); process::exit(exit_code);
} }
fn init_logging() { fn init_logging(handler: &EarlyErrorHandler) {
let color_logs = match std::env::var("RUSTDOC_LOG_COLOR").as_deref() { let color_logs = match std::env::var("RUSTDOC_LOG_COLOR").as_deref() {
Ok("always") => true, Ok("always") => true,
Ok("never") => false, Ok("never") => false,
Ok("auto") | Err(VarError::NotPresent) => io::stdout().is_terminal(), Ok("auto") | Err(VarError::NotPresent) => io::stdout().is_terminal(),
Ok(value) => early_error( Ok(value) => handler.early_error(format!(
ErrorOutputType::default(), "invalid log color value '{}': expected one of always, never, or auto",
format!("invalid log color value '{}': expected one of always, never, or auto", value), value
), )),
Err(VarError::NotUnicode(value)) => early_error( Err(VarError::NotUnicode(value)) => handler.early_error(format!(
ErrorOutputType::default(), "invalid log color value '{}': expected one of always, never, or auto",
format!( value.to_string_lossy()
"invalid log color value '{}': expected one of always, never, or auto", )),
value.to_string_lossy()
),
),
}; };
let filter = tracing_subscriber::EnvFilter::from_env("RUSTDOC_LOG"); let filter = tracing_subscriber::EnvFilter::from_env("RUSTDOC_LOG");
let layer = tracing_tree::HierarchicalLayer::default() let layer = tracing_tree::HierarchicalLayer::default()
@ -219,16 +218,13 @@ fn init_logging() {
tracing::subscriber::set_global_default(subscriber).unwrap(); tracing::subscriber::set_global_default(subscriber).unwrap();
} }
fn get_args() -> Option<Vec<String>> { fn get_args(handler: &EarlyErrorHandler) -> Option<Vec<String>> {
env::args_os() env::args_os()
.enumerate() .enumerate()
.map(|(i, arg)| { .map(|(i, arg)| {
arg.into_string() arg.into_string()
.map_err(|arg| { .map_err(|arg| {
early_warn( handler.early_warn(format!("Argument {} is not valid Unicode: {:?}", i, arg));
ErrorOutputType::default(),
format!("Argument {} is not valid Unicode: {:?}", i, arg),
);
}) })
.ok() .ok()
}) })
@ -710,7 +706,7 @@ fn run_renderer<'tcx, T: formats::FormatRenderer<'tcx>>(
} }
} }
fn main_args(at_args: &[String]) -> MainResult { fn main_args(handler: &mut EarlyErrorHandler, at_args: &[String]) -> MainResult {
// Throw away the first argument, the name of the binary. // Throw away the first argument, the name of the binary.
// In case of at_args being empty, as might be the case by // In case of at_args being empty, as might be the case by
// passing empty argument array to execve under some platforms, // passing empty argument array to execve under some platforms,
@ -721,7 +717,7 @@ fn main_args(at_args: &[String]) -> MainResult {
// the compiler with @empty_file as argv[0] and no more arguments. // the compiler with @empty_file as argv[0] and no more arguments.
let at_args = at_args.get(1..).unwrap_or_default(); let at_args = at_args.get(1..).unwrap_or_default();
let args = rustc_driver::args::arg_expand_all(at_args); let args = rustc_driver::args::arg_expand_all(handler, at_args);
let mut options = getopts::Options::new(); let mut options = getopts::Options::new();
for option in opts() { for option in opts() {
@ -730,13 +726,13 @@ fn main_args(at_args: &[String]) -> MainResult {
let matches = match options.parse(&args) { let matches = match options.parse(&args) {
Ok(m) => m, Ok(m) => m,
Err(err) => { Err(err) => {
early_error(ErrorOutputType::default(), err.to_string()); handler.early_error(err.to_string());
} }
}; };
// Note that we discard any distinction between different non-zero exit // Note that we discard any distinction between different non-zero exit
// codes from `from_matches` here. // codes from `from_matches` here.
let (options, render_options) = match config::Options::from_matches(&matches, args) { let (options, render_options) = match config::Options::from_matches(handler, &matches, args) {
Ok(opts) => opts, Ok(opts) => opts,
Err(code) => { Err(code) => {
return if code == 0 { return if code == 0 {
@ -764,7 +760,7 @@ fn main_args(at_args: &[String]) -> MainResult {
(false, true) => { (false, true) => {
let input = options.input.clone(); let input = options.input.clone();
let edition = options.edition; let edition = options.edition;
let config = core::create_config(options, &render_options); let config = core::create_config(handler, options, &render_options);
// `markdown::render` can invoke `doctest::make_test`, which // `markdown::render` can invoke `doctest::make_test`, which
// requires session globals and a thread pool, so we use // requires session globals and a thread pool, so we use
@ -797,7 +793,7 @@ fn main_args(at_args: &[String]) -> MainResult {
let scrape_examples_options = options.scrape_examples_options.clone(); let scrape_examples_options = options.scrape_examples_options.clone();
let bin_crate = options.bin_crate; let bin_crate = options.bin_crate;
let config = core::create_config(options, &render_options); let config = core::create_config(handler, options, &render_options);
interface::run_compiler(config, |compiler| { interface::run_compiler(config, |compiler| {
let sess = compiler.session(); let sess = compiler.session();

View File

@ -16,6 +16,8 @@ extern crate rustc_session;
extern crate rustc_span; extern crate rustc_span;
use rustc_interface::interface; use rustc_interface::interface;
use rustc_session::EarlyErrorHandler;
use rustc_session::config::ErrorOutputType;
use rustc_session::parse::ParseSess; use rustc_session::parse::ParseSess;
use rustc_span::symbol::Symbol; use rustc_span::symbol::Symbol;
@ -187,7 +189,9 @@ const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/ne
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
pub fn main() { pub fn main() {
rustc_driver::init_rustc_env_logger(); let handler = EarlyErrorHandler::new(ErrorOutputType::default());
rustc_driver::init_rustc_env_logger(&handler);
rustc_driver::install_ice_hook(BUG_REPORT_URL, |handler| { rustc_driver::install_ice_hook(BUG_REPORT_URL, |handler| {
// FIXME: this macro calls unwrap internally but is called in a panicking context! It's not // FIXME: this macro calls unwrap internally but is called in a panicking context! It's not

View File

@ -1,6 +1,7 @@
#![feature(rustc_private)] #![feature(rustc_private)]
extern crate rustc_driver; extern crate rustc_driver;
extern crate rustc_session;
use std::env; use std::env;
use std::error::Error; use std::error::Error;
@ -170,7 +171,9 @@ fn parse_args() -> (OutputFormat, PathBuf) {
} }
fn main() { fn main() {
rustc_driver::init_env_logger("RUST_LOG"); let handler =
rustc_session::EarlyErrorHandler::new(rustc_session::config::ErrorOutputType::default());
rustc_driver::init_env_logger(&handler, "RUST_LOG");
let (format, dst) = parse_args(); let (format, dst) = parse_args();
let result = main_with_result(format, &dst); let result = main_with_result(format, &dst);
if let Err(e) = result { if let Err(e) = result {

View File

@ -31,9 +31,9 @@ use rustc_middle::{
query::{ExternProviders, LocalCrate}, query::{ExternProviders, LocalCrate},
ty::TyCtxt, ty::TyCtxt,
}; };
use rustc_session::config::OptLevel; use rustc_session::{EarlyErrorHandler, CtfeBacktrace};
use rustc_session::config::{OptLevel, CrateType, ErrorOutputType};
use rustc_session::{config::CrateType, search_paths::PathKind, CtfeBacktrace}; use rustc_session::search_paths::PathKind;
use miri::{BacktraceStyle, BorrowTrackerMethod, ProvenanceMode, RetagFields}; use miri::{BacktraceStyle, BorrowTrackerMethod, ProvenanceMode, RetagFields};
@ -59,6 +59,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
fn after_analysis<'tcx>( fn after_analysis<'tcx>(
&mut self, &mut self,
handler: &EarlyErrorHandler,
_: &rustc_interface::interface::Compiler, _: &rustc_interface::interface::Compiler,
queries: &'tcx rustc_interface::Queries<'tcx>, queries: &'tcx rustc_interface::Queries<'tcx>,
) -> Compilation { ) -> Compilation {
@ -66,8 +67,8 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
if tcx.sess.compile_status().is_err() { if tcx.sess.compile_status().is_err() {
tcx.sess.fatal("miri cannot be run on programs that fail compilation"); tcx.sess.fatal("miri cannot be run on programs that fail compilation");
} }
;
init_late_loggers(tcx); init_late_loggers(handler, tcx);
if !tcx.sess.crate_types().contains(&CrateType::Executable) { if !tcx.sess.crate_types().contains(&CrateType::Executable) {
tcx.sess.fatal("miri only makes sense on bin crates"); tcx.sess.fatal("miri only makes sense on bin crates");
} }
@ -181,7 +182,7 @@ macro_rules! show_error {
($($tt:tt)*) => { show_error(&format_args!($($tt)*)) }; ($($tt:tt)*) => { show_error(&format_args!($($tt)*)) };
} }
fn init_early_loggers() { fn init_early_loggers(handler: &EarlyErrorHandler) {
// Note that our `extern crate log` is *not* the same as rustc's; as a result, we have to // Note that our `extern crate log` is *not* the same as rustc's; as a result, we have to
// initialize them both, and we always initialize `miri`'s first. // initialize them both, and we always initialize `miri`'s first.
let env = env_logger::Env::new().filter("MIRI_LOG").write_style("MIRI_LOG_STYLE"); let env = env_logger::Env::new().filter("MIRI_LOG").write_style("MIRI_LOG_STYLE");
@ -195,11 +196,11 @@ fn init_early_loggers() {
// later with our custom settings, and *not* log anything for what happens before // later with our custom settings, and *not* log anything for what happens before
// `miri` gets started. // `miri` gets started.
if env::var_os("RUSTC_LOG").is_some() { if env::var_os("RUSTC_LOG").is_some() {
rustc_driver::init_rustc_env_logger(); rustc_driver::init_rustc_env_logger(handler);
} }
} }
fn init_late_loggers(tcx: TyCtxt<'_>) { fn init_late_loggers(handler: &EarlyErrorHandler, tcx: TyCtxt<'_>) {
// We initialize loggers right before we start evaluation. We overwrite the `RUSTC_LOG` // We initialize loggers right before we start evaluation. We overwrite the `RUSTC_LOG`
// env var if it is not set, control it based on `MIRI_LOG`. // env var if it is not set, control it based on `MIRI_LOG`.
// (FIXME: use `var_os`, but then we need to manually concatenate instead of `format!`.) // (FIXME: use `var_os`, but then we need to manually concatenate instead of `format!`.)
@ -218,7 +219,7 @@ fn init_late_loggers(tcx: TyCtxt<'_>) {
} else { } else {
env::set_var("RUSTC_LOG", &var); env::set_var("RUSTC_LOG", &var);
} }
rustc_driver::init_rustc_env_logger(); rustc_driver::init_rustc_env_logger(handler);
} }
} }
@ -284,6 +285,8 @@ fn parse_comma_list<T: FromStr>(input: &str) -> Result<Vec<T>, T::Err> {
} }
fn main() { fn main() {
let handler = EarlyErrorHandler::new(ErrorOutputType::default());
// Snapshot a copy of the environment before `rustc` starts messing with it. // Snapshot a copy of the environment before `rustc` starts messing with it.
// (`install_ice_hook` might change `RUST_BACKTRACE`.) // (`install_ice_hook` might change `RUST_BACKTRACE`.)
let env_snapshot = env::vars_os().collect::<Vec<_>>(); let env_snapshot = env::vars_os().collect::<Vec<_>>();
@ -292,7 +295,7 @@ fn main() {
if let Some(crate_kind) = env::var_os("MIRI_BE_RUSTC") { if let Some(crate_kind) = env::var_os("MIRI_BE_RUSTC") {
// Earliest rustc setup. // Earliest rustc setup.
rustc_driver::install_ice_hook(rustc_driver::DEFAULT_BUG_REPORT_URL, |_| ()); rustc_driver::install_ice_hook(rustc_driver::DEFAULT_BUG_REPORT_URL, |_| ());
rustc_driver::init_rustc_env_logger(); rustc_driver::init_rustc_env_logger(&handler);
let target_crate = if crate_kind == "target" { let target_crate = if crate_kind == "target" {
true true
@ -314,7 +317,7 @@ fn main() {
rustc_driver::install_ice_hook("https://github.com/rust-lang/miri/issues/new", |_| ()); rustc_driver::install_ice_hook("https://github.com/rust-lang/miri/issues/new", |_| ());
// Init loggers the Miri way. // Init loggers the Miri way.
init_early_loggers(); init_early_loggers(&handler);
// Parse our arguments and split them across `rustc` and `miri`. // Parse our arguments and split them across `rustc` and `miri`.
let mut miri_config = miri::MiriConfig::default(); let mut miri_config = miri::MiriConfig::default();

View File

@ -27,7 +27,7 @@ use rustc_interface::{Config, Queries};
use rustc_middle::query::queries::mir_borrowck::ProvidedValue; use rustc_middle::query::queries::mir_borrowck::ProvidedValue;
use rustc_middle::query::{ExternProviders, Providers}; use rustc_middle::query::{ExternProviders, Providers};
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use rustc_session::Session; use rustc_session::{Session, EarlyErrorHandler};
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::thread_local; use std::thread_local;
@ -58,6 +58,7 @@ impl rustc_driver::Callbacks for CompilerCalls {
// the result. // the result.
fn after_analysis<'tcx>( fn after_analysis<'tcx>(
&mut self, &mut self,
_handler: &EarlyErrorHandler,
compiler: &Compiler, compiler: &Compiler,
queries: &'tcx Queries<'tcx>, queries: &'tcx Queries<'tcx>,
) -> Compilation { ) -> Compilation {

View File

@ -12,12 +12,14 @@ extern crate rustc_driver;
extern crate rustc_hir; extern crate rustc_hir;
extern crate rustc_interface; extern crate rustc_interface;
extern crate rustc_middle; extern crate rustc_middle;
extern crate rustc_session;
extern crate rustc_smir; extern crate rustc_smir;
use rustc_driver::{Callbacks, Compilation, RunCompiler}; use rustc_driver::{Callbacks, Compilation, RunCompiler};
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_interface::{interface, Queries}; use rustc_interface::{interface, Queries};
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use rustc_session::EarlyErrorHandler;
use rustc_smir::{rustc_internal, stable_mir}; use rustc_smir::{rustc_internal, stable_mir};
use std::io::Write; use std::io::Write;
@ -121,6 +123,7 @@ impl Callbacks for SMirCalls {
/// continue the compilation afterwards (defaults to `Compilation::Continue`) /// continue the compilation afterwards (defaults to `Compilation::Continue`)
fn after_analysis<'tcx>( fn after_analysis<'tcx>(
&mut self, &mut self,
_handler: &EarlyErrorHandler,
_compiler: &interface::Compiler, _compiler: &interface::Compiler,
queries: &'tcx Queries<'tcx>, queries: &'tcx Queries<'tcx>,
) -> Compilation { ) -> Compilation {