@@ -1148,28 +1148,6 @@ fn lint_or_fun_call(cx: &LateContext<'_, '_>, expr: &hir::Expr, method_span: Spa
11481148
11491149/// Checks for the `EXPECT_FUN_CALL` lint.
11501150fn 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-
11731151 fn generate_format_arg_snippet (
11741152 cx : & LateContext < ' _ , ' _ > ,
11751153 a : & hir:: Expr ,
@@ -1189,93 +1167,115 @@ fn lint_expect_fun_call(cx: &LateContext<'_, '_>, expr: &hir::Expr, method_span:
11891167 unreachable ! ( )
11901168 }
11911169
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 ,
12171182 }
1183+ }
12181184
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+ }
12261188
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+ } ;
12301197
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 ,
12351219 } ;
1236- let span_replace_word = method_span . with_hi ( span . hi ( ) ) ;
1220+ }
12371221
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 ( ) ) ;
12411223
1242- args . extend ( generate_format_arg_snippet ( cx , fmt_args , & mut applicability) ) ;
1224+ let mut applicability = Applicability :: MachineApplicable ;
12431225
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 ] ;
12451232
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( ) ] ;
12551235
1256- return ;
1257- }
1236+ args. extend ( generate_format_arg_snippet ( cx, fmt_args, & mut applicability) ) ;
12581237
1259- let mut applicability = Applicability :: MachineApplicable ;
1260- let sugg: Cow < ' _ , _ > = snippet_with_applicability ( cx, arg. span , ".." , & mut applicability) ;
1238+ let sugg = args. join ( ", " ) ;
12611239
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+ }
12711253 }
12721254
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 ;
12771263 }
1264+ } ;
1265+
1266+ if requires_conv {
1267+ arg_root_snippet. to_mut ( ) . push_str ( ".to_string()" ) ;
12781268 }
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+ ) ;
12791279}
12801280
12811281/// Checks for the `CLONE_ON_COPY` lint.
0 commit comments