1 use super::FLOAT_ARITHMETIC; 2 use clippy_utils::consts::constant_simple; 3 use clippy_utils::diagnostics::span_lint; 4 use rustc_hir as hir; 5 use rustc_lint::LateContext; 6 use rustc_span::source_map::Span; 7 8 #[derive(Default)] 9 pub struct Context { 10 expr_id: Option<hir::HirId>, 11 /// This field is used to check whether expressions are constants, such as in enum discriminants 12 /// and consts 13 const_span: Option<Span>, 14 } 15 impl Context { skip_expr(&mut self, e: &hir::Expr<'_>) -> bool16 fn skip_expr(&mut self, e: &hir::Expr<'_>) -> bool { 17 self.expr_id.is_some() || self.const_span.map_or(false, |span| span.contains(e.span)) 18 } 19 check_binary<'tcx>( &mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, op: hir::BinOpKind, l: &'tcx hir::Expr<'_>, r: &'tcx hir::Expr<'_>, )20 pub fn check_binary<'tcx>( 21 &mut self, 22 cx: &LateContext<'tcx>, 23 expr: &'tcx hir::Expr<'_>, 24 op: hir::BinOpKind, 25 l: &'tcx hir::Expr<'_>, 26 r: &'tcx hir::Expr<'_>, 27 ) { 28 if self.skip_expr(expr) { 29 return; 30 } 31 match op { 32 hir::BinOpKind::And 33 | hir::BinOpKind::Or 34 | hir::BinOpKind::BitAnd 35 | hir::BinOpKind::BitOr 36 | hir::BinOpKind::BitXor 37 | hir::BinOpKind::Eq 38 | hir::BinOpKind::Lt 39 | hir::BinOpKind::Le 40 | hir::BinOpKind::Ne 41 | hir::BinOpKind::Ge 42 | hir::BinOpKind::Gt => return, 43 _ => (), 44 } 45 46 let (_, r_ty) = (cx.typeck_results().expr_ty(l), cx.typeck_results().expr_ty(r)); 47 if r_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() { 48 span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); 49 self.expr_id = Some(expr.hir_id); 50 } 51 } 52 check_negate<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>)53 pub fn check_negate<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { 54 if self.skip_expr(expr) { 55 return; 56 } 57 let ty = cx.typeck_results().expr_ty(arg); 58 if constant_simple(cx, cx.typeck_results(), expr).is_none() && ty.is_floating_point() { 59 span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); 60 self.expr_id = Some(expr.hir_id); 61 } 62 } 63 expr_post(&mut self, id: hir::HirId)64 pub fn expr_post(&mut self, id: hir::HirId) { 65 if Some(id) == self.expr_id { 66 self.expr_id = None; 67 } 68 } 69 enter_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>)70 pub fn enter_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) { 71 let body_owner = cx.tcx.hir().body_owner(body.id()); 72 let body_owner_def_id = cx.tcx.hir().body_owner_def_id(body.id()); 73 74 match cx.tcx.hir().body_owner_kind(body_owner_def_id) { 75 hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const => { 76 let body_span = cx.tcx.hir().span_with_body(body_owner); 77 78 if let Some(span) = self.const_span { 79 if span.contains(body_span) { 80 return; 81 } 82 } 83 self.const_span = Some(body_span); 84 }, 85 hir::BodyOwnerKind::Fn | hir::BodyOwnerKind::Closure => (), 86 } 87 } 88 body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>)89 pub fn body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) { 90 let body_owner = cx.tcx.hir().body_owner(body.id()); 91 let body_span = cx.tcx.hir().span_with_body(body_owner); 92 93 if let Some(span) = self.const_span { 94 if span.contains(body_span) { 95 return; 96 } 97 } 98 self.const_span = None; 99 } 100 } 101