mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-27 07:00:55 +00:00
New excessive precision lint for floats
This commit is contained in:
parent
79e818267b
commit
9b14ad493b
6 changed files with 300 additions and 3 deletions
141
clippy_lints/src/excessive_precision.rs
Normal file
141
clippy_lints/src/excessive_precision.rs
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
use rustc::hir;
|
||||||
|
use rustc::lint::*;
|
||||||
|
use rustc::ty::TypeVariants;
|
||||||
|
use std::f32;
|
||||||
|
use std::f64;
|
||||||
|
use std::fmt;
|
||||||
|
use syntax::ast::*;
|
||||||
|
use syntax_pos::symbol::Symbol;
|
||||||
|
use utils::span_lint_and_sugg;
|
||||||
|
|
||||||
|
/// **What it does:** Checks for float literals with a precision greater
|
||||||
|
/// than that supported by the underlying type
|
||||||
|
///
|
||||||
|
/// **Why is this bad?** Rust will truncate the literal silently.
|
||||||
|
///
|
||||||
|
/// **Known problems:** None.
|
||||||
|
///
|
||||||
|
/// **Example:**
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// // Bad
|
||||||
|
/// Insert a short example of code that triggers the lint
|
||||||
|
/// let v: f32 = 0.123_456_789_9;
|
||||||
|
/// println!("{}", v); // 0.123_456_789
|
||||||
|
///
|
||||||
|
/// // Good
|
||||||
|
/// Insert a short example of improved code that doesn't trigger the lint
|
||||||
|
/// let v: f64 = 0.123_456_789_9;
|
||||||
|
/// println!("{}", v); // 0.123_456_789_9
|
||||||
|
/// ```
|
||||||
|
declare_clippy_lint! {
|
||||||
|
pub EXCESSIVE_PRECISION,
|
||||||
|
style,
|
||||||
|
"excessive precision for float literal"
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ExcessivePrecision;
|
||||||
|
|
||||||
|
impl LintPass for ExcessivePrecision {
|
||||||
|
fn get_lints(&self) -> LintArray {
|
||||||
|
lint_array!(EXCESSIVE_PRECISION)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExcessivePrecision {
|
||||||
|
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
|
||||||
|
if_chain! {
|
||||||
|
let ty = cx.tables.expr_ty(expr);
|
||||||
|
if let TypeVariants::TyFloat(ref fty) = ty.sty;
|
||||||
|
if let hir::ExprLit(ref lit) = expr.node;
|
||||||
|
if let LitKind::Float(ref sym, _) | LitKind::FloatUnsuffixed(ref sym) = lit.node;
|
||||||
|
if let Some(sugg) = self.check(sym, fty);
|
||||||
|
then {
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
EXCESSIVE_PRECISION,
|
||||||
|
expr.span,
|
||||||
|
"float has excessive precision",
|
||||||
|
"consider changing the type or truncating it to",
|
||||||
|
sugg,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExcessivePrecision {
|
||||||
|
// None if nothing to lint, Some(suggestion) if lint neccessary
|
||||||
|
fn check(&self, sym: &Symbol, fty: &FloatTy) -> Option<String> {
|
||||||
|
let max = max_digits(fty);
|
||||||
|
let sym_str = sym.as_str();
|
||||||
|
let formatter = FloatFormat::new(&sym_str);
|
||||||
|
let digits = count_digits(&sym_str);
|
||||||
|
// Try to bail out if the float is for sure fine.
|
||||||
|
// If its within the 2 decimal digits of being out of precision we
|
||||||
|
// check if the parsed representation is the same as the string
|
||||||
|
// since we'll need the truncated string anyway.
|
||||||
|
if digits > max as usize {
|
||||||
|
let sr = match *fty {
|
||||||
|
FloatTy::F32 => sym_str.parse::<f32>().map(|f| formatter.format(f)),
|
||||||
|
FloatTy::F64 => sym_str.parse::<f64>().map(|f| formatter.format(f)),
|
||||||
|
};
|
||||||
|
// We know this will parse since we are in LatePass
|
||||||
|
let s = sr.unwrap();
|
||||||
|
|
||||||
|
if sym_str == s {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(s)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn max_digits(fty: &FloatTy) -> u32 {
|
||||||
|
match fty {
|
||||||
|
FloatTy::F32 => f32::DIGITS,
|
||||||
|
FloatTy::F64 => f64::DIGITS,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn count_digits(s: &str) -> usize {
|
||||||
|
s.chars()
|
||||||
|
.filter(|c| *c != '-' || *c != '.')
|
||||||
|
.take_while(|c| *c != 'e' || *c != 'E')
|
||||||
|
.fold(0, |count, c| {
|
||||||
|
// leading zeros
|
||||||
|
if c == '0' && count == 0 {
|
||||||
|
count
|
||||||
|
} else {
|
||||||
|
count + 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
enum FloatFormat {
|
||||||
|
LowerExp,
|
||||||
|
UpperExp,
|
||||||
|
Normal,
|
||||||
|
}
|
||||||
|
impl FloatFormat {
|
||||||
|
fn new(s: &str) -> Self {
|
||||||
|
s.chars()
|
||||||
|
.find_map(|x| match x {
|
||||||
|
'e' => Some(FloatFormat::LowerExp),
|
||||||
|
'E' => Some(FloatFormat::UpperExp),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.unwrap_or(FloatFormat::Normal)
|
||||||
|
}
|
||||||
|
fn format<T>(&self, f: T) -> String
|
||||||
|
where T: fmt::UpperExp + fmt::LowerExp + fmt::Display {
|
||||||
|
match self {
|
||||||
|
FloatFormat::LowerExp => format!("{:e}", f),
|
||||||
|
FloatFormat::UpperExp => format!("{:E}", f),
|
||||||
|
FloatFormat::Normal => format!("{}", f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,8 @@
|
||||||
// FIXME(mark-i-m) remove after i128 stablization merges
|
// FIXME(mark-i-m) remove after i128 stablization merges
|
||||||
#![allow(stable_features)]
|
#![allow(stable_features)]
|
||||||
#![feature(i128, i128_type)]
|
#![feature(i128, i128_type)]
|
||||||
|
#![feature(iterator_find_map)]
|
||||||
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rustc;
|
extern crate rustc;
|
||||||
|
@ -124,6 +126,7 @@ pub mod erasing_op;
|
||||||
pub mod escape;
|
pub mod escape;
|
||||||
pub mod eta_reduction;
|
pub mod eta_reduction;
|
||||||
pub mod eval_order_dependence;
|
pub mod eval_order_dependence;
|
||||||
|
pub mod excessive_precision;
|
||||||
pub mod explicit_write;
|
pub mod explicit_write;
|
||||||
pub mod fallible_impl_from;
|
pub mod fallible_impl_from;
|
||||||
pub mod format;
|
pub mod format;
|
||||||
|
@ -294,6 +297,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
||||||
reg.register_early_lint_pass(box enum_variants::EnumVariantNames::new(conf.enum_variant_name_threshold));
|
reg.register_early_lint_pass(box enum_variants::EnumVariantNames::new(conf.enum_variant_name_threshold));
|
||||||
reg.register_late_lint_pass(box enum_glob_use::EnumGlobUse);
|
reg.register_late_lint_pass(box enum_glob_use::EnumGlobUse);
|
||||||
reg.register_late_lint_pass(box enum_clike::UnportableVariant);
|
reg.register_late_lint_pass(box enum_clike::UnportableVariant);
|
||||||
|
reg.register_late_lint_pass(box excessive_precision::ExcessivePrecision);
|
||||||
reg.register_late_lint_pass(box bit_mask::BitMask::new(conf.verbose_bit_mask_threshold));
|
reg.register_late_lint_pass(box bit_mask::BitMask::new(conf.verbose_bit_mask_threshold));
|
||||||
reg.register_late_lint_pass(box ptr::PointerPass);
|
reg.register_late_lint_pass(box ptr::PointerPass);
|
||||||
reg.register_late_lint_pass(box needless_bool::NeedlessBool);
|
reg.register_late_lint_pass(box needless_bool::NeedlessBool);
|
||||||
|
|
|
@ -42,7 +42,7 @@ fn main() {
|
||||||
let my_ln_2 = 0.6931471805599453;
|
let my_ln_2 = 0.6931471805599453;
|
||||||
let no_ln_2 = 0.693;
|
let no_ln_2 = 0.693;
|
||||||
|
|
||||||
let my_log10_e = 0.43429448190325182;
|
let my_log10_e = 0.4342944819032518;
|
||||||
let no_log10_e = 0.434;
|
let no_log10_e = 0.434;
|
||||||
|
|
||||||
let my_log2_e = 1.4426950408889634;
|
let my_log2_e = 1.4426950408889634;
|
||||||
|
|
|
@ -87,8 +87,8 @@ error: approximate value of `f{32, 64}::consts::LN_2` found. Consider using it d
|
||||||
error: approximate value of `f{32, 64}::consts::LOG10_E` found. Consider using it directly
|
error: approximate value of `f{32, 64}::consts::LOG10_E` found. Consider using it directly
|
||||||
--> $DIR/approx_const.rs:45:22
|
--> $DIR/approx_const.rs:45:22
|
||||||
|
|
|
|
||||||
45 | let my_log10_e = 0.43429448190325182;
|
45 | let my_log10_e = 0.4342944819032518;
|
||||||
| ^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: approximate value of `f{32, 64}::consts::LOG2_E` found. Consider using it directly
|
error: approximate value of `f{32, 64}::consts::LOG2_E` found. Consider using it directly
|
||||||
--> $DIR/approx_const.rs:48:21
|
--> $DIR/approx_const.rs:48:21
|
||||||
|
|
52
tests/ui/excessive_precision.rs
Normal file
52
tests/ui/excessive_precision.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
#![feature(plugin, custom_attribute)]
|
||||||
|
#![warn(excessive_precision)]
|
||||||
|
#![allow(print_literal)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// TODO add prefix tests
|
||||||
|
// Consts
|
||||||
|
const GOOD32_SUF: f32 = 0.123_456_f32;
|
||||||
|
const GOOD32: f32 = 0.123_456;
|
||||||
|
const GOOD32_SM: f32 = 0.000_000_000_1;
|
||||||
|
const GOOD64: f64 = 0.123_456_789_012;
|
||||||
|
const GOOD64_SM: f32 = 0.000_000_000_000_000_1;
|
||||||
|
|
||||||
|
const BAD32_1: f32 = 0.123_456_789_f32;
|
||||||
|
const BAD32_2: f32 = 0.123_456_789;
|
||||||
|
const BAD32_3: f32 = 0.100_000_000_000_1;
|
||||||
|
|
||||||
|
const BAD64_1: f64 = 0.123_456_789_012_345_67f64;
|
||||||
|
const BAD64_2: f64 = 0.123_456_789_012_345_67;
|
||||||
|
const BAD64_3: f64 = 0.100_000_000_000_000_000_1;
|
||||||
|
|
||||||
|
// Literal
|
||||||
|
println!("{}", 8.888_888_888_888_888_888_888);
|
||||||
|
|
||||||
|
// TODO add inferred type tests for f32
|
||||||
|
// TODO add tests cases exactly on the edge
|
||||||
|
// Locals
|
||||||
|
let good32: f32 = 0.123_456_f32;
|
||||||
|
let good32_2: f32 = 0.123_456;
|
||||||
|
|
||||||
|
let good64: f64 = 0.123_456_789_012f64;
|
||||||
|
let good64: f64 = 0.123_456_789_012;
|
||||||
|
let good64_2 = 0.123_456_789_012;
|
||||||
|
|
||||||
|
let bad32_1: f32 = 1.123_456_789_f32;
|
||||||
|
let bad32_2: f32 = 1.123_456_789;
|
||||||
|
|
||||||
|
let bad64_1: f64 = 0.123_456_789_012_345_67f64;
|
||||||
|
let bad64_2: f64 = 0.123_456_789_012_345_67;
|
||||||
|
let bad64_3 = 0.123_456_789_012_345_67;
|
||||||
|
|
||||||
|
// TODO Vectors / nested vectors
|
||||||
|
let vec32: Vec<f32> = vec![0.123_456_789];
|
||||||
|
let vec64: Vec<f64> = vec![0.123_456_789_123_456_789];
|
||||||
|
|
||||||
|
// Exponential float notation
|
||||||
|
let good_e32: f32 = 1e-10;
|
||||||
|
let bad_e32: f32 = 1.123_456_788_888e-10;
|
||||||
|
|
||||||
|
let good_bige32: f32 = 1E-10;
|
||||||
|
let bad_bige32: f32 = 1.123_456_788_888E-10;
|
||||||
|
}
|
100
tests/ui/excessive_precision.stderr
Normal file
100
tests/ui/excessive_precision.stderr
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
error: float has excessive precision
|
||||||
|
--> $DIR/excessive_precision.rs:14:26
|
||||||
|
|
|
||||||
|
14 | const BAD32_1: f32 = 0.123_456_789_f32;
|
||||||
|
| ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.12345679`
|
||||||
|
|
|
||||||
|
= note: `-D excessive-precision` implied by `-D warnings`
|
||||||
|
|
||||||
|
error: float has excessive precision
|
||||||
|
--> $DIR/excessive_precision.rs:15:26
|
||||||
|
|
|
||||||
|
15 | const BAD32_2: f32 = 0.123_456_789;
|
||||||
|
| ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.12345679`
|
||||||
|
|
||||||
|
error: float has excessive precision
|
||||||
|
--> $DIR/excessive_precision.rs:16:26
|
||||||
|
|
|
||||||
|
16 | const BAD32_3: f32 = 0.100_000_000_000_1;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.1`
|
||||||
|
|
||||||
|
error: float has excessive precision
|
||||||
|
--> $DIR/excessive_precision.rs:18:26
|
||||||
|
|
|
||||||
|
18 | const BAD64_1: f64 = 0.123_456_789_012_345_67f64;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.12345678901234566`
|
||||||
|
|
||||||
|
error: float has excessive precision
|
||||||
|
--> $DIR/excessive_precision.rs:19:26
|
||||||
|
|
|
||||||
|
19 | const BAD64_2: f64 = 0.123_456_789_012_345_67;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.12345678901234566`
|
||||||
|
|
||||||
|
error: float has excessive precision
|
||||||
|
--> $DIR/excessive_precision.rs:20:26
|
||||||
|
|
|
||||||
|
20 | const BAD64_3: f64 = 0.100_000_000_000_000_000_1;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.1`
|
||||||
|
|
||||||
|
error: float has excessive precision
|
||||||
|
--> $DIR/excessive_precision.rs:23:20
|
||||||
|
|
|
||||||
|
23 | println!("{}", 8.888_888_888_888_888_888_888);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `8.88888888888889`
|
||||||
|
|
||||||
|
error: float has excessive precision
|
||||||
|
--> $DIR/excessive_precision.rs:35:24
|
||||||
|
|
|
||||||
|
35 | let bad32_1: f32 = 1.123_456_789_f32;
|
||||||
|
| ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.1234568`
|
||||||
|
|
||||||
|
error: float has excessive precision
|
||||||
|
--> $DIR/excessive_precision.rs:36:24
|
||||||
|
|
|
||||||
|
36 | let bad32_2: f32 = 1.123_456_789;
|
||||||
|
| ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.1234568`
|
||||||
|
|
||||||
|
error: float has excessive precision
|
||||||
|
--> $DIR/excessive_precision.rs:38:24
|
||||||
|
|
|
||||||
|
38 | let bad64_1: f64 = 0.123_456_789_012_345_67f64;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.12345678901234566`
|
||||||
|
|
||||||
|
error: float has excessive precision
|
||||||
|
--> $DIR/excessive_precision.rs:39:24
|
||||||
|
|
|
||||||
|
39 | let bad64_2: f64 = 0.123_456_789_012_345_67;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.12345678901234566`
|
||||||
|
|
||||||
|
error: float has excessive precision
|
||||||
|
--> $DIR/excessive_precision.rs:40:19
|
||||||
|
|
|
||||||
|
40 | let bad64_3 = 0.123_456_789_012_345_67;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.12345678901234566`
|
||||||
|
|
||||||
|
error: float has excessive precision
|
||||||
|
--> $DIR/excessive_precision.rs:43:32
|
||||||
|
|
|
||||||
|
43 | let vec32: Vec<f32> = vec![0.123_456_789];
|
||||||
|
| ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.12345679`
|
||||||
|
|
||||||
|
error: float has excessive precision
|
||||||
|
--> $DIR/excessive_precision.rs:44:32
|
||||||
|
|
|
||||||
|
44 | let vec64: Vec<f64> = vec![0.123_456_789_123_456_789];
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.12345678912345678`
|
||||||
|
|
||||||
|
error: float has excessive precision
|
||||||
|
--> $DIR/excessive_precision.rs:48:24
|
||||||
|
|
|
||||||
|
48 | let bad_e32: f32 = 1.123_456_788_888e-10;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.1234568e-10`
|
||||||
|
|
||||||
|
error: float has excessive precision
|
||||||
|
--> $DIR/excessive_precision.rs:51:27
|
||||||
|
|
|
||||||
|
51 | let bad_bige32: f32 = 1.123_456_788_888E-10;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.1234568E-10`
|
||||||
|
|
||||||
|
error: aborting due to 16 previous errors
|
||||||
|
|
Loading…
Reference in a new issue