Skip to content

Commit

Permalink
Merge #5550
Browse files Browse the repository at this point in the history
5550: INSP: add quick fix to convert immutable reference to mutable reference r=mchernyavsky a=Kobzol

This PR adds a quick fix to convert an immutable reference to a mutable one:
![refmut](https://user-images.githubusercontent.com/4539057/84370594-c125ff80-abd8-11ea-96ff-dbd407d3455c.gif)

The last test should be enabled if/after #5548 gets merged.

Umbrella issue: #1730
Fixes: #2472

Co-authored-by: Jakub Beránek <berykubik@gmail.com>
  • Loading branch information
bors[bot] and Kobzol committed Jun 17, 2020
2 parents e52be04 + 8de6c0e commit e0daaa6
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 6 deletions.
@@ -0,0 +1,35 @@
/*
* Use of this source code is governed by the MIT license that can be
* found in the LICENSE file.
*/

package org.rust.ide.inspections.fixes

import com.intellij.codeInspection.LocalQuickFixOnPsiElement
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import org.rust.lang.core.psi.RsPsiFactory
import org.rust.lang.core.psi.RsUnaryExpr
import org.rust.lang.core.psi.ext.RsElement
import org.rust.lang.core.psi.ext.UnaryOperator
import org.rust.lang.core.psi.ext.operatorType


/**
* Fix that converts the given immutable reference to a mutable reference.
* @param expr An element, that represents an immutable reference.
*/
class ChangeRefToMutableFix(expr: RsElement) : LocalQuickFixOnPsiElement(expr) {
override fun getText() = "Change reference to mutable"
override fun getFamilyName() = text

override fun invoke(project: Project, file: PsiFile, startElement: PsiElement, endElement: PsiElement) {
val ref = startElement as? RsUnaryExpr ?: return
if (ref.operatorType != UnaryOperator.REF) return
val innerExpr = ref.expr ?: return

val mutableExpr = RsPsiFactory(project).tryCreateExpression("&mut ${innerExpr.text}") ?: return
startElement.replace(mutableExpr)
}
}
19 changes: 13 additions & 6 deletions src/main/kotlin/org/rust/lang/utils/RsDiagnostic.kt
Expand Up @@ -25,6 +25,7 @@ import org.rust.ide.inspections.checkMatch.Pattern
import org.rust.ide.inspections.fixes.AddMainFnFix
import org.rust.ide.inspections.fixes.AddRemainingArmsFix
import org.rust.ide.inspections.fixes.AddWildcardArmFix
import org.rust.ide.inspections.fixes.ChangeRefToMutableFix
import org.rust.ide.refactoring.implementMembers.ImplementMembersFix
import org.rust.ide.utils.isEnabledByCfg
import org.rust.lang.core.psi.*
Expand Down Expand Up @@ -105,13 +106,19 @@ sealed class RsDiagnostic(
if (isTraitWithTySubstImplForActual(lookup, items.AsRef, expectedTy)) {
add(ConvertToRefTyFix(element, expectedTy))
}
} else if (expectedTy.mutability == Mutability.MUTABLE && element is RsExpr && element.isMutable
&& lookup.coercionSequence(actualTy).all { it !is TyReference || it.mutability.isMut }) {
if (isTraitWithTySubstImplForActual(lookup, items.BorrowMut, expectedTy)) {
add(ConvertToBorrowedTyWithMutFix(element, expectedTy))
} else if (expectedTy.mutability == Mutability.MUTABLE) {
if (actualTy is TyReference && actualTy.mutability == Mutability.IMMUTABLE) {
add(ChangeRefToMutableFix(element))
}
if (isTraitWithTySubstImplForActual(lookup, items.AsMut, expectedTy)) {
add(ConvertToMutTyFix(element, expectedTy))

if (element is RsExpr && element.isMutable
&& lookup.coercionSequence(actualTy).all { it !is TyReference || it.mutability.isMut }) {
if (isTraitWithTySubstImplForActual(lookup, items.BorrowMut, expectedTy)) {
add(ConvertToBorrowedTyWithMutFix(element, expectedTy))
}
if (isTraitWithTySubstImplForActual(lookup, items.AsMut, expectedTy)) {
add(ConvertToMutTyFix(element, expectedTy))
}
}
}
} else if (expectedTy is TyAdt && expectedTy.item == items.Result) {
Expand Down
@@ -0,0 +1,85 @@
/*
* Use of this source code is governed by the MIT license that can be
* found in the LICENSE file.
*/

package org.rust.ide.inspections.typecheck

import org.rust.ide.inspections.RsInspectionsTestBase
import org.rust.ide.inspections.RsTypeCheckInspection

class ChangeRefToMutableFixTest : RsInspectionsTestBase(RsTypeCheckInspection::class) {
fun `test simple`() = checkFixByText("Change reference to mutable", """
fn foo(t: &mut u32) {}
fn bar() {
let mut x: u32 = 5;
foo(/*caret*/<error>&x</error>);
}
""", """
fn foo(t: &mut u32) {}
fn bar() {
let mut x: u32 = 5;
foo(&mut x);
}
""")

fun `test immutable variable`() = checkFixByText("Change reference to mutable", """
fn foo(t: &mut u32) {}
fn bar() {
let x: u32 = 5;
foo(/*caret*/<error>&x</error>);
}
""", """
fn foo(t: &mut u32) {}
fn bar() {
let x: u32 = 5;
foo(&mut x);
}
""")

fun `test nested references`() = checkFixByText("Change reference to mutable", """
fn foo(t: &mut &u32) {}
fn bar() {
let mut x: u32 = 5;
foo(/*caret*/<error>&x</error>);
}
""", """
fn foo(t: &mut &u32) {}
fn bar() {
let mut x: u32 = 5;
foo(&mut x);
}
""")

fun `test unknown inner type`() = checkFixByText("Change reference to mutable", """
struct S<T>(T);
impl<T> S<T> {
fn new() -> Self { unreachable!() }
}
fn foo(t: &mut S<u32>) {}
fn bar() {
let mut x = S::new();
foo(/*caret*/<error>&x</error>);
}
""", """
struct S<T>(T);
impl<T> S<T> {
fn new() -> Self { unreachable!() }
}
fn foo(t: &mut S<u32>) {}
fn bar() {
let mut x = S::new();
foo(&mut x);
}
""")

fun `test unavailable on mut reference`() = checkFixIsUnavailable("Change reference to mutable", """
fn foo(t: &mut u32) {}
fn bar() {
let mut x: u32 = 5;
foo(/*caret*/&mut x);
}
""")
}

0 comments on commit e0daaa6

Please sign in to comment.