@@ -2,12 +2,15 @@ use crate::{LateContext, LateLintPass, LintContext};
2
2
use rustc_ast as ast;
3
3
use rustc_errors:: { pluralize, Applicability } ;
4
4
use rustc_hir as hir;
5
+ use rustc_infer:: infer:: TyCtxtInferExt ;
5
6
use rustc_middle:: lint:: in_external_macro;
6
7
use rustc_middle:: ty;
8
+ use rustc_middle:: ty:: subst:: InternalSubsts ;
7
9
use rustc_parse_format:: { ParseMode , Parser , Piece } ;
8
10
use rustc_session:: lint:: FutureIncompatibilityReason ;
9
11
use rustc_span:: edition:: Edition ;
10
12
use rustc_span:: { hygiene, sym, symbol:: kw, symbol:: SymbolStr , InnerSpan , Span , Symbol } ;
13
+ use rustc_trait_selection:: infer:: InferCtxtExt ;
11
14
12
15
declare_lint ! {
13
16
/// 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
129
132
ty. ty_adt_def( ) ,
130
133
Some ( ty_def) if cx. tcx. is_diagnostic_item( sym:: string_type, ty_def. did) ,
131
134
) ;
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 {
143
176
if let Some ( ( open, close, del) ) = find_delimiters ( cx, span) {
144
177
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
+ ) ,
146
186
if del == '(' {
147
187
vec ! [ ( span. until( open) , "std::panic::panic_any" . into( ) ) ]
148
188
} else {
0 commit comments