1 use clippy_utils
::diagnostics
::span_lint_and_sugg
;
2 use clippy_utils
::eager_or_lazy
::is_lazyness_candidate
;
3 use clippy_utils
::source
::{snippet, snippet_with_applicability, snippet_with_macro_callsite}
;
4 use clippy_utils
::ty
::{implements_trait, is_type_diagnostic_item, match_type}
;
5 use clippy_utils
::{contains_return, get_trait_def_id, last_path_segment, paths}
;
6 use if_chain
::if_chain
;
7 use rustc_errors
::Applicability
;
9 use rustc_hir
::{BlockCheckMode, UnsafeSource}
;
10 use rustc_lint
::LateContext
;
12 use rustc_span
::source_map
::Span
;
13 use rustc_span
::symbol
::{kw, sym}
;
16 use super::OR_FUN_CALL
;
18 /// Checks for the `OR_FUN_CALL` lint.
19 #[allow(clippy::too_many_lines)]
20 pub(super) fn check
<'tcx
>(
21 cx
: &LateContext
<'tcx
>,
25 args
: &'tcx
[hir
::Expr
<'_
>],
27 /// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`.
28 fn check_unwrap_or_default(
32 self_expr
: &hir
::Expr
<'_
>,
39 if name
== "unwrap_or";
40 if let hir
::ExprKind
::Path(ref qpath
) = fun
.kind
;
41 let path
= last_path_segment(qpath
).ident
.name
;
42 if matches
!(path
, kw
::Default
| sym
::new
);
43 let arg_ty
= cx
.typeck_results().expr_ty(arg
);
44 if let Some(default_trait_id
) = get_trait_def_id(cx
, &paths
::DEFAULT_TRAIT
);
45 if implements_trait(cx
, arg_ty
, default_trait_id
, &[]);
48 let mut applicability
= Applicability
::MachineApplicable
;
53 &format
!("use of `{}` followed by a call to `{}`", name
, path
),
56 "{}.unwrap_or_default()",
57 snippet_with_applicability(cx
, self_expr
.span
, "..", &mut applicability
)
69 /// Checks for `*or(foo())`.
70 #[allow(clippy::too_many_arguments)]
71 fn check_general_case
<'tcx
>(
72 cx
: &LateContext
<'tcx
>,
75 self_expr
: &hir
::Expr
<'_
>,
76 arg
: &'tcx hir
::Expr
<'_
>,
78 // None if lambda is required
79 fun_span
: Option
<Span
>,
81 // (path, fn_has_argument, methods, suffix)
82 static KNOW_TYPES
: [(&[&str], bool
, &[&str], &str); 4] = [
83 (&paths
::BTREEMAP_ENTRY
, false, &["or_insert"], "with"),
84 (&paths
::HASHMAP_ENTRY
, false, &["or_insert"], "with"),
85 (&paths
::OPTION
, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),
86 (&paths
::RESULT
, true, &["or", "unwrap_or"], "else"),
89 if let hir
::ExprKind
::MethodCall(path
, _
, args
, _
) = &arg
.kind
{
90 if path
.ident
.name
== sym
::len
{
91 let ty
= cx
.typeck_results().expr_ty(&args
[0]).peel_refs();
94 ty
::Slice(_
) | ty
::Array(_
, _
) | ty
::Str
=> return,
98 if is_type_diagnostic_item(cx
, ty
, sym
::vec_type
) {
105 if KNOW_TYPES
.iter().any(|k
| k
.2.contains(&name
));
107 if is_lazyness_candidate(cx
, arg
);
108 if !contains_return(arg
);
110 let self_ty
= cx
.typeck_results().expr_ty(self_expr
);
112 if let Some(&(_
, fn_has_arguments
, poss
, suffix
)) =
113 KNOW_TYPES
.iter().find(|&&i
| match_type(cx
, self_ty
, i
.0));
115 if poss
.contains(&name
);
118 let macro_expanded_snipped
;
119 let sugg
: Cow
<'_
, str> = {
120 let (snippet_span
, use_lambda
) = match (fn_has_arguments
, fun_span
) {
121 (false, Some(fun_span
)) => (fun_span
, false),
122 _
=> (arg
.span
, true),
125 let not_macro_argument_snippet
= snippet_with_macro_callsite(cx
, snippet_span
, "..");
126 if not_macro_argument_snippet
== "vec![]" {
127 macro_expanded_snipped
= snippet(cx
, snippet_span
, "..");
128 match macro_expanded_snipped
.strip_prefix("$crate::vec::") {
129 Some(stripped
) => Cow
::from(stripped
),
130 None
=> macro_expanded_snipped
134 not_macro_argument_snippet
139 let l_arg
= if fn_has_arguments { "_" }
else { "" }
;
140 format
!("|{}| {}", l_arg
, snippet
).into()
145 let span_replace_word
= method_span
.with_hi(span
.hi());
150 &format
!("use of `{}` followed by a function call", name
),
152 format
!("{}_{}({})", name
, suffix
, sugg
),
153 Applicability
::HasPlaceholders
,
161 hir
::ExprKind
::Call(fun
, or_args
) => {
162 let or_has_args
= !or_args
.is_empty();
163 if !check_unwrap_or_default(cx
, name
, fun
, &args
[0], &args
[1], or_has_args
, expr
.span
) {
164 let fun_span
= if or_has_args { None }
else { Some(fun.span) }
;
165 check_general_case(cx
, name
, method_span
, &args
[0], &args
[1], expr
.span
, fun_span
);
168 hir
::ExprKind
::Index(..) | hir
::ExprKind
::MethodCall(..) => {
169 check_general_case(cx
, name
, method_span
, &args
[0], &args
[1], expr
.span
, None
);
171 hir
::ExprKind
::Block(block
, _
) => {
172 if let BlockCheckMode
::UnsafeBlock(UnsafeSource
::UserProvided
) = block
.rules
{
173 if let Some(block_expr
) = block
.expr
{
174 if let hir
::ExprKind
::MethodCall(..) = block_expr
.kind
{
175 check_general_case(cx
, name
, method_span
, &args
[0], &args
[1], expr
.span
, None
);