]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/methods/unwrap_or_else_default.rs
Rollup merge of #105663 - andrewpollack:patch-1, r=tmandry
[rust.git] / src / tools / clippy / clippy_lints / src / methods / unwrap_or_else_default.rs
1 //! Lint for `some_result_or_option.unwrap_or_else(Default::default)`
2
3 use super::UNWRAP_OR_ELSE_DEFAULT;
4 use clippy_utils::{
5     diagnostics::span_lint_and_sugg, is_default_equivalent_call, source::snippet_with_applicability,
6     ty::is_type_diagnostic_item,
7 };
8 use rustc_ast::ast::LitKind;
9 use rustc_errors::Applicability;
10 use rustc_hir as hir;
11 use rustc_lint::LateContext;
12 use rustc_span::{sym, symbol};
13
14 pub(super) fn check<'tcx>(
15     cx: &LateContext<'tcx>,
16     expr: &'tcx hir::Expr<'_>,
17     recv: &'tcx hir::Expr<'_>,
18     u_arg: &'tcx hir::Expr<'_>,
19 ) {
20     // something.unwrap_or_else(Default::default)
21     // ^^^^^^^^^- recv          ^^^^^^^^^^^^^^^^- u_arg
22     // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- expr
23     let recv_ty = cx.typeck_results().expr_ty(recv);
24     let is_option = is_type_diagnostic_item(cx, recv_ty, sym::Option);
25     let is_result = is_type_diagnostic_item(cx, recv_ty, sym::Result);
26
27     if_chain! {
28         if is_option || is_result;
29         if closure_body_returns_empty_to_string(cx, u_arg) || is_default_equivalent_call(cx, u_arg);
30         then {
31             let mut applicability = Applicability::MachineApplicable;
32
33             span_lint_and_sugg(
34                 cx,
35                 UNWRAP_OR_ELSE_DEFAULT,
36                 expr.span,
37                 "use of `.unwrap_or_else(..)` to construct default value",
38                 "try",
39                 format!(
40                     "{}.unwrap_or_default()",
41                     snippet_with_applicability(cx, recv.span, "..", &mut applicability)
42                 ),
43                 applicability,
44             );
45         }
46     }
47 }
48
49 fn closure_body_returns_empty_to_string(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool {
50     if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = e.kind {
51         let body = cx.tcx.hir().body(body);
52
53         if body.params.is_empty()
54             && let hir::Expr{ kind, .. } = &body.value
55             && let hir::ExprKind::MethodCall(hir::PathSegment {ident, ..}, self_arg, _, _) = kind
56             && ident == &symbol::Ident::from_str("to_string")
57             && let hir::Expr{ kind, .. } = self_arg
58             && let hir::ExprKind::Lit(lit) = kind
59             && let LitKind::Str(symbol::kw::Empty, _) = lit.node
60         {
61             return true;
62         }
63     }
64
65     false
66 }