rust-clippy/clippy_lints/src/default_numeric_fallback.rs

86 lines
3 KiB
Rust
Raw Normal View History

2021-02-07 09:00:08 +00:00
use rustc_ast::{LitFloatType, LitIntType, LitKind};
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::{Expr, ExprKind, HirId, Stmt, StmtKind};
2021-02-02 03:26:20 +00:00
use rustc_lint::{LateContext, LateLintPass};
2021-02-07 09:00:08 +00:00
use rustc_middle::ty::{self, FloatTy, IntTy};
use rustc_session::{declare_tool_lint, impl_lint_pass};
2021-02-02 03:26:20 +00:00
use if_chain::if_chain;
use crate::utils::span_lint_and_help;
declare_clippy_lint! {
/// **What it does:** Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type
/// inference.
///
/// Default numeric fallback means that if numeric types have not yet been bound to concrete
/// types at the end of type inference, then integer type is bound to `i32`, and similarly
/// floating type is bound to `f64`.
///
/// See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback.
///
/// **Why is this bad?** For those who are very careful about types, default numeric fallback
/// can be a pitfall that cause unexpected runtime behavior.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// let i = 10;
/// let f = 1.23;
/// ```
///
/// Use instead:
/// ```rust
/// let i = 10i32;
2021-02-07 09:00:08 +00:00
/// let f: f64 = 1.23;
2021-02-02 03:26:20 +00:00
/// ```
pub DEFAULT_NUMERIC_FALLBACK,
restriction,
"usage of unconstrained numeric literals which may cause default numeric fallback."
}
2021-02-07 09:00:08 +00:00
#[derive(Default)]
pub struct DefaultNumericFallback {
/// Hold `init` in `Local` if `Local` has a type annotation.
bounded_inits: FxHashSet<HirId>,
2021-02-02 03:26:20 +00:00
}
2021-02-07 09:00:08 +00:00
impl_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]);
2021-02-02 03:26:20 +00:00
impl LateLintPass<'_> for DefaultNumericFallback {
2021-02-07 09:00:08 +00:00
fn check_stmt(&mut self, _: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
if_chain! {
if let StmtKind::Local(local) = stmt.kind;
if local.ty.is_some();
if let Some(init) = local.init;
then {
self.bounded_inits.insert(init.hir_id);
2021-02-02 03:26:20 +00:00
}
}
}
2021-02-07 09:00:08 +00:00
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let expr_ty = cx.typeck_results().expr_ty(expr);
let hir_id = expr.hir_id;
if_chain! {
if let ExprKind::Lit(ref lit) = expr.kind;
if matches!(lit.node,
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));
if matches!(expr_ty.kind(), ty::Int(IntTy::I32) | ty::Float(FloatTy::F64));
if !self.bounded_inits.contains(&hir_id);
if !cx.tcx.hir().parent_iter(hir_id).any(|(ref hir_id, _)| self.bounded_inits.contains(hir_id));
then {
span_lint_and_help(
cx,
DEFAULT_NUMERIC_FALLBACK,
lit.span,
"default numeric fallback might occur",
None,
"consider adding suffix to avoid default numeric fallback",
)
}
2021-02-02 03:26:20 +00:00
}
}
}