11use clippy_utils:: diagnostics:: span_lint_and_sugg;
2+ use clippy_utils:: eager_or_lazy:: switch_to_lazy_eval;
23use clippy_utils:: macros:: { FormatArgsStorage , format_args_inputs_span, root_macro_call_first_node} ;
34use clippy_utils:: source:: snippet_with_applicability;
45use clippy_utils:: ty:: { is_type_diagnostic_item, is_type_lang_item} ;
6+ use clippy_utils:: { contains_return, peel_blocks} ;
57use rustc_errors:: Applicability ;
68use rustc_hir as hir;
79use rustc_lint:: LateContext ;
8- use rustc_middle:: ty;
910use rustc_span:: symbol:: sym;
1011use rustc_span:: { Span , Symbol } ;
1112use std:: borrow:: Cow ;
@@ -23,10 +24,10 @@ pub(super) fn check<'tcx>(
2324 receiver : & ' tcx hir:: Expr < ' tcx > ,
2425 args : & ' tcx [ hir:: Expr < ' tcx > ] ,
2526) {
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
2728 // `&str`
2829 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) ;
3031 loop {
3132 arg_root = match & arg_root. kind {
3233 hir:: ExprKind :: AddrOf ( hir:: BorrowKind :: Ref , _, expr) => expr,
@@ -47,124 +48,55 @@ pub(super) fn check<'tcx>(
4748 arg_root
4849 }
4950
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+ } ;
6565
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 ( ) ) ;
10167
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 ;
11669
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 ;
15288 }
153- return ;
154- }
15589
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) ;
16091
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+ }
170102}
0 commit comments