1 use clippy_utils
::diagnostics
::span_lint
;
2 use rustc_ast
::ast
::{FloatTy, LitFloatType, LitKind}
;
3 use rustc_hir
::{Expr, ExprKind}
;
4 use rustc_lint
::{LateContext, LateLintPass}
;
5 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
6 use rustc_span
::symbol
;
7 use std
::f64::consts
as f64;
11 /// Checks for floating point literals that approximate
12 /// constants which are defined in
13 /// [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)
15 /// [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),
16 /// respectively, suggesting to use the predefined constant.
18 /// ### Why is this bad?
19 /// Usually, the definition in the standard library is more
20 /// precise than what people come up with. If you find that your definition is
21 /// actually more precise, please [file a Rust
22 /// issue](https://github.com/rust-lang/rust/issues).
27 /// let y = 1_f64 / x;
29 /// Use predefined constants instead:
31 /// let x = std::f32::consts::PI;
32 /// let y = std::f64::consts::FRAC_1_PI;
36 "the approximate of a known float constant (in `std::fXX::consts`)"
39 // Tuples are of the form (constant, name, min_digits)
40 const KNOWN_CONSTS
: [(f64, &str, usize); 18] = [
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_10
, "LN_10", 5),
52 (f64::LN_2
, "LN_2", 5),
53 (f64::LOG10_E
, "LOG10_E", 5),
54 (f64::LOG2_E
, "LOG2_E", 5),
55 (f64::LOG2_10
, "LOG2_10", 5),
56 (f64::LOG10_2
, "LOG10_2", 5),
58 (f64::SQRT_2
, "SQRT_2", 5),
61 declare_lint_pass
!(ApproxConstant
=> [APPROX_CONSTANT
]);
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
);
71 fn check_lit(cx
: &LateContext
<'_
>, lit
: &LitKind
, e
: &Expr
<'_
>) {
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"),
77 LitKind
::Float(s
, LitFloatType
::Unsuffixed
) => check_known_consts(cx
, e
, s
, "f{32, 64}"),
82 fn check_known_consts(cx
: &LateContext
<'_
>, e
: &Expr
<'_
>, s
: symbol
::Symbol
, module
: &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
) {
92 "approximate value of `{}::consts::{}` found. \
93 Consider using it directly",
103 /// Returns `false` if the number of significant figures in `value` are
104 /// less than `min_digits`; otherwise, returns true if `value` is equal
105 /// to `constant`, rounded to the number of digits present in `value`.
107 fn is_approx_const(constant
: f64, value
: &str, min_digits
: usize) -> bool
{
108 if value
.len() <= min_digits
{
110 } else if constant
.to_string().starts_with(value
) {
111 // The value is a truncated constant
114 let round_const
= format
!("{:.*}", value
.len() - 2, constant
);