about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMara Bos <m-ou.se@m-ou.se>2021-08-16 17:25:35 +0200
committerMara Bos <m-ou.se@m-ou.se>2021-08-16 17:25:35 +0200
commit0a313250a4b34effd1f3a9b9adaf01fe01989934 (patch)
treeede7e071d16b9483dc40f21d3d700e94e3cbae58
parent23461b210f1b0a121592a18fc4fb666106006668 (diff)
Improve non_fmt_panics suggestion based on trait impls.
-rw-r--r--Cargo.lock1
-rw-r--r--compiler/rustc_lint/Cargo.toml1
-rw-r--r--compiler/rustc_lint/src/non_fmt_panic.rs64
3 files changed, 54 insertions, 12 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 940608975c5..30b59d7378b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3932,6 +3932,7 @@ dependencies = [
  "rustc_feature",
  "rustc_hir",
  "rustc_index",
+ "rustc_infer",
  "rustc_middle",
  "rustc_parse_format",
  "rustc_serialize",
diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml
index ad10db64302..8269c56e429 100644
--- a/compiler/rustc_lint/Cargo.toml
+++ b/compiler/rustc_lint/Cargo.toml
@@ -21,3 +21,4 @@ rustc_session = { path = "../rustc_session" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
 rustc_parse_format = { path = "../rustc_parse_format" }
+rustc_infer = { path = "../rustc_infer" }
diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs
index 3349063e5dc..7e2b62f00cc 100644
--- a/compiler/rustc_lint/src/non_fmt_panic.rs
+++ b/compiler/rustc_lint/src/non_fmt_panic.rs
@@ -2,12 +2,15 @@ use crate::{LateContext, LateLintPass, LintContext};
 use rustc_ast as ast;
 use rustc_errors::{pluralize, Applicability};
 use rustc_hir as hir;
+use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty;
+use rustc_middle::ty::subst::InternalSubsts;
 use rustc_parse_format::{ParseMode, Parser, Piece};
 use rustc_session::lint::FutureIncompatibilityReason;
 use rustc_span::edition::Edition;
 use rustc_span::{hygiene, sym, symbol::kw, symbol::SymbolStr, InnerSpan, Span, Symbol};
+use rustc_trait_selection::infer::InferCtxtExt;
 
 declare_lint! {
     /// The `non_fmt_panics` lint detects `panic!(..)` invocations where the first
@@ -129,20 +132,57 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
                 ty.ty_adt_def(),
                 Some(ty_def) if cx.tcx.is_diagnostic_item(sym::string_type, ty_def.did),
             );
-            l.span_suggestion_verbose(
-                arg_span.shrink_to_lo(),
-                "add a \"{}\" format string to Display the message",
-                "\"{}\", ".into(),
-                if is_str {
-                    Applicability::MachineApplicable
-                } else {
-                    Applicability::MaybeIncorrect
-                },
-            );
-            if !is_str && panic == sym::std_panic_macro {
+
+            let (suggest_display, suggest_debug) = cx.tcx.infer_ctxt().enter(|infcx| {
+                let display = is_str || cx.tcx.get_diagnostic_item(sym::display_trait).map(|t| {
+                    infcx.type_implements_trait(t, ty, InternalSubsts::empty(), cx.param_env).may_apply()
+                }) == Some(true);
+                let debug = !display && cx.tcx.get_diagnostic_item(sym::debug_trait).map(|t| {
+                    infcx.type_implements_trait(t, ty, InternalSubsts::empty(), cx.param_env).may_apply()
+                }) == Some(true);
+                (display, debug)
+            });
+
+            let suggest_panic_any = !is_str && panic == sym::std_panic_macro;
+
+            let fmt_applicability = if suggest_panic_any {
+                // If we can use panic_any, use that as the MachineApplicable suggestion.
+                Applicability::MaybeIncorrect
+            } else {
+                // If we don't suggest panic_any, using a format string is our best bet.
+                Applicability::MachineApplicable
+            };
+
+            if suggest_display {
+                l.span_suggestion_verbose(
+                    arg_span.shrink_to_lo(),
+                    "add a \"{}\" format string to Display the message",
+                    "\"{}\", ".into(),
+                    fmt_applicability,
+                );
+            } else if suggest_debug {
+                l.span_suggestion_verbose(
+                    arg_span.shrink_to_lo(),
+                    &format!(
+                        "add a \"{{:?}}\" format string to use the Debug implementation of `{}`",
+                        ty,
+                    ),
+                    "\"{:?}\", ".into(),
+                    fmt_applicability,
+                );
+            }
+
+            if suggest_panic_any {
                 if let Some((open, close, del)) = find_delimiters(cx, span) {
                     l.multipart_suggestion(
-                        "or use std::panic::panic_any instead",
+                        &format!(
+                            "{}use std::panic::panic_any instead",
+                            if suggest_display || suggest_debug {
+                                "or "
+                            } else {
+                                ""
+                            },
+                        ),
                         if del == '(' {
                             vec![(span.until(open), "std::panic::panic_any".into())]
                         } else {