|
1 | 1 | use clippy_utils::diagnostics::span_lint; |
| 2 | +use clippy_utils::{meets_msrv, msrvs}; |
2 | 3 | use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; |
3 | 4 | use rustc_hir::{Expr, ExprKind}; |
4 | | -use rustc_lint::{LateContext, LateLintPass}; |
5 | | -use rustc_session::{declare_lint_pass, declare_tool_lint}; |
| 5 | +use rustc_lint::{LateContext, LateLintPass, LintContext}; |
| 6 | +use rustc_semver::RustcVersion; |
| 7 | +use rustc_session::{declare_tool_lint, impl_lint_pass}; |
6 | 8 | use rustc_span::symbol; |
7 | 9 | use std::f64::consts as f64; |
8 | 10 |
|
@@ -36,68 +38,83 @@ declare_clippy_lint! { |
36 | 38 | "the approximate of a known float constant (in `std::fXX::consts`)" |
37 | 39 | } |
38 | 40 |
|
39 | | -// Tuples are of the form (constant, name, min_digits) |
40 | | -const KNOWN_CONSTS: [(f64, &str, usize); 18] = [ |
41 | | - (f64::E, "E", 4), |
42 | | - (f64::FRAC_1_PI, "FRAC_1_PI", 4), |
43 | | - (f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5), |
44 | | - (f64::FRAC_2_PI, "FRAC_2_PI", 5), |
45 | | - (f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5), |
46 | | - (f64::FRAC_PI_2, "FRAC_PI_2", 5), |
47 | | - (f64::FRAC_PI_3, "FRAC_PI_3", 5), |
48 | | - (f64::FRAC_PI_4, "FRAC_PI_4", 5), |
49 | | - (f64::FRAC_PI_6, "FRAC_PI_6", 5), |
50 | | - (f64::FRAC_PI_8, "FRAC_PI_8", 5), |
51 | | - (f64::LN_2, "LN_2", 5), |
52 | | - (f64::LN_10, "LN_10", 5), |
53 | | - (f64::LOG2_10, "LOG2_10", 5), |
54 | | - (f64::LOG2_E, "LOG2_E", 5), |
55 | | - (f64::LOG10_2, "LOG10_2", 5), |
56 | | - (f64::LOG10_E, "LOG10_E", 5), |
57 | | - (f64::PI, "PI", 3), |
58 | | - (f64::SQRT_2, "SQRT_2", 5), |
| 41 | +// Tuples are of the form (constant, name, min_digits, msrv) |
| 42 | +const KNOWN_CONSTS: [(f64, &str, usize, Option<RustcVersion>); 18] = [ |
| 43 | + (f64::E, "E", 4, None), |
| 44 | + (f64::FRAC_1_PI, "FRAC_1_PI", 4, None), |
| 45 | + (f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5, None), |
| 46 | + (f64::FRAC_2_PI, "FRAC_2_PI", 5, None), |
| 47 | + (f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5, None), |
| 48 | + (f64::FRAC_PI_2, "FRAC_PI_2", 5, None), |
| 49 | + (f64::FRAC_PI_3, "FRAC_PI_3", 5, None), |
| 50 | + (f64::FRAC_PI_4, "FRAC_PI_4", 5, None), |
| 51 | + (f64::FRAC_PI_6, "FRAC_PI_6", 5, None), |
| 52 | + (f64::FRAC_PI_8, "FRAC_PI_8", 5, None), |
| 53 | + (f64::LN_2, "LN_2", 5, None), |
| 54 | + (f64::LN_10, "LN_10", 5, None), |
| 55 | + (f64::LOG2_10, "LOG2_10", 5, Some(msrvs::LOG2_10)), |
| 56 | + (f64::LOG2_E, "LOG2_E", 5, None), |
| 57 | + (f64::LOG10_2, "LOG10_2", 5, Some(msrvs::LOG10_2)), |
| 58 | + (f64::LOG10_E, "LOG10_E", 5, None), |
| 59 | + (f64::PI, "PI", 3, None), |
| 60 | + (f64::SQRT_2, "SQRT_2", 5, None), |
59 | 61 | ]; |
60 | 62 |
|
61 | | -declare_lint_pass!(ApproxConstant => [APPROX_CONSTANT]); |
| 63 | +pub struct ApproxConstant { |
| 64 | + msrv: Option<RustcVersion>, |
| 65 | +} |
62 | 66 |
|
63 | | -impl<'tcx> LateLintPass<'tcx> for ApproxConstant { |
64 | | - fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { |
65 | | - if let ExprKind::Lit(lit) = &e.kind { |
66 | | - check_lit(cx, &lit.node, e); |
| 67 | +impl ApproxConstant { |
| 68 | + #[must_use] |
| 69 | + pub fn new(msrv: Option<RustcVersion>) -> Self { |
| 70 | + Self { msrv } |
| 71 | + } |
| 72 | + |
| 73 | + fn check_lit(&self, cx: &LateContext<'_>, lit: &LitKind, e: &Expr<'_>) { |
| 74 | + match *lit { |
| 75 | + LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty { |
| 76 | + FloatTy::F32 => self.check_known_consts(cx, e, s, "f32"), |
| 77 | + FloatTy::F64 => self.check_known_consts(cx, e, s, "f64"), |
| 78 | + }, |
| 79 | + LitKind::Float(s, LitFloatType::Unsuffixed) => self.check_known_consts(cx, e, s, "f{32, 64}"), |
| 80 | + _ => (), |
67 | 81 | } |
68 | 82 | } |
69 | | -} |
70 | 83 |
|
71 | | -fn check_lit(cx: &LateContext<'_>, lit: &LitKind, e: &Expr<'_>) { |
72 | | - match *lit { |
73 | | - LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty { |
74 | | - FloatTy::F32 => check_known_consts(cx, e, s, "f32"), |
75 | | - FloatTy::F64 => check_known_consts(cx, e, s, "f64"), |
76 | | - }, |
77 | | - LitKind::Float(s, LitFloatType::Unsuffixed) => check_known_consts(cx, e, s, "f{32, 64}"), |
78 | | - _ => (), |
| 84 | + fn check_known_consts(&self, cx: &LateContext<'_>, e: &Expr<'_>, s: symbol::Symbol, module: &str) { |
| 85 | + let s = s.as_str(); |
| 86 | + if s.parse::<f64>().is_ok() { |
| 87 | + for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS { |
| 88 | + if is_approx_const(constant, &s, min_digits) |
| 89 | + && msrv.as_ref().map_or(true, |msrv| meets_msrv(self.msrv.as_ref(), msrv)) |
| 90 | + { |
| 91 | + span_lint( |
| 92 | + cx, |
| 93 | + APPROX_CONSTANT, |
| 94 | + e.span, |
| 95 | + &format!( |
| 96 | + "approximate value of `{}::consts::{}` found. \ |
| 97 | + Consider using it directly", |
| 98 | + module, &name |
| 99 | + ), |
| 100 | + ); |
| 101 | + return; |
| 102 | + } |
| 103 | + } |
| 104 | + } |
79 | 105 | } |
80 | 106 | } |
81 | 107 |
|
82 | | -fn check_known_consts(cx: &LateContext<'_>, e: &Expr<'_>, s: symbol::Symbol, module: &str) { |
83 | | - let s = s.as_str(); |
84 | | - if s.parse::<f64>().is_ok() { |
85 | | - for &(constant, name, min_digits) in &KNOWN_CONSTS { |
86 | | - if is_approx_const(constant, &s, min_digits) { |
87 | | - span_lint( |
88 | | - cx, |
89 | | - APPROX_CONSTANT, |
90 | | - e.span, |
91 | | - &format!( |
92 | | - "approximate value of `{}::consts::{}` found. \ |
93 | | - Consider using it directly", |
94 | | - module, &name |
95 | | - ), |
96 | | - ); |
97 | | - return; |
98 | | - } |
| 108 | +impl_lint_pass!(ApproxConstant => [APPROX_CONSTANT]); |
| 109 | + |
| 110 | +impl<'tcx> LateLintPass<'tcx> for ApproxConstant { |
| 111 | + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { |
| 112 | + if let ExprKind::Lit(lit) = &e.kind { |
| 113 | + self.check_lit(cx, &lit.node, e); |
99 | 114 | } |
100 | 115 | } |
| 116 | + |
| 117 | + extract_msrv_attr!(LateContext); |
101 | 118 | } |
102 | 119 |
|
103 | 120 | /// Returns `false` if the number of significant figures in `value` are |
|
0 commit comments