|
| 1 | +use clippy_config::Conf; |
1 | 2 | use clippy_utils::diagnostics::span_lint_and_then;
|
2 |
| -use clippy_utils::numeric_literal; |
| 3 | +use clippy_utils::{ExprUseNode, expr_use_ctxt, numeric_literal}; |
3 | 4 | use rustc_ast::ast::{LitFloatType, LitKind};
|
4 | 5 | use rustc_errors::Applicability;
|
5 | 6 | use rustc_hir as hir;
|
6 | 7 | use rustc_lint::{LateContext, LateLintPass};
|
7 | 8 | use rustc_middle::ty::{self, FloatTy};
|
8 |
| -use rustc_session::declare_lint_pass; |
| 9 | +use rustc_session::impl_lint_pass; |
9 | 10 | use std::fmt;
|
10 | 11 |
|
11 | 12 | declare_clippy_lint! {
|
12 | 13 | /// ### What it does
|
13 | 14 | /// Checks for float literals with a precision greater
|
14 | 15 | /// than that supported by the underlying type.
|
15 | 16 | ///
|
| 17 | + /// The lint is suppressed for literals with over `const_literal_digits_threshold` digits. |
| 18 | + /// |
16 | 19 | /// ### Why is this bad?
|
17 | 20 | /// Rust will truncate the literal silently.
|
18 | 21 | ///
|
@@ -58,7 +61,21 @@ declare_clippy_lint! {
|
58 | 61 | "lossy whole number float literals"
|
59 | 62 | }
|
60 | 63 |
|
61 |
| -declare_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]); |
| 64 | +pub struct FloatLiteral { |
| 65 | + const_literal_digits_threshold: usize, |
| 66 | +} |
| 67 | + |
| 68 | +impl_lint_pass!(FloatLiteral => [ |
| 69 | + EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL |
| 70 | +]); |
| 71 | + |
| 72 | +impl FloatLiteral { |
| 73 | + pub fn new(conf: &'static Conf) -> Self { |
| 74 | + Self { |
| 75 | + const_literal_digits_threshold: conf.const_literal_digits_threshold, |
| 76 | + } |
| 77 | + } |
| 78 | +} |
62 | 79 |
|
63 | 80 | impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
|
64 | 81 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
@@ -126,13 +143,25 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
|
126 | 143 | },
|
127 | 144 | );
|
128 | 145 | }
|
129 |
| - } else if digits > max as usize && count_digits(&float_str) < count_digits(sym_str) { |
| 146 | + } else if digits > max as usize && count_digits(&float_str) < digits { |
| 147 | + if digits >= self.const_literal_digits_threshold |
| 148 | + && matches!(expr_use_ctxt(cx, expr).use_node(cx), ExprUseNode::ConstStatic(_)) |
| 149 | + { |
| 150 | + // If a big enough number of digits is specified and it's a constant |
| 151 | + // we assume the user is definining a constant, and excessive precision is ok |
| 152 | + return; |
| 153 | + } |
130 | 154 | span_lint_and_then(
|
131 | 155 | cx,
|
132 | 156 | EXCESSIVE_PRECISION,
|
133 | 157 | expr.span,
|
134 | 158 | "float has excessive precision",
|
135 | 159 | |diag| {
|
| 160 | + if digits >= self.const_literal_digits_threshold |
| 161 | + && let Some(let_stmt) = maybe_let_stmt(cx, expr) |
| 162 | + { |
| 163 | + diag.span_note(let_stmt.span, "consider making it a `const` item"); |
| 164 | + } |
136 | 165 | diag.span_suggestion_verbose(
|
137 | 166 | expr.span,
|
138 | 167 | "consider changing the type or truncating it to",
|
@@ -196,3 +225,11 @@ impl FloatFormat {
|
196 | 225 | }
|
197 | 226 | }
|
198 | 227 | }
|
| 228 | + |
| 229 | +fn maybe_let_stmt<'a>(cx: &LateContext<'a>, expr: &hir::Expr<'_>) -> Option<&'a hir::LetStmt<'a>> { |
| 230 | + let parent = cx.tcx.parent_hir_node(expr.hir_id); |
| 231 | + match parent { |
| 232 | + hir::Node::LetStmt(let_stmt) => Some(let_stmt), |
| 233 | + _ => None, |
| 234 | + } |
| 235 | +} |
0 commit comments