X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fdriver.rs;h=0c82f37d6a22e32572c15956bdf6ecd7e0c32d80;hb=a9e9b7f9b2ad52035fe7b57bb0fc8ba62d649c33;hp=3e1f423865b1d671ab54203f674c819bba9dc399;hpb=221c482142f84d5889edb8d17efab6470c81cf04;p=rust.git diff --git a/src/driver.rs b/src/driver.rs index 3e1f423865b..0c82f37d6a2 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -1,31 +1,32 @@ -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![feature(rustc_private)] +#![feature(once_cell)] +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +// warn on lints, that are included in `rust-lang/rust`s bootstrap +#![warn(rust_2018_idioms, unused_lifetimes)] +// warn on rustc internal lints +#![warn(rustc::internal)] // 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_driver; -#[allow(unused_extern_crates)] extern crate rustc_errors; -#[allow(unused_extern_crates)] extern crate rustc_interface; -#[allow(unused_extern_crates)] -extern crate rustc_middle; +extern crate rustc_session; +extern crate rustc_span; use rustc_interface::interface; -use rustc_middle::ty::TyCtxt; +use rustc_session::parse::ParseSess; +use rustc_span::symbol::Symbol; use rustc_tools_util::VersionInfo; -use lazy_static::lazy_static; use std::borrow::Cow; use std::env; +use std::lazy::SyncLazy; use std::ops::Deref; use std::panic; use std::path::{Path, PathBuf}; use std::process::{exit, Command}; -mod lintlist; - /// 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, T: Deref>( @@ -62,141 +63,63 @@ fn test_arg_value() { assert_eq!(arg_value(args, "--foo", |_| true), None); } +fn track_clippy_args(parse_sess: &mut ParseSess, args_env_var: &Option) { + parse_sess.env_depinfo.get_mut().insert(( + Symbol::intern("CLIPPY_ARGS"), + args_env_var.as_deref().map(Symbol::intern), + )); +} + struct DefaultCallbacks; impl rustc_driver::Callbacks for DefaultCallbacks {} -struct ClippyCallbacks; +/// This is different from `DefaultCallbacks` that it will inform Cargo to track the value of +/// `CLIPPY_ARGS` environment variable. +struct RustcCallbacks { + clippy_args_var: Option, +} + +impl rustc_driver::Callbacks for RustcCallbacks { + fn config(&mut self, config: &mut interface::Config) { + let clippy_args_var = self.clippy_args_var.take(); + config.parse_sess_created = Some(Box::new(move |parse_sess| { + track_clippy_args(parse_sess, &clippy_args_var); + })); + } +} + +struct ClippyCallbacks { + clippy_args_var: Option, +} + impl rustc_driver::Callbacks for ClippyCallbacks { 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| { + let clippy_args_var = self.clippy_args_var.take(); + config.parse_sess_created = Some(Box::new(move |parse_sess| { + track_clippy_args(parse_sess, &clippy_args_var); + })); + config.register_lints = Some(Box::new(move |sess, 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); } - 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); + let conf = clippy_lints::read_conf(sess); + clippy_lints::register_plugins(lint_store, sess, &conf); + clippy_lints::register_pre_expansion_lints(lint_store); + clippy_lints::register_renamed(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; + config.opts.debugging_opts.mir_opt_level = Some(0); } } -#[allow(clippy::find_map, clippy::filter_map)] -fn describe_lints() { - use lintlist::{Level, Lint, ALL_LINTS, LINT_LEVELS}; - use std::collections::HashSet; - - println!( - " -Available lint options: - -W Warn about - -A Allow - -D Deny - -F Forbid (deny and all attempts to override) - -" - ); - - let lint_level = |lint: &Lint| { - LINT_LEVELS - .iter() - .find(|level_mapping| level_mapping.0 == lint.group) - .map(|(_, level)| match level { - Level::Allow => "allow", - Level::Warn => "warn", - Level::Deny => "deny", - }) - .unwrap() - }; - - let mut lints: Vec<_> = ALL_LINTS.iter().collect(); - // The sort doesn't case-fold but it's doubtful we care. - lints.sort_by_cached_key(|x: &&Lint| (lint_level(x), x.name)); - - let max_lint_name_len = lints - .iter() - .map(|lint| lint.name.len()) - .map(|len| len + "clippy::".len()) - .max() - .unwrap_or(0); - - let padded = |x: &str| { - let mut s = " ".repeat(max_lint_name_len - x.chars().count()); - s.push_str(x); - s - }; - - let scoped = |x: &str| format!("clippy::{}", x); - - let lint_groups: HashSet<_> = lints.iter().map(|lint| lint.group).collect(); - - println!("Lint checks provided by clippy:\n"); - println!(" {} {:7.7} meaning", padded("name"), "default"); - println!(" {} {:7.7} -------", padded("----"), "-------"); - - let print_lints = |lints: &[&Lint]| { - for lint in lints { - let name = lint.name.replace("_", "-"); - println!( - " {} {:7.7} {}", - padded(&scoped(&name)), - lint_level(lint), - lint.desc - ); - } - println!("\n"); - }; - - print_lints(&lints); - - let max_group_name_len = std::cmp::max( - "clippy::all".len(), - lint_groups - .iter() - .map(|group| group.len()) - .map(|len| len + "clippy::".len()) - .max() - .unwrap_or(0), - ); - - let padded_group = |x: &str| { - let mut s = " ".repeat(max_group_name_len - x.chars().count()); - s.push_str(x); - s - }; - - println!("Lint groups provided by clippy:\n"); - println!(" {} sub-lints", padded_group("name")); - println!(" {} ---------", padded_group("----")); - println!(" {} the set of all clippy lints", padded_group("clippy::all")); - - let print_lint_groups = || { - for group in lint_groups { - let name = group.to_lowercase().replace("_", "-"); - let desc = lints - .iter() - .filter(|&lint| lint.group == group) - .map(|lint| lint.name) - .map(|name| name.replace("_", "-")) - .collect::>() - .join(", "); - println!(" {} {}", padded_group(&scoped(&name)), desc); - } - println!("\n"); - }; - - print_lint_groups(); -} - fn display_help() { println!( "\ @@ -207,6 +130,7 @@ fn display_help() { Common options: -h, --help Print this message + --rustc Pass all args to rustc -V, --version Print version info and exit Other options are the same as `cargo check`. @@ -228,13 +152,11 @@ 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 - }; -} +static ICE_HOOK: SyncLazy) + Sync + Send + 'static>> = SyncLazy::new(|| { + 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 @@ -269,15 +191,15 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { ]; for note in &xs { - handler.note_without_error(¬e); + handler.note_without_error(note); } // 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); - } + let num_frames = if backtrace { None } else { Some(2) }; + + interface::try_print_query_stack(&handler, num_frames); } fn toolchain_path(home: Option, toolchain: Option) -> Option { @@ -291,18 +213,13 @@ fn toolchain_path(home: Option, toolchain: Option) -> Option = env::args().collect(); - if orig_args.iter().any(|a| a == "--version" || a == "-V") { - let version_info = rustc_tools_util::get_version_info!(); - println!("{}", version_info); - exit(0); - } - // Get the sysroot, looking from most specific to this invocation to the least: // - command line // - runtime environment @@ -348,6 +265,28 @@ pub fn main() { .map(|pb| pb.to_string_lossy().to_string()) .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust"); + // make "clippy-driver --rustc" work like a subcommand that passes further args to "rustc" + // for example `clippy-driver --rustc --version` will print the rustc version that clippy-driver + // uses + if let Some(pos) = orig_args.iter().position(|arg| arg == "--rustc") { + orig_args.remove(pos); + orig_args[0] = "rustc".to_string(); + + // if we call "rustc", we need to pass --sysroot here as well + let mut args: Vec = orig_args.clone(); + if !have_sys_root_arg { + args.extend(vec!["--sysroot".into(), sys_root]); + }; + + return rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run(); + } + + if orig_args.iter().any(|a| a == "--version" || a == "-V") { + let version_info = rustc_tools_util::get_version_info!(); + println!("{}", version_info); + exit(0); + } + // 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 = orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref()); @@ -362,22 +301,6 @@ pub fn main() { exit(0); } - let should_describe_lints = || { - let args: Vec<_> = env::args().collect(); - args.windows(2).any(|args| { - args[1] == "help" - && match args[0].as_str() { - "-W" | "-A" | "-D" | "-F" => true, - _ => false, - } - }) - }; - - if !wrapper_mode && should_describe_lints() { - describe_lints(); - exit(0); - } - // this conditional check for the --sysroot flag is there so users can call // `clippy_driver` directly // without having to pass --sysroot or anything @@ -386,27 +309,41 @@ pub fn main() { 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").map_or(false, |val| val == "true") - || arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_none(); + let mut no_deps = false; + let clippy_args_var = env::var("CLIPPY_ARGS").ok(); + let clippy_args = clippy_args_var + .as_deref() + .unwrap_or_default() + .split("__CLIPPY_HACKERY__") + .filter_map(|s| match s { + "" => None, + "--no-deps" => { + no_deps = true; + None + }, + _ => Some(s.to_string()), + }) + .chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]) + .collect::>(); + + // We enable Clippy if one of the following conditions is met + // - IF Clippy is run on its test suite OR + // - IF Clippy is run on the main crate, not on deps (`!cap_lints_allow`) THEN + // - IF `--no-deps` is not set (`!no_deps`) OR + // - IF `--no-deps` is set and Clippy is run on the specified primary package + let clippy_tests_set = env::var("__CLIPPY_INTERNAL_TESTS").map_or(false, |val| val == "true"); + let cap_lints_allow = arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some(); + let in_primary_package = env::var("CARGO_PRIMARY_PACKAGE").is_ok(); + + let clippy_enabled = clippy_tests_set || (!cap_lints_allow && (!no_deps || in_primary_package)); + if clippy_enabled { + args.extend(clippy_args); + } if clippy_enabled { - 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() { - None - } else { - Some(s.to_string()) - } - })); - } + rustc_driver::RunCompiler::new(&args, &mut ClippyCallbacks { clippy_args_var }).run() + } else { + rustc_driver::RunCompiler::new(&args, &mut RustcCallbacks { clippy_args_var }).run() } - let mut clippy = ClippyCallbacks; - let mut default = DefaultCallbacks; - let callbacks: &mut (dyn rustc_driver::Callbacks + Send) = - if clippy_enabled { &mut clippy } else { &mut default }; - rustc_driver::run_compiler(&args, callbacks, None, None) })) }