diff --git a/CHANGELOG.md b/CHANGELOG.md index b63111cab72..5f34d24cd47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -956,6 +956,7 @@ All notable changes to this project will be documented in this file. [`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body [`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one [`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic +[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division [`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array [`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref [`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref diff --git a/README.md b/README.md index c25c83d8752..4c2e65300a6 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are 304 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are 305 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you: diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 38d7307a187..f01322768fa 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -74,7 +74,8 @@ impl CognitiveComplexity { let ret_adjust = if match_type(cx, ret_ty, &paths::RESULT) { returns } else { - returns / 2 + #[allow(clippy::integer_division)] + (returns / 2) }; if cc + divergence < match_arms + short_circuits { diff --git a/clippy_lints/src/integer_division.rs b/clippy_lints/src/integer_division.rs new file mode 100644 index 00000000000..12dcfa6f661 --- /dev/null +++ b/clippy_lints/src/integer_division.rs @@ -0,0 +1,55 @@ +use crate::utils::span_help_and_lint; +use if_chain::if_chain; +use rustc::hir; +use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; +use rustc::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for division of integers + /// + /// **Why is this bad?** When outside of some very specific algorithms, + /// integer division is very often a mistake because it discards the + /// remainder. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn main() { + /// let x = 3 / 2; + /// println!("{}", x); + /// } + /// ``` + pub INTEGER_DIVISION, + pedantic, + "integer division may cause loss of precision" +} + +declare_lint_pass!(IntegerDivision => [INTEGER_DIVISION]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IntegerDivision { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) { + if is_integer_division(cx, expr) { + span_help_and_lint( + cx, + INTEGER_DIVISION, + expr.span, + "integer division", + "division of integers may cause loss of precision. consider using floats.", + ); + } + } +} + +fn is_integer_division<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) -> bool { + if_chain! { + if let hir::ExprKind::Binary(binop, left, right) = &expr.node; + if let hir::BinOpKind::Div = &binop.node; + then { + let (left_ty, right_ty) = (cx.tables.expr_ty(left), cx.tables.expr_ty(right)); + return left_ty.is_integral() && right_ty.is_integral(); + } + } + + false +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index eaf8f9b5710..2bec5cb2d01 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -197,6 +197,7 @@ pub mod infinite_iter; pub mod inherent_impl; pub mod inline_fn_without_body; pub mod int_plus_one; +pub mod integer_division; pub mod invalid_ref; pub mod items_after_statements; pub mod large_enum_variant; @@ -580,6 +581,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { reg.register_late_lint_pass(box transmuting_null::TransmutingNull); reg.register_late_lint_pass(box path_buf_push_overwrite::PathBufPushOverwrite); reg.register_late_lint_pass(box checked_conversions::CheckedConversions); + reg.register_late_lint_pass(box integer_division::IntegerDivision); reg.register_lint_group("clippy::restriction", Some("clippy_restriction"), vec![ arithmetic::FLOAT_ARITHMETIC, @@ -624,6 +626,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { functions::TOO_MANY_LINES, if_not_else::IF_NOT_ELSE, infinite_iter::MAYBE_INFINITE_ITER, + integer_division::INTEGER_DIVISION, items_after_statements::ITEMS_AFTER_STATEMENTS, literal_representation::LARGE_DIGIT_GROUPS, loops::EXPLICIT_INTO_ITER_LOOP, diff --git a/clippy_lints/src/trivially_copy_pass_by_ref.rs b/clippy_lints/src/trivially_copy_pass_by_ref.rs index 319f6311e88..e4c4000c126 100644 --- a/clippy_lints/src/trivially_copy_pass_by_ref.rs +++ b/clippy_lints/src/trivially_copy_pass_by_ref.rs @@ -64,6 +64,7 @@ impl<'a, 'tcx> TriviallyCopyPassByRef { // Cap the calculated bit width at 32-bits to reduce // portability problems between 32 and 64-bit targets let bit_width = cmp::min(bit_width, 32); + #[allow(clippy::integer_division)] let byte_width = bit_width / 8; // Use a limit of 2 times the register byte width byte_width * 2 diff --git a/tests/ui/integer_division.rs b/tests/ui/integer_division.rs new file mode 100644 index 00000000000..800c7525752 --- /dev/null +++ b/tests/ui/integer_division.rs @@ -0,0 +1,9 @@ +#![warn(clippy::integer_division)] + +fn main() { + let two = 2; + let n = 1 / 2; + let o = 1 / two; + let p = two / 4; + let x = 1. / 2.0; +} diff --git a/tests/ui/integer_division.stderr b/tests/ui/integer_division.stderr new file mode 100644 index 00000000000..72a232ef3d7 --- /dev/null +++ b/tests/ui/integer_division.stderr @@ -0,0 +1,27 @@ +error: integer division + --> $DIR/integer_division.rs:5:13 + | +LL | let n = 1 / 2; + | ^^^^^ + | + = note: `-D clippy::integer-division` implied by `-D warnings` + = help: division of integers may cause loss of precision. consider using floats. + +error: integer division + --> $DIR/integer_division.rs:6:13 + | +LL | let o = 1 / two; + | ^^^^^^^ + | + = help: division of integers may cause loss of precision. consider using floats. + +error: integer division + --> $DIR/integer_division.rs:7:13 + | +LL | let p = two / 4; + | ^^^^^^^ + | + = help: division of integers may cause loss of precision. consider using floats. + +error: aborting due to 3 previous errors +