use rustc_ast::ast::{Label, LitFloatType, LitIntType, LitKind}; use rustc_hir::{ self as hir, intravisit::{walk_expr, walk_stmt, walk_ty, FnKind, NestedVisitorMap, Visitor}, Body, Expr, ExprKind, FnDecl, FnRetTy, Guard, HirId, Lit, Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::{ hir::map::Map, ty::{self, subst::GenericArgKind, FloatTy, IntTy, Ty, TyCtxt}, }; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; use rustc_typeck::hir_ty_to_ty; 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; /// let f = 1.23f64; /// ``` pub DEFAULT_NUMERIC_FALLBACK, restriction, "usage of unconstrained numeric literals which may cause default numeric fallback." } declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]); fn enclosing_body_owner_opt(tcx: TyCtxt<'_>, hir_id: HirId) -> Option { let hir_map = tcx.hir(); for (parent, _) in hir_map.parent_iter(hir_id) { if let Some(body) = hir_map.maybe_body_owned_by(parent) { return Some(hir_map.body_owner(body)); } } None } impl LateLintPass<'_> for DefaultNumericFallback { fn check_fn( &mut self, cx: &LateContext<'tcx>, _: FnKind<'tcx>, fn_decl: &'tcx FnDecl<'_>, body: &'tcx Body<'_>, _: Span, hir_id: HirId, ) { let ret_ty_bound = match fn_decl.output { FnRetTy::DefaultReturn(_) => None, FnRetTy::Return(ty) => Some(ty), } .and_then(|ty| { let mut infer_ty_finder = InferTyFinder::new(); infer_ty_finder.visit_ty(ty); if infer_ty_finder.found { None } else if enclosing_body_owner_opt(cx.tcx, hir_id).is_some() { cx.typeck_results().node_type_opt(ty.hir_id) } else { Some(hir_ty_to_ty(cx.tcx, ty)) } }); let mut visitor = NumericFallbackVisitor::new(ret_ty_bound, cx); visitor.visit_body(body); } } struct NumericFallbackVisitor<'a, 'tcx> { /// Stack manages type bound of exprs. The top element holds current expr type. ty_bounds: Vec>>, /// Ret type bound. ret_ty_bound: Option>, /// Break type bounds. break_ty_bounds: Vec<(Option