|  | 
| 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::{BinOpKind, Expr, ExprKind}; | 
| 6 | 8 | use rustc_lint::LateContext; | 
| 7 |  | -use rustc_span::{Span, sym}; | 
|  | 9 | +use rustc_middle::ty::{self, GenericArg}; | 
|  | 10 | +use rustc_span::{BytePos, Span, Symbol, sym}; | 
| 8 | 11 | 
 | 
| 9 | 12 | use super::MANUAL_IS_VARIANT_AND; | 
| 10 | 13 | 
 | 
| 11 | 14 | pub(super) fn check<'tcx>( | 
| 12 | 15 |     cx: &LateContext<'_>, | 
| 13 |  | -    expr: &'tcx rustc_hir::Expr<'_>, | 
| 14 |  | -    map_recv: &'tcx rustc_hir::Expr<'_>, | 
| 15 |  | -    map_arg: &'tcx rustc_hir::Expr<'_>, | 
|  | 16 | +    expr: &'tcx Expr<'_>, | 
|  | 17 | +    map_recv: &'tcx Expr<'_>, | 
|  | 18 | +    map_arg: &'tcx Expr<'_>, | 
| 16 | 19 |     map_span: Span, | 
| 17 | 20 |     msrv: Msrv, | 
| 18 | 21 | ) { | 
| @@ -57,3 +60,111 @@ pub(super) fn check<'tcx>( | 
| 57 | 60 |         Applicability::MachineApplicable, | 
| 58 | 61 |     ); | 
| 59 | 62 | } | 
|  | 63 | + | 
|  | 64 | +fn is_option_result_bool<F: Fn(&[GenericArg<'_>]) -> bool>( | 
|  | 65 | +    cx: &LateContext<'_>, | 
|  | 66 | +    expr: &Expr<'_>, | 
|  | 67 | +    symbol: Symbol, | 
|  | 68 | +    callback: F, | 
|  | 69 | +) -> bool { | 
|  | 70 | +    if let ExprKind::Call(..) = expr.kind | 
|  | 71 | +        && let ty = cx.typeck_results().expr_ty(expr) | 
|  | 72 | +        && let ty::Adt(adt, generics) = ty.kind() | 
|  | 73 | +        && cx.tcx.is_diagnostic_item(symbol, adt.did()) | 
|  | 74 | +    { | 
|  | 75 | +        callback(generics.as_slice()) | 
|  | 76 | +    } else { | 
|  | 77 | +        false | 
|  | 78 | +    } | 
|  | 79 | +} | 
|  | 80 | + | 
|  | 81 | +fn is_option_bool(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { | 
|  | 82 | +    is_option_result_bool(cx, expr, sym::Option, |generics| { | 
|  | 83 | +        if let [generic] = generics | 
|  | 84 | +            && let Some(generic) = generic.as_type() | 
|  | 85 | +        { | 
|  | 86 | +            generic.is_bool() | 
|  | 87 | +        } else { | 
|  | 88 | +            false | 
|  | 89 | +        } | 
|  | 90 | +    }) | 
|  | 91 | +} | 
|  | 92 | + | 
|  | 93 | +fn is_option_result_map(cx: &LateContext<'_>, expr: &Expr<'_>, symbol: Symbol) -> Option<Span> { | 
|  | 94 | +    if let ExprKind::MethodCall(_, recv, _, span) = expr.kind | 
|  | 95 | +        && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), symbol) | 
|  | 96 | +    { | 
|  | 97 | +        Some(span) | 
|  | 98 | +    } else { | 
|  | 99 | +        None | 
|  | 100 | +    } | 
|  | 101 | +} | 
|  | 102 | + | 
|  | 103 | +fn is_result_bool(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { | 
|  | 104 | +    is_option_result_bool(cx, expr, sym::Result, |generics| { | 
|  | 105 | +        if let [generic, _] = generics | 
|  | 106 | +            && let Some(generic) = generic.as_type() | 
|  | 107 | +        { | 
|  | 108 | +            generic.is_bool() | 
|  | 109 | +        } else { | 
|  | 110 | +            false | 
|  | 111 | +        } | 
|  | 112 | +    }) | 
|  | 113 | +} | 
|  | 114 | + | 
|  | 115 | +fn emit_lint(cx: &LateContext<'_>, op: BinOpKind, parent: &Expr<'_>, method_span: Span, is_option: bool) { | 
|  | 116 | +    if let Some(before_map_snippet) = snippet_opt(cx, parent.span.with_hi(method_span.lo())) | 
|  | 117 | +        && let Some(after_map_snippet) = snippet_opt(cx, method_span.with_lo(method_span.lo() + BytePos(3))) | 
|  | 118 | +    { | 
|  | 119 | +        span_lint_and_sugg( | 
|  | 120 | +            cx, | 
|  | 121 | +            MANUAL_IS_VARIANT_AND, | 
|  | 122 | +            parent.span, | 
|  | 123 | +            format!( | 
|  | 124 | +                "called `.map() {}= {}()`", | 
|  | 125 | +                if op == BinOpKind::Eq { '=' } else { '!' }, | 
|  | 126 | +                if is_option { "Some" } else { "Ok" }, | 
|  | 127 | +            ), | 
|  | 128 | +            "use", | 
|  | 129 | +            if is_option && op == BinOpKind::Ne { | 
|  | 130 | +                format!("{before_map_snippet}is_none_or{after_map_snippet}",) | 
|  | 131 | +            } else { | 
|  | 132 | +                format!( | 
|  | 133 | +                    "{}{before_map_snippet}{}{after_map_snippet}", | 
|  | 134 | +                    if op == BinOpKind::Eq { "" } else { "!" }, | 
|  | 135 | +                    if is_option { "is_some_and" } else { "is_ok_and" }, | 
|  | 136 | +                ) | 
|  | 137 | +            }, | 
|  | 138 | +            Applicability::MachineApplicable, | 
|  | 139 | +        ); | 
|  | 140 | +    } | 
|  | 141 | +} | 
|  | 142 | + | 
|  | 143 | +fn check_left_and_right(cx: &LateContext<'_>, op: BinOpKind, parent: &Expr<'_>, left: &Expr<'_>, right: &Expr<'_>) { | 
|  | 144 | +    if is_option_bool(cx, right) { | 
|  | 145 | +        if let Some(sp) = is_option_result_map(cx, left, sym::Option) { | 
|  | 146 | +            emit_lint(cx, op, parent, sp, true); | 
|  | 147 | +        } | 
|  | 148 | +    } else if is_option_bool(cx, left) | 
|  | 149 | +        && let Some(sp) = is_option_result_map(cx, right, sym::Option) | 
|  | 150 | +    { | 
|  | 151 | +        emit_lint(cx, op, parent, sp, true); | 
|  | 152 | +    } else if is_result_bool(cx, left) { | 
|  | 153 | +        if let Some(sp) = is_option_result_map(cx, right, sym::Result) { | 
|  | 154 | +            emit_lint(cx, op, parent, sp, false); | 
|  | 155 | +        } | 
|  | 156 | +    } else if is_result_bool(cx, right) | 
|  | 157 | +        && let Some(sp) = is_option_result_map(cx, left, sym::Result) | 
|  | 158 | +    { | 
|  | 159 | +        emit_lint(cx, op, parent, sp, false); | 
|  | 160 | +    } | 
|  | 161 | +} | 
|  | 162 | + | 
|  | 163 | +pub(super) fn check_map(cx: &LateContext<'_>, expr: &Expr<'_>) { | 
|  | 164 | +    if let Some(parent_expr) = get_parent_expr(cx, expr) | 
|  | 165 | +        && let ExprKind::Binary(op, left, right) = parent_expr.kind | 
|  | 166 | +        && matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) | 
|  | 167 | +    { | 
|  | 168 | +        check_left_and_right(cx, op.node, parent_expr, left, right); | 
|  | 169 | +    } | 
|  | 170 | +} | 
0 commit comments