Skip to content

Commit 219c94d

Browse files
Separate out lint to check lossy whole number float literals
1 parent 0d7ae7b commit 219c94d

11 files changed

+214
-145
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,7 @@ Released 2018-09-13
12071207
[`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
12081208
[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
12091209
[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
1210+
[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
12101211
[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
12111212
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
12121213
[`manual_mul_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_mul_add

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

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

8-
[There are 355 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
8+
[There are 356 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
99

1010
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1111

clippy_lints/src/excessive_precision.rs renamed to clippy_lints/src/float_literal.rs

+54-25
Original file line numberDiff line numberDiff line change
@@ -13,30 +13,53 @@ declare_clippy_lint! {
1313
/// **What it does:** Checks for float literals with a precision greater
1414
/// than that supported by the underlying type.
1515
///
16-
/// **Why is this bad?** Rust will silently lose precision during conversion
17-
/// to a float.
16+
/// **Why is this bad?** Rust will truncate the literal silently.
1817
///
1918
/// **Known problems:** None.
2019
///
2120
/// **Example:**
2221
///
2322
/// ```rust
2423
/// // Bad
25-
/// let a: f32 = 0.123_456_789_9; // 0.123_456_789
26-
/// let b: f32 = 16_777_217.0; // 16_777_216.0
24+
/// let v: f32 = 0.123_456_789_9;
25+
/// println!("{}", v); // 0.123_456_789
2726
///
2827
/// // Good
29-
/// let a: f64 = 0.123_456_789_9;
30-
/// let b: f64 = 16_777_216.0;
28+
/// let v: f64 = 0.123_456_789_9;
29+
/// println!("{}", v); // 0.123_456_789_9
3130
/// ```
3231
pub EXCESSIVE_PRECISION,
33-
correctness,
32+
style,
3433
"excessive precision for float literal"
3534
}
3635

37-
declare_lint_pass!(ExcessivePrecision => [EXCESSIVE_PRECISION]);
36+
declare_clippy_lint! {
37+
/// **What it does:** Checks for whole number float literals that
38+
/// cannot be represented as the underlying type without loss.
39+
///
40+
/// **Why is this bad?** Rust will silently lose precision during
41+
/// conversion to a float.
42+
///
43+
/// **Known problems:** None.
44+
///
45+
/// **Example:**
46+
///
47+
/// ```rust
48+
/// // Bad
49+
/// let _: f32 = 16_777_217.0; // 16_777_216.0
50+
///
51+
/// // Good
52+
/// let _: f32 = 16_777_216.0;
53+
/// let _: f64 = 16_777_217.0;
54+
/// ```
55+
pub LOSSY_FLOAT_LITERAL,
56+
restriction,
57+
"lossy whole number float literals"
58+
}
59+
60+
declare_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]);
3861

39-
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExcessivePrecision {
62+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FloatLiteral {
4063
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
4164
if_chain! {
4265
let ty = cx.tables.expr_ty(expr);
@@ -52,26 +75,41 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExcessivePrecision {
5275
// since we'll need the truncated string anyway.
5376
let digits = count_digits(&sym_str);
5477
let max = max_digits(fty);
55-
let float_str = match fty {
56-
FloatTy::F32 => sym_str.parse::<f32>().map(|f| formatter.format(f)),
57-
FloatTy::F64 => sym_str.parse::<f64>().map(|f| formatter.format(f)),
58-
}.unwrap();
5978
let type_suffix = match lit_float_ty {
6079
LitFloatType::Suffixed(FloatTy::F32) => Some("f32"),
6180
LitFloatType::Suffixed(FloatTy::F64) => Some("f64"),
6281
_ => None
6382
};
83+
let (is_whole, mut float_str) = match fty {
84+
FloatTy::F32 => {
85+
let value = sym_str.parse::<f32>().unwrap();
86+
87+
(value.fract() == 0.0, formatter.format(value))
88+
},
89+
FloatTy::F64 => {
90+
let value = sym_str.parse::<f64>().unwrap();
91+
92+
(value.fract() == 0.0, formatter.format(value))
93+
},
94+
};
6495

65-
if is_whole_number(&sym_str, fty) {
96+
if is_whole && !sym_str.contains(|c| c == 'e' || c == 'E') {
6697
// Normalize the literal by stripping the fractional portion
6798
if sym_str.split('.').next().unwrap() != float_str {
99+
// If the type suffix is missing the suggestion would be
100+
// incorrectly interpreted as an integer so adding a `.0`
101+
// suffix to prevent that.
102+
if type_suffix.is_none() {
103+
float_str.push_str(".0");
104+
}
105+
68106
span_lint_and_sugg(
69107
cx,
70-
EXCESSIVE_PRECISION,
108+
LOSSY_FLOAT_LITERAL,
71109
expr.span,
72110
"literal cannot be represented as the underlying type without loss of precision",
73111
"consider changing the type or replacing it with",
74-
format_numeric_literal(format!("{}.0", float_str).as_str(), type_suffix, true),
112+
format_numeric_literal(&float_str, type_suffix, true),
75113
Applicability::MachineApplicable,
76114
);
77115
}
@@ -91,15 +129,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExcessivePrecision {
91129
}
92130
}
93131

94-
// Checks whether a float literal is a whole number
95-
#[must_use]
96-
fn is_whole_number(sym_str: &str, fty: FloatTy) -> bool {
97-
match fty {
98-
FloatTy::F32 => sym_str.parse::<f32>().unwrap().fract() == 0.0,
99-
FloatTy::F64 => sym_str.parse::<f64>().unwrap().fract() == 0.0,
100-
}
101-
}
102-
103132
#[must_use]
104133
fn max_digits(fty: FloatTy) -> u32 {
105134
match fty {

clippy_lints/src/lib.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -205,10 +205,10 @@ pub mod escape;
205205
pub mod eta_reduction;
206206
pub mod eval_order_dependence;
207207
pub mod excessive_bools;
208-
pub mod excessive_precision;
209208
pub mod exit;
210209
pub mod explicit_write;
211210
pub mod fallible_impl_from;
211+
pub mod float_literal;
212212
pub mod format;
213213
pub mod formatting;
214214
pub mod functions;
@@ -534,10 +534,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
534534
&eval_order_dependence::EVAL_ORDER_DEPENDENCE,
535535
&excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
536536
&excessive_bools::STRUCT_EXCESSIVE_BOOLS,
537-
&excessive_precision::EXCESSIVE_PRECISION,
538537
&exit::EXIT,
539538
&explicit_write::EXPLICIT_WRITE,
540539
&fallible_impl_from::FALLIBLE_IMPL_FROM,
540+
&float_literal::EXCESSIVE_PRECISION,
541+
&float_literal::LOSSY_FLOAT_LITERAL,
541542
&format::USELESS_FORMAT,
542543
&formatting::POSSIBLE_MISSING_COMMA,
543544
&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
@@ -836,7 +837,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
836837
store.register_late_pass(|| box eq_op::EqOp);
837838
store.register_late_pass(|| box enum_glob_use::EnumGlobUse);
838839
store.register_late_pass(|| box enum_clike::UnportableVariant);
839-
store.register_late_pass(|| box excessive_precision::ExcessivePrecision);
840+
store.register_late_pass(|| box float_literal::FloatLiteral);
840841
let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
841842
store.register_late_pass(move || box bit_mask::BitMask::new(verbose_bit_mask_threshold));
842843
store.register_late_pass(|| box ptr::Ptr);
@@ -1016,6 +1017,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10161017
LintId::of(&dbg_macro::DBG_MACRO),
10171018
LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
10181019
LintId::of(&exit::EXIT),
1020+
LintId::of(&float_literal::LOSSY_FLOAT_LITERAL),
10191021
LintId::of(&implicit_return::IMPLICIT_RETURN),
10201022
LintId::of(&indexing_slicing::INDEXING_SLICING),
10211023
LintId::of(&inherent_impl::MULTIPLE_INHERENT_IMPL),
@@ -1159,8 +1161,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
11591161
LintId::of(&eta_reduction::REDUNDANT_CLOSURE),
11601162
LintId::of(&eval_order_dependence::DIVERGING_SUB_EXPRESSION),
11611163
LintId::of(&eval_order_dependence::EVAL_ORDER_DEPENDENCE),
1162-
LintId::of(&excessive_precision::EXCESSIVE_PRECISION),
11631164
LintId::of(&explicit_write::EXPLICIT_WRITE),
1165+
LintId::of(&float_literal::EXCESSIVE_PRECISION),
11641166
LintId::of(&format::USELESS_FORMAT),
11651167
LintId::of(&formatting::POSSIBLE_MISSING_COMMA),
11661168
LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
@@ -1386,6 +1388,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
13861388
LintId::of(&enum_variants::MODULE_INCEPTION),
13871389
LintId::of(&eq_op::OP_REF),
13881390
LintId::of(&eta_reduction::REDUNDANT_CLOSURE),
1391+
LintId::of(&float_literal::EXCESSIVE_PRECISION),
13891392
LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
13901393
LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING),
13911394
LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
@@ -1566,7 +1569,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
15661569
LintId::of(&enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
15671570
LintId::of(&eq_op::EQ_OP),
15681571
LintId::of(&erasing_op::ERASING_OP),
1569-
LintId::of(&excessive_precision::EXCESSIVE_PRECISION),
15701572
LintId::of(&formatting::POSSIBLE_MISSING_COMMA),
15711573
LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF),
15721574
LintId::of(&indexing_slicing::OUT_OF_BOUNDS_INDEXING),

src/lintlist/mod.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub use lint::Lint;
66
pub use lint::LINT_LEVELS;
77

88
// begin lint list, do not remove this comment, it’s used in `update_lints`
9-
pub const ALL_LINTS: [Lint; 355] = [
9+
pub const ALL_LINTS: [Lint; 356] = [
1010
Lint {
1111
name: "absurd_extreme_comparisons",
1212
group: "correctness",
@@ -492,10 +492,10 @@ pub const ALL_LINTS: [Lint; 355] = [
492492
},
493493
Lint {
494494
name: "excessive_precision",
495-
group: "correctness",
495+
group: "style",
496496
desc: "excessive precision for float literal",
497497
deprecation: None,
498-
module: "excessive_precision",
498+
module: "float_literal",
499499
},
500500
Lint {
501501
name: "exit",
@@ -1001,6 +1001,13 @@ pub const ALL_LINTS: [Lint; 355] = [
10011001
deprecation: None,
10021002
module: "booleans",
10031003
},
1004+
Lint {
1005+
name: "lossy_float_literal",
1006+
group: "restriction",
1007+
desc: "lossy whole number float literals",
1008+
deprecation: None,
1009+
module: "float_literal",
1010+
},
10041011
Lint {
10051012
name: "main_recursion",
10061013
group: "style",

tests/ui/excessive_precision.fixed

-22
Original file line numberDiff line numberDiff line change
@@ -60,26 +60,4 @@ fn main() {
6060

6161
// issue #2840
6262
let num = 0.000_000_000_01e-10f64;
63-
64-
// Lossy whole-number float literals
65-
let _: f32 = 16_777_216.0;
66-
let _: f32 = 16_777_220.0;
67-
let _: f32 = 16_777_220.0;
68-
let _: f32 = 16_777_220.0;
69-
let _ = 16_777_220.0_f32;
70-
let _: f32 = -16_777_220.0;
71-
let _: f64 = 9_007_199_254_740_992.0;
72-
let _: f64 = 9_007_199_254_740_992.0;
73-
let _: f64 = 9_007_199_254_740_992.0;
74-
let _ = 9_007_199_254_740_992.0_f64;
75-
let _: f64 = -9_007_199_254_740_992.0;
76-
77-
// Lossless whole number float literals
78-
let _: f32 = 16_777_216.0;
79-
let _: f32 = 16_777_218.0;
80-
let _: f32 = 16_777_220.0;
81-
let _: f32 = -16_777_216.0;
82-
let _: f32 = -16_777_220.0;
83-
let _: f64 = 9_007_199_254_740_992.0;
84-
let _: f64 = -9_007_199_254_740_992.0;
8563
}

tests/ui/excessive_precision.rs

-22
Original file line numberDiff line numberDiff line change
@@ -60,26 +60,4 @@ fn main() {
6060

6161
// issue #2840
6262
let num = 0.000_000_000_01e-10f64;
63-
64-
// Lossy whole-number float literals
65-
let _: f32 = 16_777_217.0;
66-
let _: f32 = 16_777_219.0;
67-
let _: f32 = 16_777_219.;
68-
let _: f32 = 16_777_219.000;
69-
let _ = 16_777_219f32;
70-
let _: f32 = -16_777_219.0;
71-
let _: f64 = 9_007_199_254_740_993.0;
72-
let _: f64 = 9_007_199_254_740_993.;
73-
let _: f64 = 9_007_199_254_740_993.000;
74-
let _ = 9_007_199_254_740_993f64;
75-
let _: f64 = -9_007_199_254_740_993.0;
76-
77-
// Lossless whole number float literals
78-
let _: f32 = 16_777_216.0;
79-
let _: f32 = 16_777_218.0;
80-
let _: f32 = 16_777_220.0;
81-
let _: f32 = -16_777_216.0;
82-
let _: f32 = -16_777_220.0;
83-
let _: f64 = 9_007_199_254_740_992.0;
84-
let _: f64 = -9_007_199_254_740_992.0;
8563
}

tests/ui/excessive_precision.stderr

+1-67
Original file line numberDiff line numberDiff line change
@@ -108,71 +108,5 @@ error: float has excessive precision
108108
LL | let bad_bige32: f32 = 1.123_456_788_888E-10;
109109
| ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8E-10`
110110

111-
error: literal cannot be represented as the underlying type without loss of precision
112-
--> $DIR/excessive_precision.rs:65:18
113-
|
114-
LL | let _: f32 = 16_777_217.0;
115-
| ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_216.0`
116-
117-
error: literal cannot be represented as the underlying type without loss of precision
118-
--> $DIR/excessive_precision.rs:66:18
119-
|
120-
LL | let _: f32 = 16_777_219.0;
121-
| ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
122-
123-
error: literal cannot be represented as the underlying type without loss of precision
124-
--> $DIR/excessive_precision.rs:67:18
125-
|
126-
LL | let _: f32 = 16_777_219.;
127-
| ^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
128-
129-
error: literal cannot be represented as the underlying type without loss of precision
130-
--> $DIR/excessive_precision.rs:68:18
131-
|
132-
LL | let _: f32 = 16_777_219.000;
133-
| ^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
134-
135-
error: literal cannot be represented as the underlying type without loss of precision
136-
--> $DIR/excessive_precision.rs:69:13
137-
|
138-
LL | let _ = 16_777_219f32;
139-
| ^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0_f32`
140-
141-
error: literal cannot be represented as the underlying type without loss of precision
142-
--> $DIR/excessive_precision.rs:70:19
143-
|
144-
LL | let _: f32 = -16_777_219.0;
145-
| ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
146-
147-
error: literal cannot be represented as the underlying type without loss of precision
148-
--> $DIR/excessive_precision.rs:71:18
149-
|
150-
LL | let _: f64 = 9_007_199_254_740_993.0;
151-
| ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
152-
153-
error: literal cannot be represented as the underlying type without loss of precision
154-
--> $DIR/excessive_precision.rs:72:18
155-
|
156-
LL | let _: f64 = 9_007_199_254_740_993.;
157-
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
158-
159-
error: literal cannot be represented as the underlying type without loss of precision
160-
--> $DIR/excessive_precision.rs:73:18
161-
|
162-
LL | let _: f64 = 9_007_199_254_740_993.000;
163-
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
164-
165-
error: literal cannot be represented as the underlying type without loss of precision
166-
--> $DIR/excessive_precision.rs:74:13
167-
|
168-
LL | let _ = 9_007_199_254_740_993f64;
169-
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0_f64`
170-
171-
error: literal cannot be represented as the underlying type without loss of precision
172-
--> $DIR/excessive_precision.rs:75:19
173-
|
174-
LL | let _: f64 = -9_007_199_254_740_993.0;
175-
| ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
176-
177-
error: aborting due to 29 previous errors
111+
error: aborting due to 18 previous errors
178112

0 commit comments

Comments
 (0)