| 
1 | 1 | use clippy_utils::diagnostics::span_lint_and_sugg;  | 
 | 2 | +use clippy_utils::get_parent_expr;  | 
2 | 3 | use clippy_utils::msrvs::{self, Msrv};  | 
3 |  | -use clippy_utils::source::snippet;  | 
 | 4 | +use clippy_utils::source::{snippet, snippet_opt};  | 
4 | 5 | use clippy_utils::ty::is_type_diagnostic_item;  | 
5 | 6 | use rustc_errors::Applicability;  | 
 | 7 | +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};  | 
 | 8 | +use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};  | 
6 | 9 | use rustc_lint::LateContext;  | 
7 |  | -use rustc_span::{Span, sym};  | 
 | 10 | +use rustc_middle::ty;  | 
 | 11 | +use rustc_span::{BytePos, Span, sym};  | 
8 | 12 | 
 
  | 
9 | 13 | use super::MANUAL_IS_VARIANT_AND;  | 
10 | 14 | 
 
  | 
11 |  | -pub(super) fn check<'tcx>(  | 
 | 15 | +pub(super) fn check(  | 
12 | 16 |     cx: &LateContext<'_>,  | 
13 |  | -    expr: &'tcx rustc_hir::Expr<'_>,  | 
14 |  | -    map_recv: &'tcx rustc_hir::Expr<'_>,  | 
15 |  | -    map_arg: &'tcx rustc_hir::Expr<'_>,  | 
 | 17 | +    expr: &Expr<'_>,  | 
 | 18 | +    map_recv: &Expr<'_>,  | 
 | 19 | +    map_arg: &Expr<'_>,  | 
16 | 20 |     map_span: Span,  | 
17 | 21 |     msrv: Msrv,  | 
18 | 22 | ) {  | 
@@ -57,3 +61,57 @@ pub(super) fn check<'tcx>(  | 
57 | 61 |         Applicability::MachineApplicable,  | 
58 | 62 |     );  | 
59 | 63 | }  | 
 | 64 | + | 
 | 65 | +fn emit_lint(cx: &LateContext<'_>, op: BinOpKind, parent: &Expr<'_>, method_span: Span, is_option: bool) {  | 
 | 66 | +    if let Some(before_map_snippet) = snippet_opt(cx, parent.span.with_hi(method_span.lo()))  | 
 | 67 | +        && let Some(after_map_snippet) = snippet_opt(cx, method_span.with_lo(method_span.lo() + BytePos(3)))  | 
 | 68 | +    {  | 
 | 69 | +        span_lint_and_sugg(  | 
 | 70 | +            cx,  | 
 | 71 | +            MANUAL_IS_VARIANT_AND,  | 
 | 72 | +            parent.span,  | 
 | 73 | +            format!(  | 
 | 74 | +                "called `.map() {}= {}()`",  | 
 | 75 | +                if op == BinOpKind::Eq { '=' } else { '!' },  | 
 | 76 | +                if is_option { "Some" } else { "Ok" },  | 
 | 77 | +            ),  | 
 | 78 | +            "use",  | 
 | 79 | +            if is_option && op == BinOpKind::Ne {  | 
 | 80 | +                format!("{before_map_snippet}is_none_or{after_map_snippet}",)  | 
 | 81 | +            } else {  | 
 | 82 | +                format!(  | 
 | 83 | +                    "{}{before_map_snippet}{}{after_map_snippet}",  | 
 | 84 | +                    if op == BinOpKind::Eq { "" } else { "!" },  | 
 | 85 | +                    if is_option { "is_some_and" } else { "is_ok_and" },  | 
 | 86 | +                )  | 
 | 87 | +            },  | 
 | 88 | +            Applicability::MachineApplicable,  | 
 | 89 | +        );  | 
 | 90 | +    }  | 
 | 91 | +}  | 
 | 92 | + | 
 | 93 | +pub(super) fn check_map(cx: &LateContext<'_>, expr: &Expr<'_>) {  | 
 | 94 | +    if let Some(parent_expr) = get_parent_expr(cx, expr)  | 
 | 95 | +        && let ExprKind::Binary(op, left, right) = parent_expr.kind  | 
 | 96 | +        && matches!(op.node, BinOpKind::Eq | BinOpKind::Ne)  | 
 | 97 | +        && op.span.eq_ctxt(expr.span)  | 
 | 98 | +    {  | 
 | 99 | +        // Check `left` and `right` expression in any order, and for `Option` and `Result`  | 
 | 100 | +        for (expr1, expr2) in [(left, right), (right, left)] {  | 
 | 101 | +            for item in [sym::Option, sym::Result] {  | 
 | 102 | +                if let ExprKind::Call(call, ..) = expr1.kind  | 
 | 103 | +                    && let ExprKind::Path(QPath::Resolved(_, path)) = call.kind  | 
 | 104 | +                    && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), _) = path.res  | 
 | 105 | +                    && let ty = cx.typeck_results().expr_ty(expr1)  | 
 | 106 | +                    && let ty::Adt(adt, args) = ty.kind()  | 
 | 107 | +                    && cx.tcx.is_diagnostic_item(item, adt.did())  | 
 | 108 | +                    && args.type_at(0).is_bool()  | 
 | 109 | +                    && let ExprKind::MethodCall(_, recv, _, span) = expr2.kind  | 
 | 110 | +                    && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), item)  | 
 | 111 | +                {  | 
 | 112 | +                    return emit_lint(cx, op.node, parent_expr, span, item == sym::Option);  | 
 | 113 | +                }  | 
 | 114 | +            }  | 
 | 115 | +        }  | 
 | 116 | +    }  | 
 | 117 | +}  | 
0 commit comments