]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/loops.rs
Fix rustup fallout
[rust.git] / clippy_lints / src / loops.rs
index 86952c10dfc1675552e68fd4e51710938d6949b2..294f0449281ab7d1304faee9b0a88c38d7d41fef 100644 (file)
@@ -3,11 +3,11 @@
 use crate::utils::sugg::Sugg;
 use crate::utils::usage::{is_unused, mutated_variables};
 use crate::utils::{
-    get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher,
-    implements_trait, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item,
-    last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, qpath_res,
-    snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint,
-    span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq,
+    get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
+    is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method,
+    match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability,
+    snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg,
+    SpanlessEq,
 };
 use if_chain::if_chain;
 use rustc_ast::ast;
@@ -1003,7 +1003,7 @@ fn detect_manual_memcpy<'tcx>(
         start: Some(start),
         end: Some(end),
         limits,
-    }) = higher::range(cx, arg)
+    }) = higher::range(arg)
     {
         // the var must be a single name
         if let PatKind::Binding(_, canonical_id, _, _) = pat.kind {
@@ -1052,82 +1052,6 @@ fn detect_manual_memcpy<'tcx>(
     }
 }
 
-// Scans for the usage of the for loop pattern
-struct ForPatternVisitor<'a, 'tcx> {
-    found_pattern: bool,
-    // Pattern that we are searching for
-    for_pattern: &'a Pat<'tcx>,
-    cx: &'a LateContext<'tcx>,
-}
-
-impl<'a, 'tcx> Visitor<'tcx> for ForPatternVisitor<'a, 'tcx> {
-    type Map = Map<'tcx>;
-
-    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
-        // Recursively explore an expression until a ExprKind::Path is found
-        match &expr.kind {
-            ExprKind::Array(expr_list) | ExprKind::MethodCall(_, _, expr_list, _) | ExprKind::Tup(expr_list) => {
-                for expr in *expr_list {
-                    self.visit_expr(expr)
-                }
-            },
-            ExprKind::Binary(_, lhs_expr, rhs_expr) => {
-                self.visit_expr(lhs_expr);
-                self.visit_expr(rhs_expr);
-            },
-            ExprKind::Box(expr)
-            | ExprKind::Unary(_, expr)
-            | ExprKind::Cast(expr, _)
-            | ExprKind::Type(expr, _)
-            | ExprKind::AddrOf(_, _, expr)
-            | ExprKind::Field(expr, _)
-            | ExprKind::Struct(_, _, Some(expr)) => self.visit_expr(expr),
-            _ => {
-                // Exploration cannot continue ... calculate the hir_id of the current
-                // expr assuming it is a Path
-                if let Some(hir_id) = var_def_id(self.cx, &expr) {
-                    // Pattern is found
-                    if hir_id == self.for_pattern.hir_id {
-                        self.found_pattern = true;
-                    }
-                    // If the for loop pattern is a tuple, determine whether the current
-                    // expr is inside that tuple pattern
-                    if let PatKind::Tuple(pat_list, _) = &self.for_pattern.kind {
-                        let hir_id_list: Vec<HirId> = pat_list.iter().map(|p| p.hir_id).collect();
-                        if hir_id_list.contains(&hir_id) {
-                            self.found_pattern = true;
-                        }
-                    }
-                }
-            },
-        }
-    }
-
-    // This is triggered by walk_expr() for the case of vec.push(pat)
-    fn visit_qpath(&mut self, qpath: &'tcx QPath<'_>, _: HirId, _: Span) {
-        if_chain! {
-            if let QPath::Resolved(_, path) = qpath;
-            if let Res::Local(hir_id) = &path.res;
-            then {
-                if *hir_id == self.for_pattern.hir_id{
-                    self.found_pattern = true;
-                }
-
-                if let PatKind::Tuple(pat_list, _) = &self.for_pattern.kind {
-                    let hir_id_list: Vec<HirId> = pat_list.iter().map(|p| p.hir_id).collect();
-                    if hir_id_list.contains(&hir_id) {
-                        self.found_pattern = true;
-                    }
-                }
-            }
-        }
-    }
-
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::None
-    }
-}
-
 // Scans the body of the for loop and determines whether lint should be given
 struct SameItemPushVisitor<'a, 'tcx> {
     should_lint: bool,
@@ -1207,6 +1131,27 @@ fn detect_same_item_push<'tcx>(
     body: &'tcx Expr<'_>,
     _: &'tcx Expr<'_>,
 ) {
+    fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) {
+        let vec_str = snippet_with_macro_callsite(cx, vec.span, "");
+        let item_str = snippet_with_macro_callsite(cx, pushed_item.span, "");
+
+        span_lint_and_help(
+            cx,
+            SAME_ITEM_PUSH,
+            vec.span,
+            "it looks like the same item is being pushed into this Vec",
+            None,
+            &format!(
+                "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})",
+                item_str, vec_str, item_str
+            ),
+        )
+    }
+
+    if !matches!(pat.kind, PatKind::Wild) {
+        return;
+    }
+
     // Determine whether it is safe to lint the body
     let mut same_item_push_visitor = SameItemPushVisitor {
         should_lint: true,
@@ -1216,32 +1161,50 @@ fn detect_same_item_push<'tcx>(
     walk_expr(&mut same_item_push_visitor, body);
     if same_item_push_visitor.should_lint {
         if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push {
-            // Make sure that the push does not involve possibly mutating values
-            if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) {
-                // Walk through the expression being pushed and make sure that it
-                // does not contain the for loop pattern
-                let mut for_pat_visitor = ForPatternVisitor {
-                    found_pattern: false,
-                    for_pattern: pat,
-                    cx,
-                };
-                walk_expr(&mut for_pat_visitor, pushed_item);
-
-                if !for_pat_visitor.found_pattern {
-                    let vec_str = snippet_with_macro_callsite(cx, vec.span, "");
-                    let item_str = snippet_with_macro_callsite(cx, pushed_item.span, "");
-
-                    span_lint_and_help(
-                        cx,
-                        SAME_ITEM_PUSH,
-                        vec.span,
-                        "it looks like the same item is being pushed into this Vec",
-                        None,
-                        &format!(
-                            "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})",
-                            item_str, vec_str, item_str
-                        ),
-                    )
+            let vec_ty = cx.typeck_results().expr_ty(vec);
+            let ty = vec_ty.walk().nth(1).unwrap().expect_ty();
+            if cx
+                .tcx
+                .lang_items()
+                .clone_trait()
+                .map_or(false, |id| implements_trait(cx, ty, id, &[]))
+            {
+                // Make sure that the push does not involve possibly mutating values
+                match pushed_item.kind {
+                    ExprKind::Path(ref qpath) => {
+                        match qpath_res(cx, qpath, pushed_item.hir_id) {
+                            // immutable bindings that are initialized with literal or constant
+                            Res::Local(hir_id) => {
+                                if_chain! {
+                                    let node = cx.tcx.hir().get(hir_id);
+                                    if let Node::Binding(pat) = node;
+                                    if let PatKind::Binding(bind_ann, ..) = pat.kind;
+                                    if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable);
+                                    let parent_node = cx.tcx.hir().get_parent_node(hir_id);
+                                    if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node);
+                                    if let Some(init) = parent_let_expr.init;
+                                    then {
+                                        match init.kind {
+                                            // immutable bindings that are initialized with literal
+                                            ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item),
+                                            // immutable bindings that are initialized with constant
+                                            ExprKind::Path(ref path) => {
+                                                if let Res::Def(DefKind::Const, ..) = qpath_res(cx, path, init.hir_id) {
+                                                    emit_lint(cx, vec, pushed_item);
+                                                }
+                                            }
+                                            _ => {},
+                                        }
+                                    }
+                                }
+                            },
+                            // constant
+                            Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item),
+                            _ => {},
+                        }
+                    },
+                    ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item),
+                    _ => {},
                 }
             }
         }
@@ -1262,7 +1225,7 @@ fn check_for_loop_range<'tcx>(
         start: Some(start),
         ref end,
         limits,
-    }) = higher::range(cx, arg)
+    }) = higher::range(arg)
     {
         // the var must be a single name
         if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind {
@@ -1764,7 +1727,7 @@ fn check_for_mut_range_bound(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'
         start: Some(start),
         end: Some(end),
         ..
-    }) = higher::range(cx, arg)
+    }) = higher::range(arg)
     {
         let mut_ids = vec![check_for_mutability(cx, start), check_for_mutability(cx, end)];
         if mut_ids[0].is_some() || mut_ids[1].is_some() {
@@ -2606,7 +2569,7 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont
                 match_type(cx, ty, &paths::BTREEMAP) ||
                 is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) {
                 if method.ident.name == sym!(len) {
-                    let span = shorten_span(expr, sym!(collect));
+                    let span = shorten_needless_collect_span(expr);
                     span_lint_and_sugg(
                         cx,
                         NEEDLESS_COLLECT,
@@ -2618,20 +2581,20 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont
                     );
                 }
                 if method.ident.name == sym!(is_empty) {
-                    let span = shorten_span(expr, sym!(iter));
+                    let span = shorten_needless_collect_span(expr);
                     span_lint_and_sugg(
                         cx,
                         NEEDLESS_COLLECT,
                         span,
                         NEEDLESS_COLLECT_MSG,
                         "replace with",
-                        "get(0).is_none()".to_string(),
+                        "next().is_none()".to_string(),
                         Applicability::MachineApplicable,
                     );
                 }
                 if method.ident.name == sym!(contains) {
                     let contains_arg = snippet(cx, args[1].span, "??");
-                    let span = shorten_span(expr, sym!(collect));
+                    let span = shorten_needless_collect_span(expr);
                     span_lint_and_then(
                         cx,
                         NEEDLESS_COLLECT,
@@ -2811,13 +2774,13 @@ fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident)
     }
 }
 
-fn shorten_span(expr: &Expr<'_>, target_fn_name: Symbol) -> Span {
-    let mut current_expr = expr;
-    while let ExprKind::MethodCall(ref path, ref span, ref args, _) = current_expr.kind {
-        if path.ident.name == target_fn_name {
+fn shorten_needless_collect_span(expr: &Expr<'_>) -> Span {
+    if_chain! {
+        if let ExprKind::MethodCall(.., args, _) = &expr.kind;
+        if let ExprKind::MethodCall(_, span, ..) = &args[0].kind;
+        then {
             return expr.span.with_lo(span.lo());
         }
-        current_expr = &args[0];
     }
-    unreachable!()
+    unreachable!();
 }