3 use rustc::ty::TypeVariants;
8 use syntax_pos::symbol::Symbol;
9 use utils::span_lint_and_sugg;
11 /// **What it does:** Checks for float literals with a precision greater
12 /// than that supported by the underlying type
14 /// **Why is this bad?** Rust will truncate the literal silently.
16 /// **Known problems:** None.
22 /// Insert a short example of code that triggers the lint
23 /// let v: f32 = 0.123_456_789_9;
24 /// println!("{}", v); // 0.123_456_789
27 /// Insert a short example of improved code that doesn't trigger the lint
28 /// let v: f64 = 0.123_456_789_9;
29 /// println!("{}", v); // 0.123_456_789_9
31 declare_clippy_lint! {
32 pub EXCESSIVE_PRECISION,
34 "excessive precision for float literal"
37 pub struct ExcessivePrecision;
39 impl LintPass for ExcessivePrecision {
40 fn get_lints(&self) -> LintArray {
41 lint_array!(EXCESSIVE_PRECISION)
45 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExcessivePrecision {
46 fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
48 let ty = cx.tables.expr_ty(expr);
49 if let TypeVariants::TyFloat(ref fty) = ty.sty;
50 if let hir::ExprLit(ref lit) = expr.node;
51 if let LitKind::Float(ref sym, _) | LitKind::FloatUnsuffixed(ref sym) = lit.node;
52 if let Some(sugg) = self.check(sym, fty);
58 "float has excessive precision",
59 "consider changing the type or truncating it to",
67 impl ExcessivePrecision {
68 // None if nothing to lint, Some(suggestion) if lint neccessary
69 fn check(&self, sym: &Symbol, fty: &FloatTy) -> Option<String> {
70 let max = max_digits(fty);
71 let sym_str = sym.as_str();
72 let formatter = FloatFormat::new(&sym_str);
73 let digits = count_digits(&sym_str);
74 // Try to bail out if the float is for sure fine.
75 // If its within the 2 decimal digits of being out of precision we
76 // check if the parsed representation is the same as the string
77 // since we'll need the truncated string anyway.
78 if digits > max as usize {
80 FloatTy::F32 => sym_str.parse::<f32>().map(|f| formatter.format(f)),
81 FloatTy::F64 => sym_str.parse::<f64>().map(|f| formatter.format(f)),
83 // We know this will parse since we are in LatePass
97 fn max_digits(fty: &FloatTy) -> u32 {
99 FloatTy::F32 => f32::DIGITS,
100 FloatTy::F64 => f64::DIGITS,
104 fn count_digits(s: &str) -> usize {
106 .filter(|c| *c != '-' || *c != '.')
107 .take_while(|c| *c != 'e' || *c != 'E')
108 .fold(0, |count, c| {
110 if c == '0' && count == 0 {
124 fn new(s: &str) -> Self {
126 .find_map(|x| match x {
127 'e' => Some(FloatFormat::LowerExp),
128 'E' => Some(FloatFormat::UpperExp),
131 .unwrap_or(FloatFormat::Normal)
133 fn format<T>(&self, f: T) -> String
134 where T: fmt::UpperExp + fmt::LowerExp + fmt::Display {
136 FloatFormat::LowerExp => format!("{:e}", f),
137 FloatFormat::UpperExp => format!("{:E}", f),
138 FloatFormat::Normal => format!("{}", f),