Skip to content

Commit

Permalink
Add new lint to detect lossy whole-number float literals
Browse files Browse the repository at this point in the history
  • Loading branch information
krishna-veerareddy committed Feb 20, 2020
1 parent f3edbaf commit f2a486b
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1207,6 +1207,7 @@ Released 2018-09-13
[`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
[`manual_mul_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_mul_add
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.

[There are 355 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
[There are 356 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:

Expand Down
4 changes: 4 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ pub mod let_underscore;
pub mod lifetimes;
pub mod literal_representation;
pub mod loops;
pub mod lossy_float_literal;
pub mod main_recursion;
pub mod map_clone;
pub mod map_unit_fn;
Expand Down Expand Up @@ -597,6 +598,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&loops::WHILE_IMMUTABLE_CONDITION,
&loops::WHILE_LET_LOOP,
&loops::WHILE_LET_ON_ITERATOR,
&lossy_float_literal::LOSSY_FLOAT_LITERAL,
&main_recursion::MAIN_RECURSION,
&map_clone::MAP_CLONE,
&map_unit_fn::OPTION_MAP_UNIT_FN,
Expand Down Expand Up @@ -1003,6 +1005,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_early_pass(|| box utils::internal_lints::ProduceIce);
store.register_late_pass(|| box let_underscore::LetUnderscore);
store.register_late_pass(|| box atomic_ordering::AtomicOrdering);
store.register_late_pass(|| box lossy_float_literal::LossyFloatLiteral);
store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports);
let max_fn_params_bools = conf.max_fn_params_bools;
let max_struct_bools = conf.max_struct_bools;
Expand All @@ -1022,6 +1025,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&integer_division::INTEGER_DIVISION),
LintId::of(&let_underscore::LET_UNDERSCORE_MUST_USE),
LintId::of(&literal_representation::DECIMAL_LITERAL_REPRESENTATION),
LintId::of(&lossy_float_literal::LOSSY_FLOAT_LITERAL),
LintId::of(&matches::WILDCARD_ENUM_MATCH_ARM),
LintId::of(&mem_forget::MEM_FORGET),
LintId::of(&methods::CLONE_ON_REF_PTR),
Expand Down
85 changes: 85 additions & 0 deletions clippy_lints/src/lossy_float_literal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use crate::utils::span_lint_and_sugg;
use crate::utils::sugg::format_numeric_literal;
use if_chain::if_chain;
use rustc::ty;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use std::{f32, f64};
use syntax::ast::*;

declare_clippy_lint! {
/// **What it does:** Checks for whole number float literals that
/// cannot be represented as the underlying type without loss.
///
/// **Why is this bad?** Rust will silently lose precision during
/// conversion to a float.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// // Bad
/// let _: f32 = 16_777_217.0; // 16_777_216.0
///
/// // Good
/// let _: f32 = 16_777_216.0;
/// let _: f64 = 16_777_217.0;
/// ```
pub LOSSY_FLOAT_LITERAL,
restriction,
"lossy whole number float literals"
}

declare_lint_pass!(LossyFloatLiteral => [LOSSY_FLOAT_LITERAL]);

impl LateLintPass<'_, '_> for LossyFloatLiteral {
fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &'_ hir::Expr<'_>) {
if_chain! {
let ty = cx.tables.expr_ty(expr);
if let ty::Float(fty) = ty.kind;
if let hir::ExprKind::Lit(ref lit) = expr.kind;
if let LitKind::Float(sym, lit_float_ty) = lit.node;
let sym_str = sym.as_str();
// Ignore literals with exponential notation
if !sym_str.contains(|c| c == 'e' || c == 'E');
then {
let (is_whole, float_str) = match fty {
FloatTy::F32 => {
let value = sym_str.parse::<f32>().unwrap();

(value.fract() == 0.0, value.to_string())
},
FloatTy::F64 => {
let value = sym_str.parse::<f64>().unwrap();

(value.fract() == 0.0, value.to_string())
}
};
let type_suffix = match lit_float_ty {
LitFloatType::Suffixed(FloatTy::F32) => Some("f32"),
LitFloatType::Suffixed(FloatTy::F64) => Some("f64"),
_ => None
};

if is_whole && sym_str.split('.').next().unwrap() != float_str {
span_lint_and_sugg(
cx,
LOSSY_FLOAT_LITERAL,
expr.span,
"literal cannot be represented as the underlying type without loss of precision",
"consider changing the type or replacing it with",
format_numeric_literal(
format!("{}.0", float_str).as_str(),
type_suffix,
true
),
Applicability::MachineApplicable,
);
}
}
}
}
}
9 changes: 8 additions & 1 deletion src/lintlist/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub use lint::Lint;
pub use lint::LINT_LEVELS;

// begin lint list, do not remove this comment, it’s used in `update_lints`
pub const ALL_LINTS: [Lint; 355] = [
pub const ALL_LINTS: [Lint; 356] = [
Lint {
name: "absurd_extreme_comparisons",
group: "correctness",
Expand Down Expand Up @@ -1001,6 +1001,13 @@ pub const ALL_LINTS: [Lint; 355] = [
deprecation: None,
module: "booleans",
},
Lint {
name: "lossy_float_literal",
group: "restriction",
desc: "lossy whole number float literals",
deprecation: None,
module: "lossy_float_literal",
},
Lint {
name: "main_recursion",
group: "style",
Expand Down
35 changes: 35 additions & 0 deletions tests/ui/lossy_float_literal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#![warn(clippy::lossy_float_literal)]
#![allow(clippy::excessive_precision)]

fn main() {
// Lossy whole-number float literals
let _: f32 = 16_777_217.0;
let _: f32 = 16_777_219.0;
let _: f32 = 16_777_219.;
let _: f32 = 16_777_219.000;
let _ = 16_777_219f32;
let _: f32 = -16_777_219.0;
let _: f64 = 9_007_199_254_740_993.0;
let _: f64 = 9_007_199_254_740_993.;
let _: f64 = 9_007_199_254_740_993.00;
let _ = 9_007_199_254_740_993f64;
let _: f64 = -9_007_199_254_740_993.0;

// Lossless whole number float literals
let _: f32 = 16_777_216.0;
let _: f32 = 16_777_218.0;
let _: f32 = 16_777_220.0;
let _: f32 = -16_777_216.0;
let _: f32 = -16_777_220.0;
let _: f64 = 16_777_217.0;
let _: f64 = -16_777_217.0;
let _: f64 = 9_007_199_254_740_992.0;
let _: f64 = -9_007_199_254_740_992.0;

// Ignored whole number float literals
let _: f32 = 1e25;
let _: f32 = 1E25;
let _: f64 = 1e99;
let _: f64 = 1E99;
let _: f32 = 0.1;
}
70 changes: 70 additions & 0 deletions tests/ui/lossy_float_literal.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
error: literal cannot be represented as the underlying type without loss of precision
--> $DIR/lossy_float_literal.rs:6:18
|
LL | let _: f32 = 16_777_217.0;
| ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_216.0`
|
= note: `-D clippy::lossy-float-literal` implied by `-D warnings`

error: literal cannot be represented as the underlying type without loss of precision
--> $DIR/lossy_float_literal.rs:7:18
|
LL | let _: f32 = 16_777_219.0;
| ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`

error: literal cannot be represented as the underlying type without loss of precision
--> $DIR/lossy_float_literal.rs:8:18
|
LL | let _: f32 = 16_777_219.;
| ^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`

error: literal cannot be represented as the underlying type without loss of precision
--> $DIR/lossy_float_literal.rs:9:18
|
LL | let _: f32 = 16_777_219.000;
| ^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`

error: literal cannot be represented as the underlying type without loss of precision
--> $DIR/lossy_float_literal.rs:10:13
|
LL | let _ = 16_777_219f32;
| ^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0_f32`

error: literal cannot be represented as the underlying type without loss of precision
--> $DIR/lossy_float_literal.rs:11:19
|
LL | let _: f32 = -16_777_219.0;
| ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`

error: literal cannot be represented as the underlying type without loss of precision
--> $DIR/lossy_float_literal.rs:12:18
|
LL | let _: f64 = 9_007_199_254_740_993.0;
| ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`

error: literal cannot be represented as the underlying type without loss of precision
--> $DIR/lossy_float_literal.rs:13:18
|
LL | let _: f64 = 9_007_199_254_740_993.;
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`

error: literal cannot be represented as the underlying type without loss of precision
--> $DIR/lossy_float_literal.rs:14:18
|
LL | let _: f64 = 9_007_199_254_740_993.00;
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`

error: literal cannot be represented as the underlying type without loss of precision
--> $DIR/lossy_float_literal.rs:15:13
|
LL | let _ = 9_007_199_254_740_993f64;
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0_f64`

error: literal cannot be represented as the underlying type without loss of precision
--> $DIR/lossy_float_literal.rs:16:19
|
LL | let _: f64 = -9_007_199_254_740_993.0;
| ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`

error: aborting due to 11 previous errors

0 comments on commit f2a486b

Please sign in to comment.