]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/redundant_clone.rs
Fix breakage due to rust-lang/rust#60913
[rust.git] / clippy_lints / src / redundant_clone.rs
index 7ac147c8ac17235cb01eef0fbc878df75e182f89..d24a20b079d1d64c683f63f2fc0e23417cae6531 100644 (file)
@@ -1,25 +1,22 @@
 use crate::utils::{
-    has_drop, in_macro, is_copy, match_def_path, match_type, paths, snippet_opt, span_lint_node,
-    span_lint_node_and_then, walk_ptrs_ty_depth,
+    has_drop, in_macro_or_desugar, is_copy, match_def_path, match_type, paths, snippet_opt, span_lint_hir,
+    span_lint_hir_and_then, walk_ptrs_ty_depth,
 };
 use if_chain::if_chain;
 use matches::matches;
 use rustc::hir::intravisit::FnKind;
-use rustc::hir::{def_id, Body, FnDecl};
+use rustc::hir::{def_id, Body, FnDecl, HirId};
 use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
 use rustc::mir::{
     self, traversal,
     visit::{MutatingUseContext, PlaceContext, Visitor},
     TerminatorKind,
 };
-use rustc::ty;
-use rustc::{declare_tool_lint, lint_array};
+use rustc::ty::{self, Ty};
+use rustc::{declare_lint_pass, declare_tool_lint};
 use rustc_errors::Applicability;
 use std::convert::TryFrom;
-use syntax::{
-    ast::NodeId,
-    source_map::{BytePos, Span},
-};
+use syntax::source_map::{BytePos, Span};
 
 macro_rules! unwrap_or_continue {
     ($x:expr) => {
@@ -30,56 +27,47 @@ macro_rules! unwrap_or_continue {
     };
 }
 
-/// **What it does:** Checks for a redudant `clone()` (and its relatives) which clones an owned
-/// value that is going to be dropped without further use.
-///
-/// **Why is this bad?** It is not always possible for the compiler to eliminate useless
-/// allocations and deallocations generated by redundant `clone()`s.
-///
-/// **Known problems:**
-///
-/// * Suggestions made by this lint could require NLL to be enabled.
-/// * False-positive if there is a borrow preventing the value from moving out.
-///
-/// ```rust
-/// let x = String::new();
-///
-/// let y = &x;
-///
-/// foo(x.clone()); // This lint suggests to remove this `clone()`
-/// ```
-///
-/// **Example:**
-/// ```rust
-/// {
-///     let x = Foo::new();
-///     call(x.clone());
-///     call(x.clone()); // this can just pass `x`
-/// }
-///
-/// ["lorem", "ipsum"].join(" ").to_string()
-///
-/// Path::new("/a/b").join("c").to_path_buf()
-/// ```
 declare_clippy_lint! {
+    /// **What it does:** Checks for a redudant `clone()` (and its relatives) which clones an owned
+    /// value that is going to be dropped without further use.
+    ///
+    /// **Why is this bad?** It is not always possible for the compiler to eliminate useless
+    /// allocations and deallocations generated by redundant `clone()`s.
+    ///
+    /// **Known problems:**
+    ///
+    /// * Suggestions made by this lint could require NLL to be enabled.
+    /// * False-positive if there is a borrow preventing the value from moving out.
+    ///
+    /// ```rust
+    /// let x = String::new();
+    ///
+    /// let y = &x;
+    ///
+    /// foo(x.clone()); // This lint suggests to remove this `clone()`
+    /// ```
+    ///
+    /// **Example:**
+    /// ```rust
+    /// {
+    ///     let x = Foo::new();
+    ///     call(x.clone());
+    ///     call(x.clone()); // this can just pass `x`
+    /// }
+    ///
+    /// ["lorem", "ipsum"].join(" ").to_string()
+    ///
+    /// Path::new("/a/b").join("c").to_path_buf()
+    /// ```
     pub REDUNDANT_CLONE,
     nursery,
     "`clone()` of an owned value that is going to be dropped immediately"
 }
 
-pub struct RedundantClone;
-
-impl LintPass for RedundantClone {
-    fn get_lints(&self) -> LintArray {
-        lint_array!(REDUNDANT_CLONE)
-    }
-
-    fn name(&self) -> &'static str {
-        "RedundantClone"
-    }
-}
+declare_lint_pass!(RedundantClone => [REDUNDANT_CLONE]);
 
 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
+    #[allow(clippy::too_many_lines)]
     fn check_fn(
         &mut self,
         cx: &LateContext<'a, 'tcx>,
@@ -87,7 +75,7 @@ fn check_fn(
         _: &'tcx FnDecl,
         body: &'tcx Body,
         _: Span,
-        _: NodeId,
+        _: HirId,
     ) {
         let def_id = cx.tcx.hir().body_owner_def_id(body.id());
         let mir = cx.tcx.optimized_mir(def_id);
@@ -95,7 +83,7 @@ fn check_fn(
         for (bb, bbdata) in mir.basic_blocks().iter_enumerated() {
             let terminator = bbdata.terminator();
 
-            if in_macro(terminator.source_info.span) {
+            if in_macro_or_desugar(terminator.source_info.span) {
                 continue;
             }
 
@@ -106,14 +94,13 @@ fn check_fn(
 
             let (fn_def_id, arg, arg_ty, _) = unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind));
 
-            let from_borrow = match_def_path(cx.tcx, fn_def_id, &paths::CLONE_TRAIT_METHOD)
-                || match_def_path(cx.tcx, fn_def_id, &paths::TO_OWNED_METHOD)
-                || (match_def_path(cx.tcx, fn_def_id, &paths::TO_STRING_METHOD)
-                    && match_type(cx, arg_ty, &paths::STRING));
+            let from_borrow = match_def_path(cx, fn_def_id, &paths::CLONE_TRAIT_METHOD)
+                || match_def_path(cx, fn_def_id, &paths::TO_OWNED_METHOD)
+                || (match_def_path(cx, fn_def_id, &paths::TO_STRING_METHOD) && match_type(cx, arg_ty, &paths::STRING));
 
             let from_deref = !from_borrow
-                && (match_def_path(cx.tcx, fn_def_id, &paths::PATH_TO_PATH_BUF)
-                    || match_def_path(cx.tcx, fn_def_id, &paths::OS_STR_TO_OS_STRING));
+                && (match_def_path(cx, fn_def_id, &paths::PATH_TO_PATH_BUF)
+                    || match_def_path(cx, fn_def_id, &paths::OS_STR_TO_OS_STRING));
 
             if !from_borrow && !from_deref {
                 continue;
@@ -145,8 +132,8 @@ fn check_fn(
                 let pred_arg = if_chain! {
                     if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, Some(res))) =
                         is_call_with_ref_arg(cx, mir, &pred_terminator.kind);
-                    if *res == mir::Place::Local(cloned);
-                    if match_def_path(cx.tcx, pred_fn_def_id, &paths::DEREF_TRAIT_METHOD);
+                    if res.base == mir::PlaceBase::Local(cloned);
+                    if match_def_path(cx, pred_fn_def_id, &paths::DEREF_TRAIT_METHOD);
                     if match_type(cx, pred_arg_ty, &paths::PATH_BUF)
                         || match_type(cx, pred_arg_ty, &paths::OS_STRING);
                     then {
@@ -201,7 +188,7 @@ fn check_fn(
                             span.lo() + BytePos(u32::try_from(dot).unwrap())
                         );
 
-                        span_lint_node_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |db| {
+                        span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |db| {
                             db.span_suggestion(
                                 sugg_span,
                                 "remove this",
@@ -214,7 +201,7 @@ fn check_fn(
                             );
                         });
                     } else {
-                        span_lint_node(cx, REDUNDANT_CLONE, node, span, "redundant clone");
+                        span_lint_hir(cx, REDUNDANT_CLONE, node, span, "redundant clone");
                     }
                 }
             }
@@ -225,13 +212,13 @@ fn check_fn(
 /// If `kind` is `y = func(x: &T)` where `T: !Copy`, returns `(DefId of func, x, T, y)`.
 fn is_call_with_ref_arg<'tcx>(
     cx: &LateContext<'_, 'tcx>,
-    mir: &'tcx mir::Mir<'tcx>,
+    mir: &'tcx mir::Body<'tcx>,
     kind: &'tcx mir::TerminatorKind<'tcx>,
-) -> Option<(def_id::DefId, mir::Local, ty::Ty<'tcx>, Option<&'tcx mir::Place<'tcx>>)> {
+) -> Option<(def_id::DefId, mir::Local, Ty<'tcx>, Option<&'tcx mir::Place<'tcx>>)> {
     if_chain! {
         if let TerminatorKind::Call { func, args, destination, .. } = kind;
         if args.len() == 1;
-        if let mir::Operand::Move(mir::Place::Local(local)) = &args[0];
+        if let mir::Operand::Move(mir::Place { base: mir::PlaceBase::Local(local), .. }) = &args[0];
         if let ty::FnDef(def_id, _) = func.ty(&*mir, cx.tcx).sty;
         if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx));
         if !is_copy(cx, inner_ty);
@@ -249,7 +236,7 @@ fn is_call_with_ref_arg<'tcx>(
 /// ``Some((from, [`true` if `from` cannot be moved out]))``.
 fn find_stmt_assigns_to<'a, 'tcx: 'a>(
     cx: &LateContext<'_, 'tcx>,
-    mir: &mir::Mir<'tcx>,
+    mir: &mir::Body<'tcx>,
     to: mir::Local,
     by_ref: bool,
     stmts: impl DoubleEndedIterator<Item = &'a mir::Statement<'tcx>>,
@@ -257,7 +244,14 @@ fn find_stmt_assigns_to<'a, 'tcx: 'a>(
     stmts
         .rev()
         .find_map(|stmt| {
-            if let mir::StatementKind::Assign(mir::Place::Local(local), v) = &stmt.kind {
+            if let mir::StatementKind::Assign(
+                mir::Place {
+                    base: mir::PlaceBase::Local(local),
+                    ..
+                },
+                v,
+            ) = &stmt.kind
+            {
                 if *local == to {
                     return Some(v);
                 }
@@ -283,28 +277,35 @@ fn find_stmt_assigns_to<'a, 'tcx: 'a>(
 /// Also reports whether given `place` cannot be moved out.
 fn base_local_and_movability<'tcx>(
     cx: &LateContext<'_, 'tcx>,
-    mir: &mir::Mir<'tcx>,
-    mut place: &mir::Place<'tcx>,
+    mir: &mir::Body<'tcx>,
+    place: &mir::Place<'tcx>,
 ) -> Option<(mir::Local, CannotMoveOut)> {
-    use rustc::mir::Place::*;
+    use rustc::mir::Place;
+    use rustc::mir::PlaceBase;
+    use rustc::mir::PlaceRef;
+    use rustc::mir::Projection;
 
     // Dereference. You cannot move things out from a borrowed value.
     let mut deref = false;
     // Accessing a field of an ADT that has `Drop`. Moving the field out will cause E0509.
     let mut field = false;
 
-    loop {
-        match place {
-            Local(local) => return Some((*local, deref || field)),
-            Projection(proj) => {
-                place = &proj.base;
-                deref = deref || matches!(proj.elem, mir::ProjectionElem::Deref);
-                if !field && matches!(proj.elem, mir::ProjectionElem::Field(..)) {
-                    field = has_drop(cx, place.ty(&mir.local_decls, cx.tcx).to_ty(cx.tcx));
-                }
-            },
-            _ => return None,
+    let PlaceRef {
+        base: place_base,
+        mut projection,
+    } = place.as_place_ref();
+    if let PlaceBase::Local(local) = place_base {
+        while let Some(box Projection { base, elem }) = projection {
+            projection = base;
+            deref = matches!(elem, mir::ProjectionElem::Deref);
+            field = !field
+                && matches!(elem, mir::ProjectionElem::Field(..))
+                && has_drop(cx, Place::ty_from(place_base, projection, &mir.local_decls, cx.tcx).ty);
         }
+
+        Some((*local, deref || field))
+    } else {
+        None
     }
 }
 
@@ -317,7 +318,7 @@ impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
     fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) {
         let statements = &data.statements;
         for (statement_index, statement) in statements.iter().enumerate() {
-            self.visit_statement(block, statement, mir::Location { block, statement_index });
+            self.visit_statement(statement, mir::Location { block, statement_index });
 
             // Once flagged, skip remaining statements
             if self.used_other_than_drop {
@@ -326,7 +327,6 @@ fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBl
         }
 
         self.visit_terminator(
-            block,
             data.terminator(),
             mir::Location {
                 block,
@@ -335,7 +335,7 @@ fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBl
         );
     }
 
-    fn visit_local(&mut self, local: &mir::Local, ctx: PlaceContext<'tcx>, _: mir::Location) {
+    fn visit_local(&mut self, local: &mir::Local, ctx: PlaceContext, _: mir::Location) {
         match ctx {
             PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) => return,
             _ => {},