diff --git a/Cargo.toml b/Cargo.toml index 7ab20320e7dc..75e7aeb626e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,11 +43,11 @@ clippy_lints = { version = "0.0.212", path = "clippy_lints" } regex = "1" semver = "0.9" rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"} +lazy_static = "1.0" [dev-dependencies] cargo_metadata = "0.8.0" compiletest_rs = { version = "0.3.23", features = ["tmp"] } -lazy_static = "1.0" clippy-mini-macro-test = { version = "0.2", path = "mini-macro" } serde = { version = "1.0", features = ["derive"] } derive-new = "0.5" diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 452e4e9787db..f57f98d276ca 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -447,6 +447,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con reg.register_late_lint_pass(box serde_api::SerdeAPI); reg.register_early_lint_pass(box utils::internal_lints::ClippyLintsInternal); + reg.register_early_lint_pass(box utils::internal_lints::ProduceIce); reg.register_late_lint_pass(box utils::internal_lints::CompilerLintFunctions::new()); reg.register_late_lint_pass(box utils::internal_lints::LintWithoutLintPass::default()); reg.register_late_lint_pass(box utils::internal_lints::OuterExpnDataPass); @@ -690,6 +691,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con utils::internal_lints::COMPILER_LINT_FUNCTIONS, utils::internal_lints::LINT_WITHOUT_LINT_PASS, utils::internal_lints::OUTER_EXPN_EXPN_DATA, + utils::internal_lints::PRODUCE_ICE, ]); reg.register_lint_group("clippy::all", Some("clippy"), vec![ diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 1ec89cb93f17..e130fe9f1112 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -10,8 +10,10 @@ use rustc::lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintAr use rustc::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; +use syntax::ast; use syntax::ast::{Crate as AstCrate, ItemKind, Name}; use syntax::source_map::Span; +use syntax::visit::FnKind; use syntax_pos::symbol::LocalInternedString; declare_clippy_lint! { @@ -98,6 +100,24 @@ declare_clippy_lint! { "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`" } +declare_clippy_lint! { + /// **What it does:** Not an actual lint. This lint is only meant for testing our customized internal compiler + /// error message by calling `panic`. + /// + /// **Why is this bad?** ICE in large quantities can damage your teeth + /// + /// **Known problems:** None + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// 🍦🍦🍦🍦🍦 + /// ``` + pub PRODUCE_ICE, + internal, + "this message should not appear anywhere as we ICE before and don't emit the lint" +} + declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); impl EarlyLintPass for ClippyLintsInternal { @@ -304,3 +324,22 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OuterExpnDataPass { } } } + +declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); + +impl EarlyLintPass for ProduceIce { + fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: &ast::FnDecl, _: Span, _: ast::NodeId) { + if is_trigger_fn(fn_kind) { + panic!("Testing the ICE message"); + } + } +} + +fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool { + match fn_kind { + FnKind::ItemFn(ident, ..) | FnKind::Method(ident, ..) => { + ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy" + }, + FnKind::Closure(..) => false, + } +} diff --git a/src/driver.rs b/src/driver.rs index 359d2f8530cb..524a31673c4d 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -4,13 +4,21 @@ // 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_errors; +#[allow(unused_extern_crates)] extern crate rustc_interface; +use rustc::ty::TyCtxt; use rustc_interface::interface; use rustc_tools_util::*; +use lazy_static::lazy_static; +use std::borrow::Cow; +use std::panic; use std::path::{Path, PathBuf}; use std::process::{exit, Command}; @@ -245,9 +253,64 @@ You can use tool lints to allow or deny lints from your code, eg.: ); } +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); + handler.abort_if_errors_and_should_abort(); + } + + 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 = std::env::var_os("RUST_BACKTRACE").map(|x| &x != "0").unwrap_or(false); + + if backtrace { + TyCtxt::try_print_query_stack(&handler); + } +} + pub fn main() { rustc_driver::init_rustc_env_logger(); - rustc_driver::install_ice_hook(); + lazy_static::initialize(&ICE_HOOK); exit( rustc_driver::catch_fatal_errors(move || { use std::env; diff --git a/tests/ui/custom_ice_message.rs b/tests/ui/custom_ice_message.rs new file mode 100644 index 000000000000..2f58fbce30bf --- /dev/null +++ b/tests/ui/custom_ice_message.rs @@ -0,0 +1,9 @@ +// rustc-env:RUST_BACKTRACE=0 +// normalize-stderr-test: "Clippy version: .*" -> "Clippy version: foo" +// normalize-stderr-test: "internal_lints.rs:\d*:\d*" -> "internal_lints.rs" + +#![deny(clippy::internal)] + +fn it_looks_like_you_are_trying_to_kill_clippy() {} + +fn main() {} diff --git a/tests/ui/custom_ice_message.stderr b/tests/ui/custom_ice_message.stderr new file mode 100644 index 000000000000..817e48724337 --- /dev/null +++ b/tests/ui/custom_ice_message.stderr @@ -0,0 +1,11 @@ +thread 'rustc' panicked at 'Testing the ICE message', clippy_lints/src/utils/internal_lints.rs +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace. + +error: internal compiler error: unexpected panic + +note: the compiler unexpectedly panicked. this is a bug. + +note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new + +note: Clippy version: foo +