@@ -47,24 +47,52 @@ impl<'tcx> LateLintPass<'tcx> for PanicFmt {
4747fn check_panic < ' tcx > ( cx : & LateContext < ' tcx > , f : & ' tcx hir:: Expr < ' tcx > , arg : & ' tcx hir:: Expr < ' tcx > ) {
4848 if let hir:: ExprKind :: Lit ( lit) = & arg. kind {
4949 if let ast:: LitKind :: Str ( sym, _) = lit. node {
50- if sym. as_str ( ) . contains ( & [ '{' , '}' ] [ ..] ) {
51- let expn = f. span . ctxt ( ) . outer_expn_data ( ) ;
52- if let Some ( id) = expn. macro_def_id {
53- if cx. tcx . is_diagnostic_item ( sym:: std_panic_macro, id)
54- || cx. tcx . is_diagnostic_item ( sym:: core_panic_macro, id)
55- {
56- let expn = {
57- // Unwrap another level of macro expansion if this
58- // panic!() was expanded from assert!().
59- let parent = expn. call_site . ctxt ( ) . outer_expn_data ( ) ;
60- if parent. macro_def_id . map_or ( false , |id| {
61- cx. tcx . is_diagnostic_item ( sym:: assert_macro, id)
62- } ) {
63- parent
64- } else {
65- expn
50+ let s = sym. as_str ( ) ;
51+ let open = s. find ( '{' ) ;
52+ let close = s[ open. unwrap_or ( 0 ) ..] . find ( '}' ) ;
53+ let looks_like_placeholder = match ( open, close) {
54+ ( Some ( _) , Some ( _) ) => true ,
55+ ( Some ( _) , None ) | ( None , Some ( _) ) => false ,
56+ ( None , None ) => return // OK, no braces.
57+ } ;
58+ let expn = f. span . ctxt ( ) . outer_expn_data ( ) ;
59+ if let Some ( id) = expn. macro_def_id {
60+ if cx. tcx . is_diagnostic_item ( sym:: std_panic_macro, id)
61+ || cx. tcx . is_diagnostic_item ( sym:: core_panic_macro, id)
62+ {
63+ let expn = {
64+ // Unwrap another level of macro expansion if this
65+ // panic!() was expanded from assert!().
66+ let parent = expn. call_site . ctxt ( ) . outer_expn_data ( ) ;
67+ if parent. macro_def_id . map_or ( false , |id| {
68+ cx. tcx . is_diagnostic_item ( sym:: assert_macro, id)
69+ } ) {
70+ parent
71+ } else {
72+ expn
73+ }
74+ } ;
75+ if looks_like_placeholder {
76+ cx. struct_span_lint ( PANIC_FMT , arg. span . source_callsite ( ) , |lint| {
77+ let mut l = lint. build ( "Panic message contains an unused formatting placeholder" ) ;
78+ l. note ( "This message is not used as a format string when given without arguments, but will be in a future Rust version" ) ;
79+ if expn. call_site . contains ( arg. span ) {
80+ l. span_suggestion (
81+ arg. span . shrink_to_hi ( ) ,
82+ "add the missing argument(s)" ,
83+ ", argument" . into ( ) ,
84+ Applicability :: HasPlaceholders ,
85+ ) ;
86+ l. span_suggestion (
87+ arg. span . shrink_to_lo ( ) ,
88+ "or add a \" {}\" format string to use the message literally" ,
89+ "\" {}\" , " . into ( ) ,
90+ Applicability :: MaybeIncorrect ,
91+ ) ;
6692 }
67- } ;
93+ l. emit ( ) ;
94+ } ) ;
95+ } else {
6896 cx. struct_span_lint ( PANIC_FMT , expn. call_site , |lint| {
6997 let mut l = lint. build ( "Panic message contains a brace" ) ;
7098 l. note ( "This message is not used as a format string, but will be in a future Rust version" ) ;
0 commit comments