X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fdriver.rs;h=7865f6bb9dd0e0f2c173bfe457e515900fc8ea7b;hb=a97f60b8e31e1913c0b937bc2adcfb5e9e123120;hp=146e3fe2d08f03dc0c76036902a11966196c270a;hpb=d509b5afef4b98efd55c428174f3354706bbd12a;p=rust.git diff --git a/src/driver.rs b/src/driver.rs index 146e3fe2d08..7865f6bb9dd 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -1,18 +1,27 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![feature(rustc_private)] +#![feature(str_strip)] // FIXME: switch to something more ergonomic here, once available. // (Currently there is no way to opt into sysroot crates without `extern crate`.) #[allow(unused_extern_crates)] +extern crate rustc; +#[allow(unused_extern_crates)] extern crate rustc_driver; #[allow(unused_extern_crates)] -extern crate rustc_interface; +extern crate rustc_errors; #[allow(unused_extern_crates)] -extern crate rustc_plugin; +extern crate rustc_interface; +use rustc::ty::TyCtxt; use rustc_interface::interface; -use rustc_tools_util::*; +use rustc_tools_util::VersionInfo; +use lazy_static::lazy_static; +use std::borrow::Cow; +use std::env; +use std::ops::Deref; +use std::panic; use std::path::{Path, PathBuf}; use std::process::{exit, Command}; @@ -20,22 +29,21 @@ /// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If /// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`. -fn arg_value<'a>( - args: impl IntoIterator, +fn arg_value<'a, T: Deref>( + args: &'a [T], find_arg: &str, pred: impl Fn(&str) -> bool, ) -> Option<&'a str> { - let mut args = args.into_iter().map(String::as_str); - + let mut args = args.iter().map(Deref::deref); while let Some(arg) = args.next() { - let arg: Vec<_> = arg.splitn(2, '=').collect(); - if arg.get(0) != Some(&find_arg) { + let mut arg = arg.splitn(2, '='); + if arg.next() != Some(find_arg) { continue; } - let value = arg.get(1).cloned().or_else(|| args.next()); - if value.as_ref().map_or(false, |p| pred(p)) { - return value; + match arg.next().or_else(|| args.next()) { + Some(v) if pred(v) => return Some(v), + _ => {}, } } None @@ -43,77 +51,49 @@ fn arg_value<'a>( #[test] fn test_arg_value() { - let args: Vec<_> = ["--bar=bar", "--foobar", "123", "--foo"] - .iter() - .map(std::string::ToString::to_string) - .collect(); - - assert_eq!(arg_value(None, "--foobar", |_| true), None); - assert_eq!(arg_value(&args, "--bar", |_| false), None); - assert_eq!(arg_value(&args, "--bar", |_| true), Some("bar")); - assert_eq!(arg_value(&args, "--bar", |p| p == "bar"), Some("bar")); - assert_eq!(arg_value(&args, "--bar", |p| p == "foo"), None); - assert_eq!(arg_value(&args, "--foobar", |p| p == "foo"), None); - assert_eq!(arg_value(&args, "--foobar", |p| p == "123"), Some("123")); - assert_eq!(arg_value(&args, "--foo", |_| true), None); + let args = &["--bar=bar", "--foobar", "123", "--foo"]; + + assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None); + assert_eq!(arg_value(args, "--bar", |_| false), None); + assert_eq!(arg_value(args, "--bar", |_| true), Some("bar")); + assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar")); + assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None); + assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None); + assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123")); + assert_eq!(arg_value(args, "--foo", |_| true), None); } -#[allow(clippy::too_many_lines)] +struct DefaultCallbacks; +impl rustc_driver::Callbacks for DefaultCallbacks {} struct ClippyCallbacks; - impl rustc_driver::Callbacks for ClippyCallbacks { - fn after_parsing(&mut self, compiler: &interface::Compiler) -> bool { - let sess = compiler.session(); - let mut registry = rustc_plugin::registry::Registry::new( - sess, - compiler - .parse() - .expect( - "at this compilation stage \ - the crate must be parsed", - ) - .peek() - .span, - ); - registry.args_hidden = Some(Vec::new()); - - let conf = clippy_lints::read_conf(®istry); - clippy_lints::register_plugins(&mut registry, &conf); - - let rustc_plugin::registry::Registry { - early_lint_passes, - late_lint_passes, - lint_groups, - llvm_passes, - attributes, - .. - } = registry; - let mut ls = sess.lint_store.borrow_mut(); - for pass in early_lint_passes { - ls.register_early_pass(Some(sess), true, false, pass); - } - for pass in late_lint_passes { - ls.register_late_pass(Some(sess), true, false, false, pass); - } - - for (name, (to, deprecated_name)) in lint_groups { - ls.register_group(Some(sess), true, name, deprecated_name, to); - } - clippy_lints::register_pre_expansion_lints(sess, &mut ls, &conf); - clippy_lints::register_renamed(&mut ls); - - sess.plugin_llvm_passes.borrow_mut().extend(llvm_passes); - sess.plugin_attributes.borrow_mut().extend(attributes); + fn config(&mut self, config: &mut interface::Config) { + let previous = config.register_lints.take(); + config.register_lints = Some(Box::new(move |sess, mut lint_store| { + // technically we're ~guaranteed that this is none but might as well call anything that + // is there already. Certainly it can't hurt. + if let Some(previous) = &previous { + (previous)(sess, lint_store); + } - // Continue execution - true + let conf = clippy_lints::read_conf(&[], &sess); + clippy_lints::register_plugins(&mut lint_store, &sess, &conf); + clippy_lints::register_pre_expansion_lints(&mut lint_store, &conf); + clippy_lints::register_renamed(&mut lint_store); + })); + + // FIXME: #4825; This is required, because Clippy lints that are based on MIR have to be + // run on the unoptimized MIR. On the other hand this results in some false negatives. If + // MIR passes can be enabled / disabled separately, we should figure out, what passes to + // use for Clippy. + config.opts.debugging_opts.mir_opt_level = 0; } } #[allow(clippy::find_map, clippy::filter_map)] fn describe_lints() { - use lintlist::*; + use lintlist::{Level, Lint, ALL_LINTS, LINT_LEVELS}; use std::collections::HashSet; println!( @@ -247,20 +227,84 @@ fn display_help() { ); } +const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new"; + +lazy_static! { + static ref ICE_HOOK: Box) + Sync + Send + 'static> = { + let hook = panic::take_hook(); + panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL))); + hook + }; +} + +fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { + // Invoke our ICE handler, which prints the actual panic message and optionally a backtrace + (*ICE_HOOK)(info); + + // Separate the output with an empty line + eprintln!(); + + let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( + rustc_errors::ColorConfig::Auto, + None, + false, + false, + None, + false, + )); + let handler = rustc_errors::Handler::with_emitter(true, None, emitter); + + // a .span_bug or .bug call has already printed what + // it wants to print. + if !info.payload().is::() { + let d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic"); + handler.emit_diagnostic(&d); + } + + let version_info = rustc_tools_util::get_version_info!(); + + let xs: Vec> = vec![ + "the compiler unexpectedly panicked. this is a bug.".into(), + format!("we would appreciate a bug report: {}", bug_report_url).into(), + format!("Clippy version: {}", version_info).into(), + ]; + + for note in &xs { + handler.note_without_error(¬e); + } + + // If backtraces are enabled, also print the query stack + let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0"); + + if backtrace { + TyCtxt::try_print_query_stack(&handler); + } +} + +fn toolchain_path(home: Option, toolchain: Option) -> Option { + home.and_then(|home| { + toolchain.map(|toolchain| { + let mut path = PathBuf::from(home); + path.push("toolchains"); + path.push(toolchain); + path + }) + }) +} + pub fn main() { rustc_driver::init_rustc_env_logger(); + lazy_static::initialize(&ICE_HOOK); exit( - rustc_driver::report_ices_to_stderr_if_any(move || { - use std::env; + rustc_driver::catch_fatal_errors(move || { + let mut orig_args: Vec = env::args().collect(); - if std::env::args().any(|a| a == "--version" || a == "-V") { + if orig_args.iter().any(|a| a == "--version" || a == "-V") { let version_info = rustc_tools_util::get_version_info!(); println!("{}", version_info); exit(0); } - let mut orig_args: Vec = env::args().collect(); - // Get the sysroot, looking from most specific to this invocation to the least: // - command line // - runtime environment @@ -268,22 +312,21 @@ pub fn main() { // - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN // - sysroot from rustc in the path // - compile-time environment + // - SYSROOT + // - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN let sys_root_arg = arg_value(&orig_args, "--sysroot", |_| true); let have_sys_root_arg = sys_root_arg.is_some(); let sys_root = sys_root_arg .map(PathBuf::from) .or_else(|| std::env::var("SYSROOT").ok().map(PathBuf::from)) .or_else(|| { - let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); - let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN")); - home.and_then(|home| { - toolchain.map(|toolchain| { - let mut path = PathBuf::from(home); - path.push("toolchains"); - path.push(toolchain); - path - }) - }) + let home = std::env::var("RUSTUP_HOME") + .or_else(|_| std::env::var("MULTIRUST_HOME")) + .ok(); + let toolchain = std::env::var("RUSTUP_TOOLCHAIN") + .or_else(|_| std::env::var("MULTIRUST_TOOLCHAIN")) + .ok(); + toolchain_path(home, toolchain) }) .or_else(|| { Command::new("rustc") @@ -295,25 +338,34 @@ pub fn main() { .map(|s| PathBuf::from(s.trim())) }) .or_else(|| option_env!("SYSROOT").map(PathBuf::from)) + .or_else(|| { + let home = option_env!("RUSTUP_HOME") + .or(option_env!("MULTIRUST_HOME")) + .map(ToString::to_string); + let toolchain = option_env!("RUSTUP_TOOLCHAIN") + .or(option_env!("MULTIRUST_TOOLCHAIN")) + .map(ToString::to_string); + toolchain_path(home, toolchain) + }) .map(|pb| pb.to_string_lossy().to_string()) .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust"); // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument. // We're invoking the compiler programmatically, so we ignore this/ - let wrapper_mode = Path::new(&orig_args[1]).file_stem() == Some("rustc".as_ref()); + let wrapper_mode = orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref()); if wrapper_mode { // we still want to be able to invoke it normally though orig_args.remove(1); } - if !wrapper_mode && std::env::args().any(|a| a == "--help" || a == "-h") { + if !wrapper_mode && (orig_args.iter().any(|a| a == "--help" || a == "-h") || orig_args.len() == 1) { display_help(); exit(0); } let should_describe_lints = || { - let args: Vec<_> = std::env::args().collect(); + let args: Vec<_> = env::args().collect(); args.windows(2).any(|args| { args[1] == "help" && match args[0].as_str() { @@ -331,25 +383,18 @@ pub fn main() { // this conditional check for the --sysroot flag is there so users can call // `clippy_driver` directly // without having to pass --sysroot or anything - let mut args: Vec = if have_sys_root_arg { - orig_args.clone() - } else { - orig_args - .clone() - .into_iter() - .chain(Some("--sysroot".to_owned())) - .chain(Some(sys_root)) - .collect() + let mut args: Vec = orig_args.clone(); + if !have_sys_root_arg { + args.extend(vec!["--sysroot".into(), sys_root]); }; // this check ensures that dependencies are built but not linted and the final - // crate is - // linted but not built - let clippy_enabled = env::var("CLIPPY_TESTS").ok().map_or(false, |val| val == "true") - || arg_value(&orig_args, "--emit", |val| val.split(',').any(|e| e == "metadata")).is_some(); + // crate is linted but not built + let clippy_enabled = env::var("CLIPPY_TESTS").map_or(false, |val| val == "true") + || arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_none(); if clippy_enabled { - args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-clippy""#.to_owned()]); + args.extend(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]); if let Ok(extra_args) = env::var("CLIPPY_ARGS") { args.extend(extra_args.split("__CLIPPY_HACKERY__").filter_map(|s| { if s.is_empty() { @@ -360,12 +405,10 @@ pub fn main() { })); } } - let mut clippy = ClippyCallbacks; - let mut default = rustc_driver::DefaultCallbacks; + let mut default = DefaultCallbacks; let callbacks: &mut (dyn rustc_driver::Callbacks + Send) = if clippy_enabled { &mut clippy } else { &mut default }; - let args = args; rustc_driver::run_compiler(&args, callbacks, None, None) }) .and_then(|result| result)