]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/loops.rs
Auto merge of #4551 - mikerite:fix-ice-reporting, r=llogiq
[rust.git] / clippy_lints / src / loops.rs
index 61bb3e2f82383bbc83b3308858795ea487140ce3..2db8acc4b95e54e08623cac05367731f68608bb0 100644 (file)
@@ -11,7 +11,7 @@
 // use rustc::middle::region::CodeExtent;
 use crate::consts::{constant, Constant};
 use crate::utils::usage::mutated_variables;
-use crate::utils::{in_macro_or_desugar, sext, sugg};
+use crate::utils::{is_type_diagnostic_item, qpath_res, sext, sugg};
 use rustc::middle::expr_use_visitor::*;
 use rustc::middle::mem_categorization::cmt_;
 use rustc::middle::mem_categorization::Categorization;
 use std::mem;
 use syntax::ast;
 use syntax::source_map::Span;
-use syntax_pos::BytePos;
+use syntax_pos::{BytePos, Symbol};
 
 use crate::utils::paths;
 use crate::utils::{
-    get_enclosing_block, get_parent_expr, has_iter_method, higher, is_integer_literal, is_refutable, last_path_segment,
+    get_enclosing_block, get_parent_expr, has_iter_method, higher, is_integer_const, is_refutable, last_path_segment,
     match_trait_method, match_type, match_var, multispan_sugg, snippet, snippet_opt, snippet_with_applicability,
     span_help_and_lint, span_lint, span_lint_and_sugg, span_lint_and_then, SpanlessEq,
 };
     /// **Known problems:** None.
     ///
     /// **Example:**
-    /// ```ignore
+    /// ```rust
+    /// # let src = vec![1];
+    /// # let mut dst = vec![0; 65];
     /// for i in 0..src.len() {
     ///     dst[i + 64] = src[i];
     /// }
     /// ```
+    /// Could be written as:
+    /// ```rust
+    /// # let src = vec![1];
+    /// # let mut dst = vec![0; 65];
+    /// dst[64..(src.len() + 64)].clone_from_slice(&src[..]);
+    /// ```
     pub MANUAL_MEMCPY,
     perf,
     "manually copying items between slices"
     /// types.
     ///
     /// **Example:**
-    /// ```ignore
+    /// ```rust
     /// // with `y` a `Vec` or slice:
+    /// # let y = vec![1];
     /// for x in y.iter() {
-    ///     ..
+    ///     // ..
     /// }
     /// ```
     /// can be rewritten to
     /// ```rust
+    /// # let y = vec![1];
     /// for x in &y {
-    ///     ..
+    ///     // ..
     /// }
     /// ```
     pub EXPLICIT_ITER_LOOP,
     /// **Known problems:** None
     ///
     /// **Example:**
-    /// ```ignore
+    /// ```rust
+    /// # let y = vec![1];
     /// // with `y` a `Vec` or slice:
     /// for x in y.into_iter() {
-    ///     ..
+    ///     // ..
     /// }
     /// ```
     /// can be rewritten to
-    /// ```ignore
+    /// ```rust
+    /// # let y = vec![1];
     /// for x in y {
-    ///     ..
+    ///     // ..
     /// }
     /// ```
     pub EXPLICIT_INTO_ITER_LOOP,
     /// **Known problems:** Sometimes the wrong binding is displayed (#383).
     ///
     /// **Example:**
-    /// ```rust
+    /// ```rust,no_run
+    /// # let y = Some(1);
     /// loop {
     ///     let x = match y {
     ///         Some(x) => x,
     ///         None => break,
-    ///     }
+    ///     };
     ///     // .. do something with x
     /// }
     /// // is easier written as
     /// while let Some(x) = y {
     ///     // .. do something with x
-    /// }
+    /// };
     /// ```
     pub WHILE_LET_LOOP,
     complexity,
     "`loop { if let { ... } else break }`, which can be written as a `while let` loop"
 }
 
-declare_clippy_lint! {
-    /// **What it does:** Checks for using `collect()` on an iterator without using
-    /// the result.
-    ///
-    /// **Why is this bad?** It is more idiomatic to use a `for` loop over the
-    /// iterator instead.
-    ///
-    /// **Known problems:** None.
-    ///
-    /// **Example:**
-    /// ```ignore
-    /// vec.iter().map(|x| /* some operation returning () */).collect::<Vec<_>>();
-    /// ```
-    pub UNUSED_COLLECT,
-    perf,
-    "`collect()`ing an iterator without using the result; this is usually better written as a for loop"
-}
-
 declare_clippy_lint! {
     /// **What it does:** Checks for functions collecting an iterator when collect
     /// is not needed.
     /// None
     ///
     /// **Example:**
-    /// ```ignore
-    /// let len = iterator.collect::<Vec<_>>().len();
+    /// ```rust
+    /// # let iterator = vec![1].into_iter();
+    /// let len = iterator.clone().collect::<Vec<_>>().len();
     /// // should be
     /// let len = iterator.count();
     /// ```
     /// **What it does:** Checks `for` loops over slices with an explicit counter
     /// and suggests the use of `.enumerate()`.
     ///
-    /// **Why is it bad?** Not only is the version using `.enumerate()` more
-    /// readable, the compiler is able to remove bounds checks which can lead to
-    /// faster code in some instances.
+    /// **Why is it bad?** Using `.enumerate()` makes the intent more clear,
+    /// declutters the code and may be faster in some instances.
     ///
     /// **Known problems:** None.
     ///
     /// **Example:**
-    /// ```ignore
-    /// for i in 0..v.len() { foo(v[i]);
-    /// for i in 0..v.len() { bar(i, v[i]); }
+    /// ```rust
+    /// # let v = vec![1];
+    /// # fn bar(bar: usize, baz: usize) {}
+    /// let mut i = 0;
+    /// for item in &v {
+    ///     bar(i, *item);
+    ///     i += 1;
+    /// }
+    /// ```
+    /// Could be written as
+    /// ```rust
+    /// # let v = vec![1];
+    /// # fn bar(bar: usize, baz: usize) {}
+    /// for (i, item) in v.iter().enumerate() { bar(i, *item); }
     /// ```
     pub EXPLICIT_COUNTER_LOOP,
     complexity,
     FOR_LOOP_OVER_RESULT,
     FOR_LOOP_OVER_OPTION,
     WHILE_LET_LOOP,
-    UNUSED_COLLECT,
     NEEDLESS_COLLECT,
     REVERSE_RANGE_LOOP,
     EXPLICIT_COUNTER_LOOP,
@@ -472,7 +477,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Loops {
     #[allow(clippy::too_many_lines)]
     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
         // we don't want to check expanded macros
-        if in_macro_or_desugar(expr.span) {
+        if expr.span.from_expansion() {
             return;
         }
 
@@ -481,16 +486,11 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
         }
 
         // check for never_loop
-        match expr.node {
-            ExprKind::While(_, ref block, _) | ExprKind::Loop(ref block, _, _) => {
-                match never_loop_block(block, expr.hir_id) {
-                    NeverLoopResult::AlwaysBreak => {
-                        span_lint(cx, NEVER_LOOP, expr.span, "this loop never actually loops")
-                    },
-                    NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (),
-                }
-            },
-            _ => (),
+        if let ExprKind::Loop(ref block, _, _) = expr.node {
+            match never_loop_block(block, expr.hir_id) {
+                NeverLoopResult::AlwaysBreak => span_lint(cx, NEVER_LOOP, expr.span, "this loop never actually loops"),
+                NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (),
+            }
         }
 
         // check for `loop { if let {} else break }` that could be `while let`
@@ -590,32 +590,12 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
             }
         }
 
-        // check for while loops which conditions never change
-        if let ExprKind::While(ref cond, _, _) = expr.node {
-            check_infinite_loop(cx, cond, expr);
+        if let Some((cond, body)) = higher::while_loop(&expr) {
+            check_infinite_loop(cx, cond, body);
         }
 
         check_needless_collect(expr, cx);
     }
-
-    fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt) {
-        if let StmtKind::Semi(ref expr) = stmt.node {
-            if let ExprKind::MethodCall(ref method, _, ref args) = expr.node {
-                if args.len() == 1
-                    && method.ident.name == sym!(collect)
-                    && match_trait_method(cx, expr, &paths::ITERATOR)
-                {
-                    span_lint(
-                        cx,
-                        UNUSED_COLLECT,
-                        expr.span,
-                        "you are collect()ing an iterator and throwing away the result. \
-                         Consider using an explicit for loop to exhaust the iterator",
-                    );
-                }
-            }
-        }
-    }
 }
 
 enum NeverLoopResult {
@@ -701,12 +681,6 @@ fn never_loop_expr(expr: &Expr, main_loop_id: HirId) -> NeverLoopResult {
             // Break can come from the inner loop so remove them.
             absorb_break(&never_loop_block(b, main_loop_id))
         },
-        ExprKind::While(ref e, ref b, _) => {
-            let e = never_loop_expr(e, main_loop_id);
-            let result = never_loop_block(b, main_loop_id);
-            // Break can come from the inner loop so remove them.
-            combine_seq(e, absorb_break(&result))
-        },
         ExprKind::Match(ref e, ref arms, _) => {
             let e = never_loop_expr(e, main_loop_id);
             if arms.is_empty() {
@@ -727,8 +701,7 @@ fn never_loop_expr(expr: &Expr, main_loop_id: HirId) -> NeverLoopResult {
                 NeverLoopResult::AlwaysBreak
             }
         },
-        ExprKind::Break(_, _) => NeverLoopResult::AlwaysBreak,
-        ExprKind::Ret(ref e) => {
+        ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => {
             if let Some(ref e) = *e {
                 combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak)
             } else {
@@ -781,7 +754,7 @@ fn same_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr, var: HirId) -> bo
         if let ExprKind::Path(ref qpath) = expr.node;
         if let QPath::Resolved(None, ref path) = *qpath;
         if path.segments.len() == 1;
-        if let Res::Local(local_id) = cx.tables.qpath_res(qpath, expr.hir_id);
+        if let Res::Local(local_id) = qpath_res(cx, qpath, expr.hir_id);
         // our variable!
         if local_id == var;
         then {
@@ -822,7 +795,7 @@ fn is_slice_like<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'_>) -> bool {
         _ => false,
     };
 
-    is_slice || match_type(cx, ty, &paths::VEC) || match_type(cx, ty, &paths::VEC_DEQUE)
+    is_slice || is_type_diagnostic_item(cx, ty, Symbol::intern("vec_type")) || match_type(cx, ty, &paths::VEC_DEQUE)
 }
 
 fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr, var: HirId) -> Option<FixedOffsetVar> {
@@ -1068,7 +1041,7 @@ fn check_for_loop_range<'a, 'tcx>(
     body: &'tcx Expr,
     expr: &'tcx Expr,
 ) {
-    if in_macro_or_desugar(expr.span) {
+    if expr.span.from_expansion() {
         return;
     }
 
@@ -1103,7 +1076,7 @@ fn check_for_loop_range<'a, 'tcx>(
                 // ensure that the indexed variable was declared before the loop, see #601
                 if let Some(indexed_extent) = indexed_extent {
                     let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
-                    let parent_def_id = cx.tcx.hir().local_def_id_from_hir_id(parent_id);
+                    let parent_def_id = cx.tcx.hir().local_def_id(parent_id);
                     let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id);
                     let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id);
                     if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) {
@@ -1123,7 +1096,7 @@ fn check_for_loop_range<'a, 'tcx>(
                     return;
                 }
 
-                let starts_at_zero = is_integer_literal(start, 0);
+                let starts_at_zero = is_integer_const(cx, start, 0);
 
                 let skip = if starts_at_zero {
                     String::new()
@@ -1255,7 +1228,7 @@ fn is_end_eq_array_len<'tcx>(
         if let ExprKind::Lit(ref lit) = end.node;
         if let ast::LitKind::Int(end_int, _) = lit.node;
         if let ty::Array(_, arr_len_const) = indexed_ty.sty;
-        if let Some(arr_len) = arr_len_const.assert_usize(cx.tcx);
+        if let Some(arr_len) = arr_len_const.try_eval_usize(cx.tcx, cx.param_env);
         then {
             return match limits {
                 ast::RangeLimits::Closed => end_int + 1 >= arr_len.into(),
@@ -1377,7 +1350,7 @@ fn check_for_loop_arg(cx: &LateContext<'_, '_>, pat: &Pat, arg: &Expr, expr: &Ex
                     match cx.tables.expr_ty(&args[0]).sty {
                         // If the length is greater than 32 no traits are implemented for array and
                         // therefore we cannot use `&`.
-                        ty::Array(_, size) if size.assert_usize(cx.tcx).expect("array size") > 32 => (),
+                        ty::Array(_, size) if size.eval_usize(cx.tcx, cx.param_env) > 32 => {},
                         _ => lint_iter_method(cx, args, arg, method_name),
                     };
                 } else {
@@ -1645,7 +1618,7 @@ fn check_for_mutability(cx: &LateContext<'_, '_>, bound: &Expr) -> Option<HirId>
         if let ExprKind::Path(ref qpath) = bound.node;
         if let QPath::Resolved(None, _) = *qpath;
         then {
-            let res = cx.tables.qpath_res(qpath, bound.hir_id);
+            let res = qpath_res(cx, qpath, bound.hir_id);
             if let Res::Local(node_id) = res {
                 let node_str = cx.tcx.hir().get(node_id);
                 if_chain! {
@@ -1789,11 +1762,11 @@ fn check(&mut self, idx: &'tcx Expr, seqexpr: &'tcx Expr, expr: &'tcx Expr) -> b
                     if self.prefer_mutable {
                         self.indexed_mut.insert(seqvar.segments[0].ident.name);
                     }
-                    let res = self.cx.tables.qpath_res(seqpath, seqexpr.hir_id);
+                    let res = qpath_res(self.cx, seqpath, seqexpr.hir_id);
                     match res {
                         Res::Local(hir_id) => {
                             let parent_id = self.cx.tcx.hir().get_parent_item(expr.hir_id);
-                            let parent_def_id = self.cx.tcx.hir().local_def_id_from_hir_id(parent_id);
+                            let parent_def_id = self.cx.tcx.hir().local_def_id(parent_id);
                             let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id);
                             if indexed_indirectly {
                                 self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent));
@@ -1851,7 +1824,7 @@ fn visit_expr(&mut self, expr: &'tcx Expr) {
             if let QPath::Resolved(None, ref path) = *qpath;
             if path.segments.len() == 1;
             then {
-                if let Res::Local(local_id) = self.cx.tables.qpath_res(qpath, expr.hir_id) {
+                if let Res::Local(local_id) = qpath_res(self.cx, qpath, expr.hir_id) {
                     if local_id == self.var {
                         self.nonindex = true;
                     } else {
@@ -1977,7 +1950,7 @@ fn is_ref_iterable_type(cx: &LateContext<'_, '_>, e: &Expr) -> bool {
     // will allow further borrows afterwards
     let ty = cx.tables.expr_ty(e);
     is_iterable_array(ty, cx) ||
-    match_type(cx, ty, &paths::VEC) ||
+    is_type_diagnostic_item(cx, ty, Symbol::intern("vec_type")) ||
     match_type(cx, ty, &paths::LINKED_LIST) ||
     match_type(cx, ty, &paths::HASHMAP) ||
     match_type(cx, ty, &paths::HASHSET) ||
@@ -1990,7 +1963,7 @@ fn is_ref_iterable_type(cx: &LateContext<'_, '_>, e: &Expr) -> bool {
 fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'_, 'tcx>) -> bool {
     // IntoIterator is currently only implemented for array sizes <= 32 in rustc
     match ty.sty {
-        ty::Array(_, n) => (0..=32).contains(&n.assert_usize(cx.tcx).expect("array length")),
+        ty::Array(_, n) => (0..=32).contains(&n.eval_usize(cx.tcx, cx.param_env)),
         _ => false,
     }
 }
@@ -2030,10 +2003,7 @@ fn extract_first_expr(block: &Block) -> Option<&Expr> {
 fn is_simple_break_expr(expr: &Expr) -> bool {
     match expr.node {
         ExprKind::Break(dest, ref passed_expr) if dest.label.is_none() && passed_expr.is_none() => true,
-        ExprKind::Block(ref b, _) => match extract_first_expr(b) {
-            Some(subexpr) => is_simple_break_expr(subexpr),
-            None => false,
-        },
+        ExprKind::Block(ref b, _) => extract_first_expr(b).map_or(false, |subexpr| is_simple_break_expr(subexpr)),
         _ => false,
     }
 }
@@ -2072,7 +2042,7 @@ fn visit_expr(&mut self, expr: &'tcx Expr) {
                 match parent.node {
                     ExprKind::AssignOp(op, ref lhs, ref rhs) => {
                         if lhs.hir_id == expr.hir_id {
-                            if op.node == BinOpKind::Add && is_integer_literal(rhs, 1) {
+                            if op.node == BinOpKind::Add && is_integer_const(self.cx, rhs, 1) {
                                 *state = match *state {
                                     VarState::Initial if self.depth == 0 => VarState::IncrOnce,
                                     _ => VarState::DontWarn,
@@ -2124,7 +2094,7 @@ fn visit_stmt(&mut self, stmt: &'tcx Stmt) {
                     self.name = Some(ident.name);
 
                     self.state = if let Some(ref init) = local.init {
-                        if is_integer_literal(init, 0) {
+                        if is_integer_const(&self.cx, init, 0) {
                             VarState::Warn
                         } else {
                             VarState::Declared
@@ -2160,7 +2130,7 @@ fn visit_expr(&mut self, expr: &'tcx Expr) {
                         self.state = VarState::DontWarn;
                     },
                     ExprKind::Assign(ref lhs, ref rhs) if lhs.hir_id == expr.hir_id => {
-                        self.state = if is_integer_literal(rhs, 0) && self.depth == 0 {
+                        self.state = if is_integer_const(&self.cx, rhs, 0) && self.depth == 0 {
                             VarState::Warn
                         } else {
                             VarState::DontWarn
@@ -2193,7 +2163,7 @@ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
 
 fn var_def_id(cx: &LateContext<'_, '_>, expr: &Expr) -> Option<HirId> {
     if let ExprKind::Path(ref qpath) = expr.node {
-        let path_res = cx.tables.qpath_res(qpath, expr.hir_id);
+        let path_res = qpath_res(cx, qpath, expr.hir_id);
         if let Res::Local(node_id) = path_res {
             return Some(node_id);
         }
@@ -2203,7 +2173,7 @@ fn var_def_id(cx: &LateContext<'_, '_>, expr: &Expr) -> Option<HirId> {
 
 fn is_loop(expr: &Expr) -> bool {
     match expr.node {
-        ExprKind::Loop(..) | ExprKind::While(..) => true,
+        ExprKind::Loop(..) => true,
         _ => false,
     }
 }
@@ -2240,11 +2210,10 @@ fn is_loop_nested(cx: &LateContext<'_, '_>, loop_expr: &Expr, iter_expr: &Expr)
             return false;
         }
         match cx.tcx.hir().find(parent) {
-            Some(Node::Expr(expr)) => match expr.node {
-                ExprKind::Loop(..) | ExprKind::While(..) => {
+            Some(Node::Expr(expr)) => {
+                if let ExprKind::Loop(..) = expr.node {
                     return true;
-                },
-                _ => (),
+                };
             },
             Some(Node::Block(block)) => {
                 let mut block_visitor = LoopNestVisitor {
@@ -2386,7 +2355,7 @@ fn insert_def_id(&mut self, ex: &'tcx Expr) {
         if_chain! {
             if let ExprKind::Path(ref qpath) = ex.node;
             if let QPath::Resolved(None, _) = *qpath;
-            let res = self.cx.tables.qpath_res(qpath, ex.hir_id);
+            let res = qpath_res(self.cx, qpath, ex.hir_id);
             then {
                 match res {
                     Res::Local(node_id) => {
@@ -2430,7 +2399,7 @@ fn check_needless_collect<'a, 'tcx>(expr: &'tcx Expr, cx: &LateContext<'a, 'tcx>
         if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
         then {
             let ty = cx.tables.node_type(ty.hir_id);
-            if match_type(cx, ty, &paths::VEC) ||
+            if is_type_diagnostic_item(cx, ty, Symbol::intern("vec_type")) ||
                 match_type(cx, ty, &paths::VEC_DEQUE) ||
                 match_type(cx, ty, &paths::BTREEMAP) ||
                 match_type(cx, ty, &paths::HASHMAP) {