Repository: intellij-rust/intellij-rust
Issue: False positive "use of moved value" when destructuring array of move-only types

## Environment

* **Intellij-Rust plugin version:**  v.0.2.98.2125-191
* **Rust toolchain version:** 1.35.0
* **IDE name and version:** CLion #CL-191.7479.33
* **Operating system:** macOS Mojave 10.14.5

## Problem description

False positive when destructuring arrays of move-only types.

## Steps to reproduce
 
```rust
let box_arr = [Box::new('a'), Box::new('b'), Box::new('c')];
let [a, b, c] = box_arr; // falsely reports "use of moved value"
```


Changed files:
src/main/kotlin/org/rust/lang/core/types/borrowck/CheckLoans.kt, src/test/kotlin/org/rust/ide/inspections/borrowck/RsBorrowCheckerMovesTest.kt, src/main/kotlin/org/rust/lang/core/types/borrowck/BorrowChecker.kt

diff --git a/src/main/kotlin/org/rust/lang/core/types/borrowck/BorrowChecker.kt b/src/main/kotlin/org/rust/lang/core/types/borrowck/BorrowChecker.kt
index 31699bdc9..4ebf8f86f 100644
--- a/src/main/kotlin/org/rust/lang/core/types/borrowck/BorrowChecker.kt
+++ b/src/main/kotlin/org/rust/lang/core/types/borrowck/BorrowChecker.kt
@@ -21,9 +21,9 @@ class BorrowCheckContext(
     val owner: RsInferenceContextOwner,
     val body: RsBlock,
     val implLookup: ImplLookup = ImplLookup.relativeTo(body),
-    private val usesOfMovedValue: MutableList<UseOfMovedValueError> = mutableListOf(),
-    private val usesOfUninitializedVariable: MutableList<UseOfUninitializedVariable> = mutableListOf(),
-    private val moveErrors: MutableList<MoveError> = mutableListOf()
+    private val usesOfMovedValue: MutableSet<UseOfMovedValueError> = hashSetOf(),
+    private val usesOfUninitializedVariable: MutableSet<UseOfUninitializedVariable> = hashSetOf(),
+    private val moveErrors: MutableSet<MoveError> = hashSetOf()
 ) {
     companion object {
         fun buildFor(owner: RsInferenceContextOwner): BorrowCheckContext? {
@@ -40,7 +40,12 @@ class BorrowCheckContext(
             val clcx = CheckLoanContext(this, data.moveData)
             clcx.checkLoans(body)
         }
-        return BorrowCheckResult(this.usesOfMovedValue, this.usesOfUninitializedVariable, this.moveErrors)
+
+        return BorrowCheckResult(
+            this.usesOfMovedValue.toList(),
+            this.usesOfUninitializedVariable.toList(),
+            this.moveErrors.toList()
+        )
     }
 
     private fun buildAnalysisData(bccx: BorrowCheckContext): AnalysisData? {
@@ -72,6 +77,6 @@ data class BorrowCheckResult(
     val moveErrors: List<MoveError>
 )
 
-class UseOfMovedValueError(val use: RsElement, val move: Move)
-class UseOfUninitializedVariable(val use: RsElement)
-class MoveError(val from: Cmt, val to: MovePlace?)
+data class UseOfMovedValueError(val use: RsElement, val move: Move)
+data class UseOfUninitializedVariable(val use: RsElement)
+data class MoveError(val from: Cmt, val to: MovePlace?)
diff --git a/src/main/kotlin/org/rust/lang/core/types/borrowck/CheckLoans.kt b/src/main/kotlin/org/rust/lang/core/types/borrowck/CheckLoans.kt
index ef51f144c..b22494887 100644
--- a/src/main/kotlin/org/rust/lang/core/types/borrowck/CheckLoans.kt
+++ b/src/main/kotlin/org/rust/lang/core/types/borrowck/CheckLoans.kt
@@ -7,7 +7,9 @@ package org.rust.lang.core.types.borrowck
 
 import org.rust.lang.core.psi.RsBlock
 import org.rust.lang.core.psi.RsPat
+import org.rust.lang.core.psi.RsPatSlice
 import org.rust.lang.core.psi.ext.RsElement
+import org.rust.lang.core.psi.ext.ancestorOrSelf
 import org.rust.lang.core.types.borrowck.LoanPathElement.Interior
 import org.rust.lang.core.types.borrowck.LoanPathKind.Downcast
 import org.rust.lang.core.types.borrowck.LoanPathKind.Extend
@@ -29,11 +31,20 @@ class CheckLoanContext(private val bccx: BorrowCheckContext, private val moveDat
 
     private fun checkIfPathIsMoved(element: RsElement, loanPath: LoanPath) {
         moveData.eachMoveOf(element, loanPath) { move, _ ->
-            bccx.reportUseOfMovedValue(loanPath, move)
-            false
+            if (isInsideSliceDestructing(loanPath, move)) {
+                // It's permitted to move multiple times inside one slice destructing
+                // e.g. `let [a, b, c] = arr`
+                true
+            } else {
+                bccx.reportUseOfMovedValue(loanPath, move)
+                false
+            }
         }
     }
 
+    private fun isInsideSliceDestructing(loanPath: LoanPath, move: Move): Boolean =
+        loanPath.element is RsPatSlice && move.element.ancestorOrSelf<RsPatSlice>() == loanPath.element
+
     override fun declarationWithoutInit(element: RsElement) {}
 
     override fun mutate(assignmentElement: RsElement, assigneeCmt: Cmt, mode: MutateMode) {
diff --git a/src/test/kotlin/org/rust/ide/inspections/borrowck/RsBorrowCheckerMovesTest.kt b/src/test/kotlin/org/rust/ide/inspections/borrowck/RsBorrowCheckerMovesTest.kt
index ec186af38..607805843 100644
--- a/src/test/kotlin/org/rust/ide/inspections/borrowck/RsBorrowCheckerMovesTest.kt
+++ b/src/test/kotlin/org/rust/ide/inspections/borrowck/RsBorrowCheckerMovesTest.kt
@@ -369,4 +369,33 @@ class RsBorrowCheckerMovesTest : RsInspectionsTestBase(RsBorrowCheckerInspection
             if *a == *b {}
         }
     """, checkWarn = false)
+
+    fun `test no move error destruct array`() = checkByText("""
+        struct S;
+
+        fn main() {
+            let arr = [S, S, S];
+            let [a, b, c] = arr;
+        }
+    """, checkWarn = false)
+
+    fun `test move array after destruct`() = checkByText("""
+        struct S;
+
+        fn main() {
+            let arr = [S, S, S];
+            let [a, b, c] = arr;
+            <error descr="Use of moved value">arr</error>;
+        }
+    """, checkWarn = false)
+
+    fun `test double destruct array`() = checkByText("""
+        struct S;
+
+        fn main() {
+            let arr = [S, S, S];
+            let [a, b, c] = arr;
+            let <error descr="Use of moved value">[a1, b1, c1]</error> = arr;
+        }
+    """, checkWarn = false)
 }