diff --git a/Cargo.toml b/Cargo.toml
index dabc4a62e6d..6b757ac0a2f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -40,6 +40,7 @@ semver = "0.9"
 rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"}
 git2 = { version = "0.10", optional = true }
 tempfile = { version = "3.1.0", optional = true }
+lazy_static = "1.0"
 
 [dev-dependencies]
 cargo_metadata = "0.9.0"
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 07fe9f47465..c1113daadd8 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -963,6 +963,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
     let array_size_threshold = conf.array_size_threshold;
     store.register_late_pass(move || box large_stack_arrays::LargeStackArrays::new(array_size_threshold));
     store.register_early_pass(|| box as_conversions::AsConversions);
+    store.register_early_pass(|| box utils::internal_lints::ProduceIce);
 
     store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
         LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -1057,6 +1058,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
         LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS),
         LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS),
         LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA),
+        LintId::of(&utils::internal_lints::PRODUCE_ICE),
     ]);
 
     store.register_group(true, "clippy::all", Some("clippy"), vec![
diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs
index 5bf10558221..cb75720f120 100644
--- a/clippy_lints/src/utils/internal_lints.rs
+++ b/clippy_lints/src/utils/internal_lints.rs
@@ -11,9 +11,11 @@ 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_pos::symbol::SymbolStr;
+use syntax::visit::FnKind;
 
 declare_clippy_lint! {
     /// **What it does:** Checks for various things we like to keep tidy in clippy.
@@ -99,6 +101,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 {
@@ -302,3 +322,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() == "should_trigger_an_ice_in_clippy"
+        },
+        FnKind::Closure(..) => false,
+    }
+}
diff --git a/src/driver.rs b/src/driver.rs
index 0b14e1d5a5a..76aaae3747b 100644
--- a/src/driver.rs
+++ b/src/driver.rs
@@ -5,13 +5,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};
 
@@ -221,9 +229,62 @@ 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<dyn Fn(&panic::PanicInfo<'_>) + 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::<rustc_errors::ExplicitBug>() {
+        let d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
+        handler.emit_diagnostic(&d);
+        handler.abort_if_errors_and_should_abort();
+    }
+
+    let xs: Vec<Cow<'static, str>> = vec![
+        "the compiler unexpectedly panicked. this is a bug.".into(),
+        format!("we would appreciate a bug report: {}", bug_report_url).into(),
+        format!("rustc {}", option_env!("CFG_VERSION").unwrap_or("unknown_version")).into(),
+    ];
+
+    for note in &xs {
+        handler.note_without_error(&note);
+    }
+
+    // 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();
+    }
+}
+
 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 00000000000..8ad5ebba7f3
--- /dev/null
+++ b/tests/ui/custom_ice_message.rs
@@ -0,0 +1,5 @@
+#![deny(clippy::internal)]
+
+fn should_trigger_an_ice_in_clippy() {}
+
+fn main() {}
diff --git a/tests/ui/custom_ice_message.stderr b/tests/ui/custom_ice_message.stderr
new file mode 100644
index 00000000000..85c9f42a2de
--- /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:333:13
+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: rustc unknown_version
+