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, 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) = cx.tcx.get_diagnostic_item(sym::Default);
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);