Repository: intellij-rust/intellij-rust
Issue: False positive with "Dropping reference" inspection

<!--
Hello and thank you for the issue!
If you would like to report a bug, we have added some points below that you can fill out.
Feel free to remove all the irrelevant text to request a new feature.
-->

## Environment

* **Intellij-Rust plugin version:** v0.2.95.2120-183
* **Rust toolchain version:** 1.35.0-nightly
* **IDE name and version:** CLion 2018.3.4
* **Operating system:** Ubuntu 18.10

## Problem description

The plugin appears to confuse `std::mem::ManuallyDrop::drop` with `std::mem::drop` and reports a false positive when using the former.

## Steps to reproduce

Minimal example:

```rust
let mut drop = ManuallyDrop::new("nodrop");
unsafe { ManuallyDrop::drop(&mut drop) };
```

Changed files:
src/main/kotlin/org/rust/ide/inspections/fixes/RemoveRefFix.kt, src/test/kotlin/org/rust/ide/inspections/RsDropRefInspectionTest.kt, src/main/kotlin/org/rust/ide/inspections/RsDropRefInspection.kt

diff --git a/src/main/kotlin/org/rust/ide/inspections/RsDropRefInspection.kt b/src/main/kotlin/org/rust/ide/inspections/RsDropRefInspection.kt
index 1fc1dcef3..5c510b2b6 100644
--- a/src/main/kotlin/org/rust/ide/inspections/RsDropRefInspection.kt
+++ b/src/main/kotlin/org/rust/ide/inspections/RsDropRefInspection.kt
@@ -11,6 +11,9 @@ import org.rust.lang.core.psi.RsCallExpr
 import org.rust.lang.core.psi.RsFunction
 import org.rust.lang.core.psi.RsPathExpr
 import org.rust.lang.core.psi.RsVisitor
+import org.rust.lang.core.psi.ext.RsAbstractableOwner
+import org.rust.lang.core.psi.ext.owner
+import org.rust.lang.core.psi.ext.qualifiedName
 import org.rust.lang.core.types.ty.TyReference
 import org.rust.lang.core.types.type
 
@@ -30,7 +33,9 @@ class RsDropRefInspection : RsLocalInspectionTool() {
         val pathExpr = expr.expr as? RsPathExpr ?: return
 
         val resEl = pathExpr.path.reference.resolve()
-        if (resEl !is RsFunction || resEl.crateRelativePath != "::mem::drop") return
+        if (resEl !is RsFunction ||
+            resEl.qualifiedName != "core::mem::drop" ||
+            resEl.owner != RsAbstractableOwner.Free) return
 
         val args = expr.valueArgumentList.exprList
         if (args.size != 1) return
diff --git a/src/main/kotlin/org/rust/ide/inspections/fixes/RemoveRefFix.kt b/src/main/kotlin/org/rust/ide/inspections/fixes/RemoveRefFix.kt
index 09af11e0f..18d1b0b71 100644
--- a/src/main/kotlin/org/rust/ide/inspections/fixes/RemoveRefFix.kt
+++ b/src/main/kotlin/org/rust/ide/inspections/fixes/RemoveRefFix.kt
@@ -7,11 +7,12 @@ package org.rust.ide.inspections.fixes
 
 import com.intellij.codeInspection.LocalQuickFixOnPsiElement
 import com.intellij.openapi.project.Project
-import com.intellij.psi.PsiDocumentManager
 import com.intellij.psi.PsiElement
 import com.intellij.psi.PsiFile
 import org.rust.lang.core.psi.RsExpr
-import org.rust.lang.core.psi.ext.startOffset
+import org.rust.lang.core.psi.RsUnaryExpr
+import org.rust.lang.core.psi.ext.UnaryOperator
+import org.rust.lang.core.psi.ext.operatorType
 
 
 /**
@@ -28,10 +29,11 @@ class RemoveRefFix(
     override fun getFamilyName() = "Change reference to owned value"
 
     override fun invoke(project: Project, file: PsiFile, argEl: PsiElement, endElement: PsiElement) {
-        if (argEl.text != null && argEl.text[0] == '&') {
-            val offset = argEl.startOffset
-            val document = PsiDocumentManager.getInstance(project).getDocument(argEl.containingFile)
-            document?.deleteString(offset, offset + 1)
+        // If the reference is a mut one (&mut X) we want to remove the `mut` part too as `mut X` is either:
+        // - not valid Rust code (e.g.: `std::mem::drop(mut x)`)
+        // - or redundant (e.g.: `fn foo(mut self) {}` is exactly the same as `fn foo(self) {}`)
+        if (argEl is RsUnaryExpr && argEl.operatorType in listOf(UnaryOperator.REF, UnaryOperator.REF_MUT)) {
+            argEl.expr?.let { argEl.replace(it) }
         }
     }
 }
diff --git a/src/test/kotlin/org/rust/ide/inspections/RsDropRefInspectionTest.kt b/src/test/kotlin/org/rust/ide/inspections/RsDropRefInspectionTest.kt
index 687119657..4b9ede721 100644
--- a/src/test/kotlin/org/rust/ide/inspections/RsDropRefInspectionTest.kt
+++ b/src/test/kotlin/org/rust/ide/inspections/RsDropRefInspectionTest.kt
@@ -52,6 +52,16 @@ class RsDropRefInspectionTest : RsInspectionsTestBase(RsDropRefInspection()) {
         }
     """)
 
+    fun testDropManuallyDrop() = checkByText("""
+        use std::mem::ManuallyDrop;
+        fn main() {
+            let mut drop = ManuallyDrop::new("nodrop");
+            unsafe {
+                ManuallyDrop::drop(&mut drop); // This must not be highlighted
+            }
+        }
+    """)
+
     fun testDropRefMethodCall() = checkByText("""
         struct Foo;
         impl Foo {
@@ -74,4 +84,40 @@ class RsDropRefInspectionTest : RsInspectionsTestBase(RsDropRefInspection()) {
             drop(val1);
         }
     """)
+
+    fun testDropRefFix2() = checkFixByText("Call with owned value", """
+        fn main() {
+            let val1 = Box::new(40);
+            <warning descr="Call to std::mem::drop with a reference argument. Dropping a reference does nothing">drop(&   va<caret>l1)</warning>;
+        }
+    """, """
+        fn main() {
+            let val1 = Box::new(40);
+            drop(val1);
+        }
+    """)
+
+    fun testRefMutDropFix() = checkFixByText("Call with owned value", """
+        fn main() {
+            let val1 = Box::new(40);
+            <warning descr="Call to std::mem::drop with a reference argument. Dropping a reference does nothing">drop(&mut<caret> val1)</warning>;
+        }
+    """, """
+        fn main() {
+            let val1 = Box::new(40);
+            drop(val1);
+        }
+    """)
+
+    fun testRefMutDropFix2() = checkFixByText("Call with owned value", """
+        fn main() {
+            let val1 = Box::new(40);
+            <warning descr="Call to std::mem::drop with a reference argument. Dropping a reference does nothing">drop(&  mut<caret>  val1)</warning>;
+        }
+    """, """
+        fn main() {
+            let val1 = Box::new(40);
+            drop(val1);
+        }
+    """)
 }