@@ -2,13 +2,15 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
2
2
use clippy_utils:: macros:: { FormatArgsStorage , format_args_inputs_span, root_macro_call_first_node} ;
3
3
use clippy_utils:: source:: snippet_with_applicability;
4
4
use clippy_utils:: ty:: { is_type_diagnostic_item, is_type_lang_item} ;
5
+ use clippy_utils:: visitors:: for_each_expr;
6
+ use clippy_utils:: { contains_return, is_inside_always_const_context, peel_blocks} ;
5
7
use rustc_errors:: Applicability ;
6
8
use rustc_hir as hir;
7
9
use rustc_lint:: LateContext ;
8
- use rustc_middle:: ty;
9
10
use rustc_span:: symbol:: sym;
10
11
use rustc_span:: { Span , Symbol } ;
11
12
use std:: borrow:: Cow ;
13
+ use std:: ops:: ControlFlow ;
12
14
13
15
use super :: EXPECT_FUN_CALL ;
14
16
@@ -23,10 +25,10 @@ pub(super) fn check<'tcx>(
23
25
receiver : & ' tcx hir:: Expr < ' tcx > ,
24
26
args : & ' tcx [ hir:: Expr < ' tcx > ] ,
25
27
) {
26
- // Strip `&`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or
28
+ // Strip `{}`, ` &`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or
27
29
// `&str`
28
30
fn get_arg_root < ' a > ( cx : & LateContext < ' _ > , arg : & ' a hir:: Expr < ' a > ) -> & ' a hir:: Expr < ' a > {
29
- let mut arg_root = arg;
31
+ let mut arg_root = peel_blocks ( arg) ;
30
32
loop {
31
33
arg_root = match & arg_root. kind {
32
34
hir:: ExprKind :: AddrOf ( hir:: BorrowKind :: Ref , _, expr) => expr,
@@ -47,124 +49,68 @@ pub(super) fn check<'tcx>(
47
49
arg_root
48
50
}
49
51
50
- // Only `&'static str` or `String` can be used directly in the `panic!`. Other types should be
51
- // converted to string.
52
- fn requires_to_string ( cx : & LateContext < ' _ > , arg : & hir:: Expr < ' _ > ) -> bool {
53
- let arg_ty = cx. typeck_results ( ) . expr_ty ( arg) ;
54
- if is_type_lang_item ( cx, arg_ty, hir:: LangItem :: String ) {
55
- return false ;
56
- }
57
- if let ty:: Ref ( _, ty, ..) = arg_ty. kind ( )
58
- && ty. is_str ( )
59
- && can_be_static_str ( cx, arg)
60
- {
61
- return false ;
62
- }
63
- true
52
+ fn contains_call < ' a > ( cx : & LateContext < ' a > , arg : & ' a hir:: Expr < ' a > ) -> bool {
53
+ for_each_expr ( cx, arg, |expr| {
54
+ if matches ! ( expr. kind, hir:: ExprKind :: MethodCall { .. } | hir:: ExprKind :: Call { .. } )
55
+ && !is_inside_always_const_context ( cx. tcx , expr. hir_id )
56
+ {
57
+ ControlFlow :: Break ( ( ) )
58
+ } else {
59
+ ControlFlow :: Continue ( ( ) )
60
+ }
61
+ } )
62
+ . is_some ( )
64
63
}
65
64
66
- // Check if an expression could have type `&'static str`, knowing that it
67
- // has type `&str` for some lifetime.
68
- fn can_be_static_str ( cx : & LateContext < ' _ > , arg : & hir:: Expr < ' _ > ) -> bool {
69
- match arg. kind {
70
- hir:: ExprKind :: Lit ( _) => true ,
71
- hir:: ExprKind :: Call ( fun, _) => {
72
- if let hir:: ExprKind :: Path ( ref p) = fun. kind {
73
- match cx. qpath_res ( p, fun. hir_id ) {
74
- hir:: def:: Res :: Def ( hir:: def:: DefKind :: Fn | hir:: def:: DefKind :: AssocFn , def_id) => matches ! (
75
- cx. tcx. fn_sig( def_id) . instantiate_identity( ) . output( ) . skip_binder( ) . kind( ) ,
76
- ty:: Ref ( re, ..) if re. is_static( ) ,
77
- ) ,
78
- _ => false ,
79
- }
80
- } else {
81
- false
82
- }
83
- } ,
84
- hir:: ExprKind :: MethodCall ( ..) => {
85
- cx. typeck_results ( )
86
- . type_dependent_def_id ( arg. hir_id )
87
- . is_some_and ( |method_id| {
88
- matches ! (
89
- cx. tcx. fn_sig( method_id) . instantiate_identity( ) . output( ) . skip_binder( ) . kind( ) ,
90
- ty:: Ref ( re, ..) if re. is_static( )
91
- )
92
- } )
93
- } ,
94
- hir:: ExprKind :: Path ( ref p) => matches ! (
95
- cx. qpath_res( p, arg. hir_id) ,
96
- hir:: def:: Res :: Def ( hir:: def:: DefKind :: Const | hir:: def:: DefKind :: Static { .. } , _)
97
- ) ,
98
- _ => false ,
99
- }
100
- }
65
+ if name == sym:: expect
66
+ && let [ arg] = args
67
+ && let arg_root = get_arg_root ( cx, arg)
68
+ && contains_call ( cx, arg_root)
69
+ && !contains_return ( arg_root)
70
+ {
71
+ let receiver_type = cx. typeck_results ( ) . expr_ty_adjusted ( receiver) ;
72
+ let closure_args = if is_type_diagnostic_item ( cx, receiver_type, sym:: Option ) {
73
+ "||"
74
+ } else if is_type_diagnostic_item ( cx, receiver_type, sym:: Result ) {
75
+ "|_|"
76
+ } else {
77
+ return ;
78
+ } ;
101
79
102
- fn is_call ( node : & hir:: ExprKind < ' _ > ) -> bool {
103
- match node {
104
- hir:: ExprKind :: AddrOf ( hir:: BorrowKind :: Ref , _, expr) => {
105
- is_call ( & expr. kind )
106
- } ,
107
- hir:: ExprKind :: Call ( ..)
108
- | hir:: ExprKind :: MethodCall ( ..)
109
- // These variants are debatable or require further examination
110
- | hir:: ExprKind :: If ( ..)
111
- | hir:: ExprKind :: Match ( ..)
112
- | hir:: ExprKind :: Block { .. } => true ,
113
- _ => false ,
114
- }
115
- }
80
+ let span_replace_word = method_span. with_hi ( expr. span . hi ( ) ) ;
116
81
117
- if args. len ( ) != 1 || name != sym:: expect || !is_call ( & args[ 0 ] . kind ) {
118
- return ;
119
- }
82
+ let mut applicability = Applicability :: MachineApplicable ;
120
83
121
- let receiver_type = cx. typeck_results ( ) . expr_ty_adjusted ( receiver) ;
122
- let closure_args = if is_type_diagnostic_item ( cx, receiver_type, sym:: Option ) {
123
- "||"
124
- } else if is_type_diagnostic_item ( cx, receiver_type, sym:: Result ) {
125
- "|_|"
126
- } else {
127
- return ;
128
- } ;
129
-
130
- let arg_root = get_arg_root ( cx, & args[ 0 ] ) ;
131
-
132
- let span_replace_word = method_span. with_hi ( expr. span . hi ( ) ) ;
133
-
134
- let mut applicability = Applicability :: MachineApplicable ;
135
-
136
- // Special handling for `format!` as arg_root
137
- if let Some ( macro_call) = root_macro_call_first_node ( cx, arg_root) {
138
- if cx. tcx . is_diagnostic_item ( sym:: format_macro, macro_call. def_id )
139
- && let Some ( format_args) = format_args_storage. get ( cx, arg_root, macro_call. expn )
140
- {
141
- let span = format_args_inputs_span ( format_args) ;
142
- let sugg = snippet_with_applicability ( cx, span, ".." , & mut applicability) ;
143
- span_lint_and_sugg (
144
- cx,
145
- EXPECT_FUN_CALL ,
146
- span_replace_word,
147
- format ! ( "function call inside of `{name}`" ) ,
148
- "try" ,
149
- format ! ( "unwrap_or_else({closure_args} panic!({sugg}))" ) ,
150
- applicability,
151
- ) ;
84
+ // Special handling for `format!` as arg_root
85
+ if let Some ( macro_call) = root_macro_call_first_node ( cx, arg_root) {
86
+ if cx. tcx . is_diagnostic_item ( sym:: format_macro, macro_call. def_id )
87
+ && let Some ( format_args) = format_args_storage. get ( cx, arg_root, macro_call. expn )
88
+ {
89
+ let span = format_args_inputs_span ( format_args) ;
90
+ let sugg = snippet_with_applicability ( cx, span, ".." , & mut applicability) ;
91
+ span_lint_and_sugg (
92
+ cx,
93
+ EXPECT_FUN_CALL ,
94
+ span_replace_word,
95
+ format ! ( "function call inside of `{name}`" ) ,
96
+ "try" ,
97
+ format ! ( "unwrap_or_else({closure_args} panic!({sugg}))" ) ,
98
+ applicability,
99
+ ) ;
100
+ }
101
+ return ;
152
102
}
153
- return ;
154
- }
155
103
156
- let mut arg_root_snippet: Cow < ' _ , _ > = snippet_with_applicability ( cx, arg_root. span , ".." , & mut applicability) ;
157
- if requires_to_string ( cx, arg_root) {
158
- arg_root_snippet. to_mut ( ) . push_str ( ".to_string()" ) ;
159
- }
104
+ let arg_root_snippet: Cow < ' _ , _ > = snippet_with_applicability ( cx, arg_root. span , ".." , & mut applicability) ;
160
105
161
- span_lint_and_sugg (
162
- cx,
163
- EXPECT_FUN_CALL ,
164
- span_replace_word,
165
- format ! ( "function call inside of `{name}`" ) ,
166
- "try" ,
167
- format ! ( "unwrap_or_else({closure_args} {{ panic!(\" {{}}\" , {arg_root_snippet}) }})" ) ,
168
- applicability,
169
- ) ;
106
+ span_lint_and_sugg (
107
+ cx,
108
+ EXPECT_FUN_CALL ,
109
+ span_replace_word,
110
+ format ! ( "function call inside of `{name}`" ) ,
111
+ "try" ,
112
+ format ! ( "unwrap_or_else({closure_args} panic!(\" {{}}\" , {arg_root_snippet}))" ) ,
113
+ applicability,
114
+ ) ;
115
+ }
170
116
}
0 commit comments