diff --git a/CHANGELOG.md b/CHANGELOG.md index 37cdcc571..6c51e5595 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -107,6 +107,7 @@ All notable changes to this project will be documented in this file. [`explicit_iter_loop`]: https://github.com/Manishearth/rust-clippy/wiki#explicit_iter_loop [`extend_from_slice`]: https://github.com/Manishearth/rust-clippy/wiki#extend_from_slice [`filter_next`]: https://github.com/Manishearth/rust-clippy/wiki#filter_next +[`float_arithmetic`]: https://github.com/Manishearth/rust-clippy/wiki#float_arithmetic [`float_cmp`]: https://github.com/Manishearth/rust-clippy/wiki#float_cmp [`for_kv_map`]: https://github.com/Manishearth/rust-clippy/wiki#for_kv_map [`for_loop_over_option`]: https://github.com/Manishearth/rust-clippy/wiki#for_loop_over_option @@ -118,6 +119,7 @@ All notable changes to this project will be documented in this file. [`indexing_slicing`]: https://github.com/Manishearth/rust-clippy/wiki#indexing_slicing [`ineffective_bit_mask`]: https://github.com/Manishearth/rust-clippy/wiki#ineffective_bit_mask [`inline_always`]: https://github.com/Manishearth/rust-clippy/wiki#inline_always +[`integer_arithmetic`]: https://github.com/Manishearth/rust-clippy/wiki#integer_arithmetic [`invalid_regex`]: https://github.com/Manishearth/rust-clippy/wiki#invalid_regex [`invalid_upcast_comparisons`]: https://github.com/Manishearth/rust-clippy/wiki#invalid_upcast_comparisons [`items_after_statements`]: https://github.com/Manishearth/rust-clippy/wiki#items_after_statements diff --git a/README.md b/README.md index 59724457a..3b0ca3f4a 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Table of contents: * [License](#license) ##Lints -There are 144 lints included in this crate: +There are 146 lints included in this crate: name | default | meaning ---------------------------------------------------------------------------------------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ @@ -56,6 +56,7 @@ name [explicit_iter_loop](https://github.com/Manishearth/rust-clippy/wiki#explicit_iter_loop) | warn | for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do [extend_from_slice](https://github.com/Manishearth/rust-clippy/wiki#extend_from_slice) | warn | `.extend_from_slice(_)` is a faster way to extend a Vec by a slice [filter_next](https://github.com/Manishearth/rust-clippy/wiki#filter_next) | warn | using `filter(p).next()`, which is more succinctly expressed as `.find(p)` +[float_arithmetic](https://github.com/Manishearth/rust-clippy/wiki#float_arithmetic) | allow | Any floating-point arithmetic statement [float_cmp](https://github.com/Manishearth/rust-clippy/wiki#float_cmp) | warn | using `==` or `!=` on float values (as floating-point operations usually involve rounding errors, it is always better to check for approximate equality within small bounds) [for_kv_map](https://github.com/Manishearth/rust-clippy/wiki#for_kv_map) | warn | looping on a map using `iter` when `keys` or `values` would do [for_loop_over_option](https://github.com/Manishearth/rust-clippy/wiki#for_loop_over_option) | warn | for-looping over an `Option`, which is more clearly expressed as an `if let` @@ -67,6 +68,7 @@ name [indexing_slicing](https://github.com/Manishearth/rust-clippy/wiki#indexing_slicing) | allow | indexing/slicing usage [ineffective_bit_mask](https://github.com/Manishearth/rust-clippy/wiki#ineffective_bit_mask) | warn | expressions where a bit mask will be rendered useless by a comparison, e.g. `(x | 1) > 2` [inline_always](https://github.com/Manishearth/rust-clippy/wiki#inline_always) | warn | `#[inline(always)]` is a bad idea in most cases +[integer_arithmetic](https://github.com/Manishearth/rust-clippy/wiki#integer_arithmetic) | allow | Any integer arithmetic statement [invalid_regex](https://github.com/Manishearth/rust-clippy/wiki#invalid_regex) | deny | finds invalid regular expressions in `Regex::new(_)` invocations [invalid_upcast_comparisons](https://github.com/Manishearth/rust-clippy/wiki#invalid_upcast_comparisons) | warn | a comparison involving an upcast which is always true or false [items_after_statements](https://github.com/Manishearth/rust-clippy/wiki#items_after_statements) | allow | finds blocks where an item comes after a statement diff --git a/src/arithmetic.rs b/src/arithmetic.rs new file mode 100644 index 000000000..72375f4a1 --- /dev/null +++ b/src/arithmetic.rs @@ -0,0 +1,104 @@ +use rustc::hir; +use rustc::lint::*; +use syntax::codemap::Span; +use utils::span_lint; + +/// **What it does:** This lint checks for plain integer arithmetic +/// +/// **Why is this bad?** This is only checked against overflow in debug builds. +/// In some applications one wants explicitly checked, wrapping or saturating +/// arithmetic. +/// +/// **Known problems:** None +/// +/// **Example:** +/// ``` +/// a + 1 +/// ``` +declare_lint! { + pub INTEGER_ARITHMETIC, + Allow, + "Any integer arithmetic statement" +} + +/// **What it does:** This lint checks for float arithmetic +/// +/// **Why is this bad?** For some embedded systems or kernel development, it +/// can be useful to rule out floating-point numbers +/// +/// **Known problems:** None +/// +/// **Example:** +/// ``` +/// a + 1.0 +/// ``` +declare_lint! { + pub FLOAT_ARITHMETIC, + Allow, + "Any floating-point arithmetic statement" +} + +#[derive(Copy, Clone, Default)] +pub struct Arithmetic { + span: Option +} + +impl LintPass for Arithmetic { + fn get_lints(&self) -> LintArray { + lint_array!(INTEGER_ARITHMETIC, FLOAT_ARITHMETIC) + } +} + +impl LateLintPass for Arithmetic { + fn check_expr(&mut self, cx: &LateContext, expr: &hir::Expr) { + if let Some(_) = self.span { return; } + match expr.node { + hir::ExprBinary(ref op, ref l, ref r) => { + match op.node { + hir::BiRem | hir::BiAnd | hir::BiOr | hir::BiBitAnd | + hir::BiBitOr | hir::BiBitXor | hir::BiShl | hir::BiShr | + hir::BiEq | hir::BiLt | hir::BiLe | hir::BiNe | hir::BiGe | + hir::BiGt => return, + _ => () + } + let (l_ty, r_ty) = (cx.tcx.expr_ty(l), cx.tcx.expr_ty(r)); + if l_ty.is_integral() && r_ty.is_integral() { + span_lint(cx, + INTEGER_ARITHMETIC, + expr.span, + "integer arithmetic detected"); + self.span = Some(expr.span); + } else if l_ty.is_floating_point() && r_ty.is_floating_point() { + span_lint(cx, + FLOAT_ARITHMETIC, + expr.span, + "floating-point arithmetic detected"); + self.span = Some(expr.span); + } + }, + hir::ExprUnary(hir::UnOp::UnNeg, ref arg) => { + let ty = cx.tcx.expr_ty(arg); + if ty.is_integral() { + span_lint(cx, + INTEGER_ARITHMETIC, + expr.span, + "integer arithmetic detected"); + self.span = Some(expr.span); + } else if ty.is_floating_point() { + span_lint(cx, + FLOAT_ARITHMETIC, + expr.span, + "floating-point arithmetic detected"); + self.span = Some(expr.span); + } + }, + _ => () + } + } + + fn check_expr_post(&mut self, _: &LateContext, expr: &hir::Expr) { + if Some(expr.span) == self.span { + self.span = None; + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 14d1a19f2..584ae7f77 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ #![feature(question_mark)] #![feature(stmt_expr_attributes)] #![allow(indexing_slicing, shadow_reuse, unknown_lints)] +#![allow(float_arithmetic, integer_arithmetic)] // this only exists to allow the "dogfood" integration test to work #[allow(dead_code)] @@ -49,6 +50,7 @@ pub mod utils; // begin lints modules, do not remove this comment, it’s used in `update_lints` pub mod approx_const; +pub mod arithmetic; pub mod array_indexing; pub mod attrs; pub mod bit_mask; @@ -238,8 +240,11 @@ pub fn plugin_registrar(reg: &mut Registry) { reg.register_late_lint_pass(box neg_multiply::NegMultiply); reg.register_late_lint_pass(box unsafe_removed_from_name::UnsafeNameRemoval); reg.register_late_lint_pass(box mem_forget::MemForget); + reg.register_late_lint_pass(box arithmetic::Arithmetic::default()); reg.register_lint_group("clippy_pedantic", vec![ + arithmetic::FLOAT_ARITHMETIC, + arithmetic::INTEGER_ARITHMETIC, array_indexing::INDEXING_SLICING, booleans::NONMINIMAL_BOOL, enum_glob_use::ENUM_GLOB_USE, diff --git a/tests/compile-fail/arithmetic.rs b/tests/compile-fail/arithmetic.rs new file mode 100644 index 000000000..856f39094 --- /dev/null +++ b/tests/compile-fail/arithmetic.rs @@ -0,0 +1,31 @@ +#![feature(plugin)] +#![plugin(clippy)] + +#![deny(integer_arithmetic, float_arithmetic)] +#![allow(unused, shadow_reuse, shadow_unrelated, no_effect)] +fn main() { + let i = 1i32; + 1 + i; //~ERROR integer arithmetic detected + i * 2; //~ERROR integer arithmetic detected + 1 % //~ERROR integer arithmetic detected + i / 2; + i - 2 + 2 - i; //~ERROR integer arithmetic detected + -i; //~ERROR integer arithmetic detected + + i & 1; // no wrapping + i | 1; + i ^ 1; + i % 7; + i >> 1; + i << 1; + + let f = 1.0f32; + + f * 2.0; //~ERROR floating-point arithmetic detected + + 1.0 + f; //~ERROR floating-point arithmetic detected + f * 2.0; //~ERROR floating-point arithmetic detected + f / 2.0; //~ERROR floating-point arithmetic detected + f - 2.0 * 4.2; //~ERROR floating-point arithmetic detected + -f; //~ERROR floating-point arithmetic detected +}