mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-23 13:13:34 +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
|
||||
#![allow(stable_features)]
|
||||
#![feature(i128, i128_type)]
|
||||
#![feature(iterator_find_map)]
|
||||
|
||||
|
||||
#[macro_use]
|
||||
extern crate rustc;
|
||||
|
@ -124,6 +126,7 @@ pub mod erasing_op;
|
|||
pub mod escape;
|
||||
pub mod eta_reduction;
|
||||
pub mod eval_order_dependence;
|
||||
pub mod excessive_precision;
|
||||
pub mod explicit_write;
|
||||
pub mod fallible_impl_from;
|
||||
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_late_lint_pass(box enum_glob_use::EnumGlobUse);
|
||||
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 ptr::PointerPass);
|
||||
reg.register_late_lint_pass(box needless_bool::NeedlessBool);
|
||||
|
|
|
@ -42,7 +42,7 @@ fn main() {
|
|||
let my_ln_2 = 0.6931471805599453;
|
||||
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 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
|
||||
--> $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
|
||||
--> $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