1 use clippy_utils
::diagnostics
::span_lint_and_sugg
;
2 use clippy_utils
::macros
::{root_macro_call_first_node, FormatArgsExpn}
;
3 use clippy_utils
::source
::snippet_with_applicability
;
4 use clippy_utils
::ty
::{is_type_diagnostic_item, is_type_lang_item}
;
5 use rustc_errors
::Applicability
;
7 use rustc_lint
::LateContext
;
9 use rustc_span
::source_map
::Span
;
10 use rustc_span
::symbol
::sym
;
13 use super::EXPECT_FUN_CALL
;
15 /// Checks for the `EXPECT_FUN_CALL` lint.
16 #[allow(clippy::too_many_lines)]
17 pub(super) fn check
<'tcx
>(
18 cx
: &LateContext
<'tcx
>,
22 receiver
: &'tcx hir
::Expr
<'tcx
>,
23 args
: &'tcx
[hir
::Expr
<'tcx
>],
25 // Strip `&`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or
27 fn get_arg_root
<'a
>(cx
: &LateContext
<'_
>, arg
: &'a hir
::Expr
<'a
>) -> &'a hir
::Expr
<'a
> {
28 let mut arg_root
= arg
;
30 arg_root
= match &arg_root
.kind
{
31 hir
::ExprKind
::AddrOf(hir
::BorrowKind
::Ref
, _
, expr
) => expr
,
32 hir
::ExprKind
::MethodCall(method_name
, receiver
, [], ..) => {
33 if (method_name
.ident
.name
== sym
::as_str
|| method_name
.ident
.name
== sym
::as_ref
) && {
34 let arg_type
= cx
.typeck_results().expr_ty(receiver
);
35 let base_type
= arg_type
.peel_refs();
36 *base_type
.kind() == ty
::Str
|| is_type_lang_item(cx
, base_type
, hir
::LangItem
::String
)
49 // Only `&'static str` or `String` can be used directly in the `panic!`. Other types should be
50 // converted to string.
51 fn requires_to_string(cx
: &LateContext
<'_
>, arg
: &hir
::Expr
<'_
>) -> bool
{
52 let arg_ty
= cx
.typeck_results().expr_ty(arg
);
53 if is_type_lang_item(cx
, arg_ty
, hir
::LangItem
::String
) {
56 if let ty
::Ref(_
, ty
, ..) = arg_ty
.kind() {
57 if *ty
.kind() == ty
::Str
&& can_be_static_str(cx
, arg
) {
64 // Check if an expression could have type `&'static str`, knowing that it
65 // has type `&str` for some lifetime.
66 fn can_be_static_str(cx
: &LateContext
<'_
>, arg
: &hir
::Expr
<'_
>) -> bool
{
68 hir
::ExprKind
::Lit(_
) => true,
69 hir
::ExprKind
::Call(fun
, _
) => {
70 if let hir
::ExprKind
::Path(ref p
) = fun
.kind
{
71 match cx
.qpath_res(p
, fun
.hir_id
) {
72 hir
::def
::Res
::Def(hir
::def
::DefKind
::Fn
| hir
::def
::DefKind
::AssocFn
, def_id
) => matches
!(
73 cx
.tcx
.fn_sig(def_id
).output().skip_binder().kind(),
74 ty
::Ref(re
, ..) if re
.is_static(),
82 hir
::ExprKind
::MethodCall(..) => {
84 .type_dependent_def_id(arg
.hir_id
)
85 .map_or(false, |method_id
| {
87 cx
.tcx
.fn_sig(method_id
).output().skip_binder().kind(),
88 ty
::Ref(re
, ..) if re
.is_static()
92 hir
::ExprKind
::Path(ref p
) => matches
!(
93 cx
.qpath_res(p
, arg
.hir_id
),
94 hir
::def
::Res
::Def(hir
::def
::DefKind
::Const
| hir
::def
::DefKind
::Static(_
), _
)
100 fn is_call(node
: &hir
::ExprKind
<'_
>) -> bool
{
102 hir
::ExprKind
::AddrOf(hir
::BorrowKind
::Ref
, _
, expr
) => {
105 hir
::ExprKind
::Call(..)
106 | hir
::ExprKind
::MethodCall(..)
107 // These variants are debatable or require further examination
108 | hir
::ExprKind
::If(..)
109 | hir
::ExprKind
::Match(..)
110 | hir
::ExprKind
::Block{ .. }
=> true,
115 if args
.len() != 1 || name
!= "expect" || !is_call(&args
[0].kind
) {
119 let receiver_type
= cx
.typeck_results().expr_ty_adjusted(receiver
);
120 let closure_args
= if is_type_diagnostic_item(cx
, receiver_type
, sym
::Option
) {
122 } else if is_type_diagnostic_item(cx
, receiver_type
, sym
::Result
) {
128 let arg_root
= get_arg_root(cx
, &args
[0]);
130 let span_replace_word
= method_span
.with_hi(expr
.span
.hi());
132 let mut applicability
= Applicability
::MachineApplicable
;
134 //Special handling for `format!` as arg_root
135 if let Some(macro_call
) = root_macro_call_first_node(cx
, arg_root
) {
136 if !cx
.tcx
.is_diagnostic_item(sym
::format_macro
, macro_call
.def_id
) {
139 let Some(format_args
) = FormatArgsExpn
::find_nested(cx
, arg_root
, macro_call
.expn
) else { return }
;
140 let span
= format_args
.inputs_span();
141 let sugg
= snippet_with_applicability(cx
, span
, "..", &mut applicability
);
146 &format
!("use of `{name}` followed by a function call"),
148 format
!("unwrap_or_else({closure_args} panic!({sugg}))"),
154 let mut arg_root_snippet
: Cow
<'_
, _
> = snippet_with_applicability(cx
, arg_root
.span
, "..", &mut applicability
);
155 if requires_to_string(cx
, arg_root
) {
156 arg_root_snippet
.to_mut().push_str(".to_string()");
163 &format
!("use of `{name}` followed by a function call"),
165 format
!("unwrap_or_else({closure_args} {{ panic!(\"{{}}\", {arg_root_snippet}) }})"),