about summary refs log tree commit diff
diff options
context:
space:
mode:
authorYoshitomo Nakanishi <yurayura.rounin.3@gmail.com>2021-02-15 14:22:31 +0900
committerYoshitomo Nakanishi <yurayura.rounin.3@gmail.com>2021-02-15 14:43:37 +0900
commit93796b2346a1177809628ea141f0dd958f4c4d29 (patch)
tree72b37ff74f6bed72535ec00afb33a0232dc80e8a
parent22d4483dee55fbf0c9ba48a3ddfd8abe06805c07 (diff)
downloadrust-93796b2346a1177809628ea141f0dd958f4c4d29.tar.gz
rust-93796b2346a1177809628ea141f0dd958f4c4d29.zip
Add some restrictions to default_numeric_fallback to avoid FNs
-rw-r--r--clippy_lints/src/default_numeric_fallback.rs171
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--tests/ui/default_numeric_fallback.rs40
-rw-r--r--tests/ui/default_numeric_fallback.stderr50
4 files changed, 218 insertions, 45 deletions
diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs
index b3b5a4a8251..3102d032057 100644
--- a/clippy_lints/src/default_numeric_fallback.rs
+++ b/clippy_lints/src/default_numeric_fallback.rs
@@ -1,9 +1,14 @@
-use rustc_ast::{LitFloatType, LitIntType, LitKind};
-use rustc_data_structures::fx::FxHashSet;
-use rustc_hir::{Expr, ExprKind, HirId, Stmt, StmtKind};
+use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
+use rustc_hir::{
+    intravisit::{walk_expr, walk_stmt, NestedVisitorMap, Visitor},
+    Body, Expr, ExprKind, Lit, Stmt, StmtKind,
+};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{self, FloatTy, IntTy};
-use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_middle::{
+    hir::map::Map,
+    ty::{self, FloatTy, IntTy, Ty},
+};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
 
 use if_chain::if_chain;
 
@@ -33,53 +38,141 @@ declare_clippy_lint! {
     /// Use instead:
     /// ```rust
     /// let i = 10i32;
-    /// let f: f64 = 1.23;
+    /// let f = 1.23f64;
     /// ```
     pub DEFAULT_NUMERIC_FALLBACK,
     restriction,
     "usage of unconstrained numeric literals which may cause default numeric fallback."
 }
 
-#[derive(Default)]
-pub struct DefaultNumericFallback {
-    /// Hold `init` in `Local` if `Local` has a type annotation.
-    bounded_inits: FxHashSet<HirId>,
+declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]);
+
+impl LateLintPass<'_> for DefaultNumericFallback {
+    fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
+        let mut visitor = NumericFallbackVisitor::new(cx);
+        visitor.visit_body(body);
+    }
 }
 
-impl_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]);
+struct NumericFallbackVisitor<'a, 'tcx> {
+    /// Stack manages type bound of exprs. The top element holds current expr type.
+    ty_bounds: Vec<TyBound<'tcx>>,
 
-impl LateLintPass<'_> for DefaultNumericFallback {
-    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);
-            }
+    cx: &'a LateContext<'tcx>,
+}
+
+impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
+    fn new(cx: &'a LateContext<'tcx>) -> Self {
+        Self {
+            ty_bounds: vec![TyBound::Nothing],
+            cx,
         }
     }
 
-    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;
+    /// Check whether a passed literal has potential to cause fallback or not.
+    fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>) {
+        let ty_bound = self.ty_bounds.last().unwrap();
         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",
-                 )
-            }
+                if matches!(lit.node,
+                            LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));
+                if matches!(lit_ty.kind(), ty::Int(IntTy::I32) | ty::Float(FloatTy::F64));
+                if !ty_bound.is_integral();
+                then {
+                    span_lint_and_help(
+                        self.cx,
+                        DEFAULT_NUMERIC_FALLBACK,
+                        lit.span,
+                        "default numeric fallback might occur",
+                        None,
+                        "consider adding suffix to avoid default numeric fallback",
+                    );
+                }
+        }
+    }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
+    type Map = Map<'tcx>;
+
+    #[allow(clippy::too_many_lines)]
+    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+        match &expr.kind {
+            ExprKind::Call(func, args) => {
+                if_chain! {
+                    if let ExprKind::Path(ref func_path) = func.kind;
+                    if let Some(def_id) = self.cx.qpath_res(func_path, func.hir_id).opt_def_id();
+                    then {
+                        let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder();
+                        for (expr, bound) in args.iter().zip(fn_sig.inputs().iter()) {
+                            // Push found arg type, then visit arg.
+                            self.ty_bounds.push(TyBound::Ty(bound));
+                            self.visit_expr(expr);
+                            self.ty_bounds.pop();
+                        }
+                        return;
+                    }
+                }
+            },
+
+            ExprKind::MethodCall(_, _, args, _) => {
+                if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) {
+                    let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder();
+                    for (expr, bound) in args.iter().zip(fn_sig.inputs().iter()) {
+                        self.ty_bounds.push(TyBound::Ty(bound));
+                        self.visit_expr(expr);
+                        self.ty_bounds.pop();
+                    }
+                    return;
+                }
+            },
+
+            ExprKind::Lit(lit) => {
+                let ty = self.cx.typeck_results().expr_ty(expr);
+                self.check_lit(lit, ty);
+                return;
+            },
+
+            _ => {},
+        }
+
+        walk_expr(self, expr);
+    }
+
+    fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
+        match stmt.kind {
+            StmtKind::Local(local) => {
+                if local.ty.is_some() {
+                    self.ty_bounds.push(TyBound::Any)
+                } else {
+                    self.ty_bounds.push(TyBound::Nothing)
+                }
+            },
+
+            _ => self.ty_bounds.push(TyBound::Nothing),
+        }
+
+        walk_stmt(self, stmt);
+        self.ty_bounds.pop();
+    }
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+}
+
+#[derive(Debug, Clone, Copy)]
+enum TyBound<'ctx> {
+    Any,
+    Ty(Ty<'ctx>),
+    Nothing,
+}
+
+impl<'ctx> TyBound<'ctx> {
+    fn is_integral(self) -> bool {
+        match self {
+            TyBound::Any => true,
+            TyBound::Ty(t) => t.is_integral(),
+            TyBound::Nothing => false,
         }
     }
 }
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 6590613b93a..642c1e68dac 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -1028,7 +1028,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box strings::StringAdd);
     store.register_late_pass(|| box implicit_return::ImplicitReturn);
     store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub);
-    store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback::default());
+    store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback);
 
     let msrv = conf.msrv.as_ref().and_then(|s| {
         parse_msrv(s, None, None).or_else(|| {
diff --git a/tests/ui/default_numeric_fallback.rs b/tests/ui/default_numeric_fallback.rs
index 5ca5bc1e907..420c1f0c931 100644
--- a/tests/ui/default_numeric_fallback.rs
+++ b/tests/ui/default_numeric_fallback.rs
@@ -1,12 +1,46 @@
 #![warn(clippy::default_numeric_fallback)]
 #![allow(unused)]
+#![allow(clippy::never_loop)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::unnecessary_operation)]
+
+fn concrete_arg(x: i32) {}
+
+fn generic_arg<T>(t: T) {}
+
+struct ConcreteStruct {
+    x: i32,
+}
+
+struct StructForMethodCallTest {
+    x: i32,
+}
+
+impl StructForMethodCallTest {
+    fn concrete_arg(&self, x: i32) {}
+
+    fn generic_arg<T>(&self, t: T) {}
+}
 
 fn main() {
+    let s = StructForMethodCallTest { x: 10_i32 };
+
     // Bad.
     let x = 1;
     let x = 0.1;
+
     let x = if true { 1 } else { 2 };
 
+    let x: _ = {
+        let y = 1;
+        1
+    };
+
+    generic_arg(10);
+    s.generic_arg(10);
+    let x: _ = generic_arg(10);
+    let x: _ = s.generic_arg(10);
+
     // Good.
     let x = 1_i32;
     let x: i32 = 1;
@@ -14,5 +48,11 @@ fn main() {
     let x = 0.1_f64;
     let x: f64 = 0.1;
     let x: _ = 0.1;
+
     let x: _ = if true { 1 } else { 2 };
+
+    concrete_arg(10);
+    s.concrete_arg(10);
+    let x = concrete_arg(10);
+    let x = s.concrete_arg(10);
 }
diff --git a/tests/ui/default_numeric_fallback.stderr b/tests/ui/default_numeric_fallback.stderr
index c36409a052c..cb7c174ad8d 100644
--- a/tests/ui/default_numeric_fallback.stderr
+++ b/tests/ui/default_numeric_fallback.stderr
@@ -1,5 +1,5 @@
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:6:13
+  --> $DIR/default_numeric_fallback.rs:29:13
    |
 LL |     let x = 1;
    |             ^
@@ -8,7 +8,7 @@ LL |     let x = 1;
    = help: consider adding suffix to avoid default numeric fallback
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:7:13
+  --> $DIR/default_numeric_fallback.rs:30:13
    |
 LL |     let x = 0.1;
    |             ^^^
@@ -16,7 +16,7 @@ LL |     let x = 0.1;
    = help: consider adding suffix to avoid default numeric fallback
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:8:23
+  --> $DIR/default_numeric_fallback.rs:32:23
    |
 LL |     let x = if true { 1 } else { 2 };
    |                       ^
@@ -24,12 +24,52 @@ LL |     let x = if true { 1 } else { 2 };
    = help: consider adding suffix to avoid default numeric fallback
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:8:34
+  --> $DIR/default_numeric_fallback.rs:32:34
    |
 LL |     let x = if true { 1 } else { 2 };
    |                                  ^
    |
    = help: consider adding suffix to avoid default numeric fallback
 
-error: aborting due to 4 previous errors
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:35:17
+   |
+LL |         let y = 1;
+   |                 ^
+   |
+   = help: consider adding suffix to avoid default numeric fallback
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:39:17
+   |
+LL |     generic_arg(10);
+   |                 ^^
+   |
+   = help: consider adding suffix to avoid default numeric fallback
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:40:19
+   |
+LL |     s.generic_arg(10);
+   |                   ^^
+   |
+   = help: consider adding suffix to avoid default numeric fallback
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:41:28
+   |
+LL |     let x: _ = generic_arg(10);
+   |                            ^^
+   |
+   = help: consider adding suffix to avoid default numeric fallback
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:42:30
+   |
+LL |     let x: _ = s.generic_arg(10);
+   |                              ^^
+   |
+   = help: consider adding suffix to avoid default numeric fallback
+
+error: aborting due to 9 previous errors