diff --git a/CHANGELOG.md b/CHANGELOG.md index c1032204a22..5eed9664088 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1909,6 +1909,7 @@ Released 2018-09-13 [`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call [`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation [`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const +[`default_numeric_fallback`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_numeric_fallback [`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access [`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr [`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs new file mode 100644 index 00000000000..f049e64d0fb --- /dev/null +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -0,0 +1,388 @@ +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