-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add new lint to detect lossy whole-number float literals
- Loading branch information
1 parent
f3edbaf
commit f2a486b
Showing
7 changed files
with
204 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
); | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|