use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg; use rustc_ast::util::parser::AssocOp; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::source_map::Spanned; use rustc_span::sym; use super::FLOAT_EQUALITY_WITHOUT_ABS; pub(crate) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: BinOpKind, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>, ) { let (lhs, rhs) = match op { BinOpKind::Lt => (lhs, rhs), BinOpKind::Gt => (rhs, lhs), _ => return, }; if let ExprKind::Binary( // left hand side is a subtraction Spanned { node: BinOpKind::Sub, .. }, val_l, val_r, ) = lhs.kind // right hand side matches either f32::EPSILON or f64::EPSILON && let ExprKind::Path(ref epsilon_path) = rhs.kind && let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id) && ([sym::f32_epsilon, sym::f64_epsilon].into_iter().any(|sym| cx.tcx.is_diagnostic_item(sym, def_id))) // values of the subtractions on the left hand side are of the type float && let t_val_l = cx.typeck_results().expr_ty(val_l) && let t_val_r = cx.typeck_results().expr_ty(val_r) && let ty::Float(_) = t_val_l.kind() && let ty::Float(_) = t_val_r.kind() { let sug_l = sugg::Sugg::hir(cx, val_l, ".."); let sug_r = sugg::Sugg::hir(cx, val_r, ".."); // format the suggestion let suggestion = format!( "{}.abs()", sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par() ); // spans the lint span_lint_and_then( cx, FLOAT_EQUALITY_WITHOUT_ABS, expr.span, "float equality check without `.abs()`", |diag| { diag.span_suggestion(lhs.span, "add `.abs()`", suggestion, Applicability::MaybeIncorrect); }, ); } }