1
1
use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
+ use clippy_utils:: eager_or_lazy:: switch_to_lazy_eval;
2
3
use clippy_utils:: macros:: { FormatArgsStorage , format_args_inputs_span, root_macro_call_first_node} ;
3
4
use clippy_utils:: source:: snippet_with_applicability;
4
5
use clippy_utils:: ty:: { is_type_diagnostic_item, is_type_lang_item} ;
6
+ use clippy_utils:: { contains_return, 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 ;
@@ -23,10 +24,10 @@ pub(super) fn check<'tcx>(
23
24
receiver : & ' tcx hir:: Expr < ' tcx > ,
24
25
args : & ' tcx [ hir:: Expr < ' tcx > ] ,
25
26
) {
26
- // Strip `&`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or
27
+ // Strip `{}`, ` &`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or
27
28
// `&str`
28
29
fn get_arg_root < ' a > ( cx : & LateContext < ' _ > , arg : & ' a hir:: Expr < ' a > ) -> & ' a hir:: Expr < ' a > {
29
- let mut arg_root = arg;
30
+ let mut arg_root = peel_blocks ( arg) ;
30
31
loop {
31
32
arg_root = match & arg_root. kind {
32
33
hir:: ExprKind :: AddrOf ( hir:: BorrowKind :: Ref , _, expr) => expr,
@@ -47,124 +48,55 @@ pub(super) fn check<'tcx>(
47
48
arg_root
48
49
}
49
50
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
64
- }
51
+ if name == sym:: expect
52
+ && let [ arg] = args
53
+ && let arg_root = get_arg_root ( cx, arg)
54
+ && switch_to_lazy_eval ( cx, arg_root)
55
+ && !contains_return ( arg_root)
56
+ {
57
+ let receiver_type = cx. typeck_results ( ) . expr_ty_adjusted ( receiver) ;
58
+ let closure_args = if is_type_diagnostic_item ( cx, receiver_type, sym:: Option ) {
59
+ "||"
60
+ } else if is_type_diagnostic_item ( cx, receiver_type, sym:: Result ) {
61
+ "|_|"
62
+ } else {
63
+ return ;
64
+ } ;
65
65
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
- }
66
+ let span_replace_word = method_span. with_hi ( expr. span . hi ( ) ) ;
101
67
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
- }
68
+ let mut applicability = Applicability :: MachineApplicable ;
116
69
117
- if args. len ( ) != 1 || name != sym:: expect || !is_call ( & args[ 0 ] . kind ) {
118
- return ;
119
- }
120
-
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
- ) ;
70
+ // Special handling for `format!` as arg_root
71
+ if let Some ( macro_call) = root_macro_call_first_node ( cx, arg_root) {
72
+ if cx. tcx . is_diagnostic_item ( sym:: format_macro, macro_call. def_id )
73
+ && let Some ( format_args) = format_args_storage. get ( cx, arg_root, macro_call. expn )
74
+ {
75
+ let span = format_args_inputs_span ( format_args) ;
76
+ let sugg = snippet_with_applicability ( cx, span, ".." , & mut applicability) ;
77
+ span_lint_and_sugg (
78
+ cx,
79
+ EXPECT_FUN_CALL ,
80
+ span_replace_word,
81
+ format ! ( "function call inside of `{name}`" ) ,
82
+ "try" ,
83
+ format ! ( "unwrap_or_else({closure_args} panic!({sugg}))" ) ,
84
+ applicability,
85
+ ) ;
86
+ }
87
+ return ;
152
88
}
153
- return ;
154
- }
155
89
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
- }
90
+ let arg_root_snippet: Cow < ' _ , _ > = snippet_with_applicability ( cx, arg_root. span , ".." , & mut applicability) ;
160
91
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
- ) ;
92
+ span_lint_and_sugg (
93
+ cx,
94
+ EXPECT_FUN_CALL ,
95
+ span_replace_word,
96
+ format ! ( "function call inside of `{name}`" ) ,
97
+ "try" ,
98
+ format ! ( "unwrap_or_else({closure_args} panic!(\" {{}}\" , {arg_root_snippet}))" ) ,
99
+ applicability,
100
+ ) ;
101
+ }
170
102
}
0 commit comments