@@ -1148,28 +1148,6 @@ fn lint_or_fun_call(cx: &LateContext<'_, '_>, expr: &hir::Expr, method_span: Spa
1148
1148
1149
1149
/// Checks for the `EXPECT_FUN_CALL` lint.
1150
1150
fn lint_expect_fun_call ( cx : & LateContext < ' _ , ' _ > , expr : & hir:: Expr , method_span : Span , name : & str , args : & [ hir:: Expr ] ) {
1151
- fn extract_format_args ( arg : & hir:: Expr ) -> Option < ( & hir:: Expr , & hir:: Expr ) > {
1152
- let arg = match & arg. node {
1153
- hir:: ExprKind :: AddrOf ( _, expr) => expr,
1154
- hir:: ExprKind :: MethodCall ( method_name, _, args)
1155
- if method_name. ident . name == "as_str" || method_name. ident . name == "as_ref" =>
1156
- {
1157
- & args[ 0 ]
1158
- } ,
1159
- _ => arg,
1160
- } ;
1161
-
1162
- if let hir:: ExprKind :: Call ( ref inner_fun, ref inner_args) = arg. node {
1163
- if is_expn_of ( inner_fun. span , "format" ) . is_some ( ) && inner_args. len ( ) == 1 {
1164
- if let hir:: ExprKind :: Call ( _, format_args) = & inner_args[ 0 ] . node {
1165
- return Some ( ( & format_args[ 0 ] , & format_args[ 1 ] ) ) ;
1166
- }
1167
- }
1168
- }
1169
-
1170
- None
1171
- }
1172
-
1173
1151
fn generate_format_arg_snippet (
1174
1152
cx : & LateContext < ' _ , ' _ > ,
1175
1153
a : & hir:: Expr ,
@@ -1189,93 +1167,115 @@ fn lint_expect_fun_call(cx: &LateContext<'_, '_>, expr: &hir::Expr, method_span:
1189
1167
unreachable ! ( )
1190
1168
}
1191
1169
1192
- fn check_general_case (
1193
- cx : & LateContext < ' _ , ' _ > ,
1194
- name : & str ,
1195
- method_span : Span ,
1196
- self_expr : & hir:: Expr ,
1197
- arg : & hir:: Expr ,
1198
- span : Span ,
1199
- ) {
1200
- fn is_call ( node : & hir:: ExprKind ) -> bool {
1201
- match node {
1202
- hir:: ExprKind :: AddrOf ( _, expr) => {
1203
- is_call ( & expr. node )
1204
- } ,
1205
- hir:: ExprKind :: Call ( ..)
1206
- | hir:: ExprKind :: MethodCall ( ..)
1207
- // These variants are debatable or require further examination
1208
- | hir:: ExprKind :: If ( ..)
1209
- | hir:: ExprKind :: Match ( ..)
1210
- | hir:: ExprKind :: Block { .. } => true ,
1211
- _ => false ,
1212
- }
1213
- }
1214
-
1215
- if name != "expect" {
1216
- return ;
1170
+ fn is_call ( node : & hir:: ExprKind ) -> bool {
1171
+ match node {
1172
+ hir:: ExprKind :: AddrOf ( _, expr) => {
1173
+ is_call ( & expr. node )
1174
+ } ,
1175
+ hir:: ExprKind :: Call ( ..)
1176
+ | hir:: ExprKind :: MethodCall ( ..)
1177
+ // These variants are debatable or require further examination
1178
+ | hir:: ExprKind :: If ( ..)
1179
+ | hir:: ExprKind :: Match ( ..)
1180
+ | hir:: ExprKind :: Block { .. } => true ,
1181
+ _ => false ,
1217
1182
}
1183
+ }
1218
1184
1219
- let self_type = cx. tables . expr_ty ( self_expr) ;
1220
- let known_types = & [ & paths:: OPTION , & paths:: RESULT ] ;
1221
-
1222
- // if not a known type, return early
1223
- if known_types. iter ( ) . all ( |& k| !match_type ( cx, self_type, k) ) {
1224
- return ;
1225
- }
1185
+ if args. len ( ) != 2 || name != "expect" || !is_call ( & args[ 1 ] . node ) {
1186
+ return ;
1187
+ }
1226
1188
1227
- if !is_call ( & arg. node ) {
1228
- return ;
1229
- }
1189
+ let receiver_type = cx. tables . expr_ty ( & args[ 0 ] ) ;
1190
+ let closure_args = if match_type ( cx, receiver_type, & paths:: OPTION ) {
1191
+ "||"
1192
+ } else if match_type ( cx, receiver_type, & paths:: RESULT ) {
1193
+ "|_|"
1194
+ } else {
1195
+ return ;
1196
+ } ;
1230
1197
1231
- let closure = if match_type ( cx, self_type, & paths:: OPTION ) {
1232
- "||"
1233
- } else {
1234
- "|_|"
1198
+ // Strip off `&`, `as_ref()` and `as_str()` until we're left with either a `String` or `&str`
1199
+ // which we call `arg_root`.
1200
+ let mut arg_root = & args[ 1 ] ;
1201
+ loop {
1202
+ arg_root = match & arg_root. node {
1203
+ hir:: ExprKind :: AddrOf ( _, expr) => expr,
1204
+ hir:: ExprKind :: MethodCall ( method_name, _, call_args) => {
1205
+ if call_args. len ( ) == 1
1206
+ && ( method_name. ident . name == "as_str" || method_name. ident . name == "as_ref" )
1207
+ && {
1208
+ let arg_type = cx. tables . expr_ty ( & call_args[ 0 ] ) ;
1209
+ let base_type = walk_ptrs_ty ( arg_type) ;
1210
+ base_type. sty == ty:: Str || match_type ( cx, base_type, & paths:: STRING )
1211
+ }
1212
+ {
1213
+ & call_args[ 0 ]
1214
+ } else {
1215
+ break ;
1216
+ }
1217
+ } ,
1218
+ _ => break ,
1235
1219
} ;
1236
- let span_replace_word = method_span . with_hi ( span . hi ( ) ) ;
1220
+ }
1237
1221
1238
- if let Some ( ( fmt_spec, fmt_args) ) = extract_format_args ( arg) {
1239
- let mut applicability = Applicability :: MachineApplicable ;
1240
- let mut args = vec ! [ snippet( cx, fmt_spec. span, ".." ) . into_owned( ) ] ;
1222
+ let span_replace_word = method_span. with_hi ( expr. span . hi ( ) ) ;
1241
1223
1242
- args . extend ( generate_format_arg_snippet ( cx , fmt_args , & mut applicability) ) ;
1224
+ let mut applicability = Applicability :: MachineApplicable ;
1243
1225
1244
- let sugg = args. join ( ", " ) ;
1226
+ //Special handling for `format!` as arg_root
1227
+ if let hir:: ExprKind :: Call ( ref inner_fun, ref inner_args) = arg_root. node {
1228
+ if is_expn_of ( inner_fun. span , "format" ) . is_some ( ) && inner_args. len ( ) == 1 {
1229
+ if let hir:: ExprKind :: Call ( _, format_args) = & inner_args[ 0 ] . node {
1230
+ let fmt_spec = & format_args[ 0 ] ;
1231
+ let fmt_args = & format_args[ 1 ] ;
1245
1232
1246
- span_lint_and_sugg (
1247
- cx,
1248
- EXPECT_FUN_CALL ,
1249
- span_replace_word,
1250
- & format ! ( "use of `{}` followed by a function call" , name) ,
1251
- "try this" ,
1252
- format ! ( "unwrap_or_else({} panic!({}))" , closure, sugg) ,
1253
- applicability,
1254
- ) ;
1233
+ let mut applicability = Applicability :: MachineApplicable ;
1234
+ let mut args = vec ! [ snippet( cx, fmt_spec. span, ".." ) . into_owned( ) ] ;
1255
1235
1256
- return ;
1257
- }
1236
+ args. extend ( generate_format_arg_snippet ( cx, fmt_args, & mut applicability) ) ;
1258
1237
1259
- let mut applicability = Applicability :: MachineApplicable ;
1260
- let sugg: Cow < ' _ , _ > = snippet_with_applicability ( cx, arg. span , ".." , & mut applicability) ;
1238
+ let sugg = args. join ( ", " ) ;
1261
1239
1262
- span_lint_and_sugg (
1263
- cx,
1264
- EXPECT_FUN_CALL ,
1265
- span_replace_word,
1266
- & format ! ( "use of `{}` followed by a function call" , name) ,
1267
- "try this" ,
1268
- format ! ( "unwrap_or_else({} {{ let msg = {}; panic!(msg) }}))" , closure, sugg) ,
1269
- applicability,
1270
- ) ;
1240
+ span_lint_and_sugg (
1241
+ cx,
1242
+ EXPECT_FUN_CALL ,
1243
+ span_replace_word,
1244
+ & format ! ( "use of `{}` followed by a function call" , name) ,
1245
+ "try this" ,
1246
+ format ! ( "unwrap_or_else({} panic!({}))" , closure_args, sugg) ,
1247
+ applicability,
1248
+ ) ;
1249
+
1250
+ return ;
1251
+ }
1252
+ }
1271
1253
}
1272
1254
1273
- if args. len ( ) == 2 {
1274
- match args[ 1 ] . node {
1275
- hir:: ExprKind :: Lit ( _) => { } ,
1276
- _ => check_general_case ( cx, name, method_span, & args[ 0 ] , & args[ 1 ] , expr. span ) ,
1255
+ // If root_arg is `&'static str` or `String` we can use it directly in the `panic!` call otherwise
1256
+ // we must use `to_string` to convert it.
1257
+ let mut arg_root_snippet: Cow < ' _ , _ > = snippet_with_applicability ( cx, arg_root. span , ".." , & mut applicability) ;
1258
+ let arg_root_ty = cx. tables . expr_ty ( arg_root) ;
1259
+ let mut requires_conv = !match_type ( cx, arg_root_ty, & paths:: STRING ) ;
1260
+ if let ty:: Ref ( ty:: ReStatic , ty, ..) = arg_root_ty. sty {
1261
+ if ty. sty == ty:: Str {
1262
+ requires_conv = false ;
1277
1263
}
1264
+ } ;
1265
+
1266
+ if requires_conv {
1267
+ arg_root_snippet. to_mut ( ) . push_str ( ".to_string()" ) ;
1278
1268
}
1269
+
1270
+ span_lint_and_sugg (
1271
+ cx,
1272
+ EXPECT_FUN_CALL ,
1273
+ span_replace_word,
1274
+ & format ! ( "use of `{}` followed by a function call" , name) ,
1275
+ "try this" ,
1276
+ format ! ( "unwrap_or_else({} {{ panic!({}) }})" , closure_args, arg_root_snippet) ,
1277
+ applicability,
1278
+ ) ;
1279
1279
}
1280
1280
1281
1281
/// Checks for the `CLONE_ON_COPY` lint.
0 commit comments