@@ -2,12 +2,15 @@ use crate::{LateContext, LateLintPass, LintContext};
22use rustc_ast as ast;
33use rustc_errors:: { pluralize, Applicability } ;
44use rustc_hir as hir;
5+ use rustc_infer:: infer:: TyCtxtInferExt ;
56use rustc_middle:: lint:: in_external_macro;
67use rustc_middle:: ty;
8+ use rustc_middle:: ty:: subst:: InternalSubsts ;
79use rustc_parse_format:: { ParseMode , Parser , Piece } ;
810use rustc_session:: lint:: FutureIncompatibilityReason ;
911use rustc_span:: edition:: Edition ;
1012use rustc_span:: { hygiene, sym, symbol:: kw, symbol:: SymbolStr , InnerSpan , Span , Symbol } ;
13+ use rustc_trait_selection:: infer:: InferCtxtExt ;
1114
1215declare_lint ! {
1316 /// 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
129132 ty. ty_adt_def( ) ,
130133 Some ( ty_def) if cx. tcx. is_diagnostic_item( sym:: string_type, ty_def. did) ,
131134 ) ;
132- l. span_suggestion_verbose (
133- arg_span. shrink_to_lo ( ) ,
134- "add a \" {}\" format string to Display the message" ,
135- "\" {}\" , " . into ( ) ,
136- if is_str {
137- Applicability :: MachineApplicable
138- } else {
139- Applicability :: MaybeIncorrect
140- } ,
141- ) ;
142- if !is_str && panic == sym:: std_panic_macro {
135+
136+ let ( suggest_display, suggest_debug) = cx. tcx . infer_ctxt ( ) . enter ( |infcx| {
137+ let display = is_str || cx. tcx . get_diagnostic_item ( sym:: display_trait) . map ( |t| {
138+ infcx. type_implements_trait ( t, ty, InternalSubsts :: empty ( ) , cx. param_env ) . may_apply ( )
139+ } ) == Some ( true ) ;
140+ let debug = !display && cx. tcx . get_diagnostic_item ( sym:: debug_trait) . map ( |t| {
141+ infcx. type_implements_trait ( t, ty, InternalSubsts :: empty ( ) , cx. param_env ) . may_apply ( )
142+ } ) == Some ( true ) ;
143+ ( display, debug)
144+ } ) ;
145+
146+ let suggest_panic_any = !is_str && panic == sym:: std_panic_macro;
147+
148+ let fmt_applicability = if suggest_panic_any {
149+ // If we can use panic_any, use that as the MachineApplicable suggestion.
150+ Applicability :: MaybeIncorrect
151+ } else {
152+ // If we don't suggest panic_any, using a format string is our best bet.
153+ Applicability :: MachineApplicable
154+ } ;
155+
156+ if suggest_display {
157+ l. span_suggestion_verbose (
158+ arg_span. shrink_to_lo ( ) ,
159+ "add a \" {}\" format string to Display the message" ,
160+ "\" {}\" , " . into ( ) ,
161+ fmt_applicability,
162+ ) ;
163+ } else if suggest_debug {
164+ l. span_suggestion_verbose (
165+ arg_span. shrink_to_lo ( ) ,
166+ & format ! (
167+ "add a \" {{:?}}\" format string to use the Debug implementation of `{}`" ,
168+ ty,
169+ ) ,
170+ "\" {:?}\" , " . into ( ) ,
171+ fmt_applicability,
172+ ) ;
173+ }
174+
175+ if suggest_panic_any {
143176 if let Some ( ( open, close, del) ) = find_delimiters ( cx, span) {
144177 l. multipart_suggestion (
145- "or use std::panic::panic_any instead" ,
178+ & format ! (
179+ "{}use std::panic::panic_any instead" ,
180+ if suggest_display || suggest_debug {
181+ "or "
182+ } else {
183+ ""
184+ } ,
185+ ) ,
146186 if del == '(' {
147187 vec ! [ ( span. until( open) , "std::panic::panic_any" . into( ) ) ]
148188 } else {
0 commit comments