@@ -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