3 use std::f64::consts as f64;
4 use syntax::ast::{Lit, LitKind, FloatTy};
7 /// **What it does:** This lint checks for floating point literals that approximate constants which are defined in [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants) or [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants), respectively, suggesting to use the predefined constant.
9 /// **Why is this bad?** Usually, the definition in the standard library is more precise than what people come up with. If you find that your definition is actually more precise, please [file a Rust issue](https://github.com/rust-lang/rust/issues).
11 /// **Known problems:** If you happen to have a value that is within 1/8192 of a known constant, but is not *and should not* be the same, this lint will report your value anyway. We have not yet noticed any false positives in code we tested clippy with (this includes servo), but YMMV.
20 "the approximate of a known float constant (in `std::f64::consts` or `std::f32::consts`) \
21 is found; suggests to use the constant"
24 // Tuples are of the form (constant, name, min_digits)
25 const KNOWN_CONSTS: &'static [(f64, &'static str, usize)] = &[(f64::E, "E", 4),
26 (f64::FRAC_1_PI, "FRAC_1_PI", 4),
27 (f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5),
28 (f64::FRAC_2_PI, "FRAC_2_PI", 5),
29 (f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5),
30 (f64::FRAC_PI_2, "FRAC_PI_2", 5),
31 (f64::FRAC_PI_3, "FRAC_PI_3", 5),
32 (f64::FRAC_PI_4, "FRAC_PI_4", 5),
33 (f64::FRAC_PI_6, "FRAC_PI_6", 5),
34 (f64::FRAC_PI_8, "FRAC_PI_8", 5),
35 (f64::LN_10, "LN_10", 5),
36 (f64::LN_2, "LN_2", 5),
37 (f64::LOG10_E, "LOG10_E", 5),
38 (f64::LOG2_E, "LOG2_E", 5),
40 (f64::SQRT_2, "SQRT_2", 5)];
45 impl LintPass for Pass {
46 fn get_lints(&self) -> LintArray {
47 lint_array!(APPROX_CONSTANT)
51 impl LateLintPass for Pass {
52 fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
53 if let ExprLit(ref lit) = e.node {
54 check_lit(cx, lit, e);
59 fn check_lit(cx: &LateContext, lit: &Lit, e: &Expr) {
61 LitKind::Float(ref s, FloatTy::F32) => check_known_consts(cx, e, s, "f32"),
62 LitKind::Float(ref s, FloatTy::F64) => check_known_consts(cx, e, s, "f64"),
63 LitKind::FloatUnsuffixed(ref s) => check_known_consts(cx, e, s, "f{32, 64}"),
68 fn check_known_consts(cx: &LateContext, e: &Expr, s: &str, module: &str) {
69 if let Ok(_) = s.parse::<f64>() {
70 for &(constant, name, min_digits) in KNOWN_CONSTS {
71 if is_approx_const(constant, s, min_digits) {
75 &format!("approximate value of `{}::consts::{}` found. Consider using it directly", module, &name));
82 /// Returns false if the number of significant figures in `value` are
83 /// less than `min_digits`; otherwise, returns true if `value` is equal
84 /// to `constant`, rounded to the number of digits present in `value`.
85 fn is_approx_const(constant: f64, value: &str, min_digits: usize) -> bool {
86 if value.len() <= min_digits {
89 let round_const = format!("{:.*}", value.len() - 2, constant);
91 let mut trunc_const = constant.to_string();
92 if trunc_const.len() > value.len() {
93 trunc_const.truncate(value.len());
96 (value == round_const) || (value == trunc_const)