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 {