diff --git a/src/main/kotlin/org/rust/ide/inspections/RsAssignInMatchGuardInspection.kt b/src/main/kotlin/org/rust/ide/inspections/RsAssignInMatchGuardInspection.kt new file mode 100644 index 00000000000..9d1ca3b4c8d --- /dev/null +++ b/src/main/kotlin/org/rust/ide/inspections/RsAssignInMatchGuardInspection.kt @@ -0,0 +1,54 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.rust.ide.inspections + +import com.intellij.psi.util.descendants +import org.rust.lang.core.psi.RsBinaryExpr +import org.rust.lang.core.psi.RsBlockExpr +import org.rust.lang.core.psi.RsExpr +import org.rust.lang.core.psi.RsExprStmt +import org.rust.lang.core.psi.RsMatchExpr +import org.rust.lang.core.psi.RsPathExpr +import org.rust.lang.core.psi.RsUnaryExpr +import org.rust.lang.core.psi.RsVisitor +import org.rust.lang.core.psi.ext.isAssignBinaryExpr +import org.rust.lang.utils.RsDiagnostic +import org.rust.lang.utils.addToHolder + +/** + * Inspection that detects the E0510 error. + */ +class RsAssignInMatchGuardInspection : RsLocalInspectionTool() { + override fun buildVisitor(holder: RsProblemsHolder, isOnTheFly: Boolean): RsVisitor = object : RsVisitor() { + override fun visitMatchExpr(match: RsMatchExpr) = inspect(holder, match) + + } +} + +private fun inspect(holder: RsProblemsHolder, match: RsMatchExpr) { + val varName = getVarName(match.expr) ?: return + match.matchBody?.matchArmList?.forEach { arm -> + (arm.matchArmGuard?.expr as? RsBlockExpr)?.let { guard -> + guard.descendants().forEach { element -> + (element as? RsExprStmt)?.expr.let { expr -> + (expr as? RsBinaryExpr)?.takeIf { it.isAssignBinaryExpr }?.let { binaryExpr -> + getVarName(binaryExpr.left).takeIf { it == varName }?.let { + RsDiagnostic.AssignInMatchGuardError(binaryExpr).addToHolder(holder) + } + } + } + } + } + } +} + +private fun getVarName(expr: RsExpr?): String? { + return when (expr) { + is RsPathExpr -> expr.path.referenceName + is RsUnaryExpr -> expr.expr?.let { getVarName(it) } + else -> null + } +} diff --git a/src/main/kotlin/org/rust/lang/utils/RsDiagnostic.kt b/src/main/kotlin/org/rust/lang/utils/RsDiagnostic.kt index 01050275103..3750e8c8321 100644 --- a/src/main/kotlin/org/rust/lang/utils/RsDiagnostic.kt +++ b/src/main/kotlin/org/rust/lang/utils/RsDiagnostic.kt @@ -1626,6 +1626,16 @@ sealed class RsDiagnostic( fixes = listOf(AddAssocTypeBindingsFix(element, missingTypes.map { it.name })) ) } + + class AssignInMatchGuardError( + element: PsiElement + ) : RsDiagnostic(element) { + override fun prepare() = PreparedAnnotation( + ERROR, + E0510, + "The matched value was assigned in a match guard" + ) + } } enum class RsErrorCode { @@ -1634,7 +1644,7 @@ enum class RsErrorCode { E0200, E0201, E0220, E0252, E0254, E0255, E0259, E0260, E0261, E0262, E0263, E0267, E0268, E0277, E0308, E0322, E0328, E0364, E0365, E0379, E0384, E0403, E0404, E0407, E0415, E0416, E0424, E0426, E0428, E0429, E0430, E0431, E0433, E0434, E0435, E0437, E0438, E0449, E0451, E0463, - E0517, E0518, E0537, E0552, E0554, E0562, E0569, E0583, E0586, E0594, + E0510, E0517, E0518, E0537, E0552, E0554, E0562, E0569, E0583, E0586, E0594, E0601, E0603, E0614, E0616, E0618, E0624, E0658, E0666, E0667, E0688, E0695, E0703, E0704, E0728, E0732, E0733, E0741, E0742, E0747, E0774; diff --git a/src/main/resources/META-INF/rust-core.xml b/src/main/resources/META-INF/rust-core.xml index 86e768f0ed8..33df08ddbc7 100644 --- a/src/main/resources/META-INF/rust-core.xml +++ b/src/main/resources/META-INF/rust-core.xml @@ -520,6 +520,11 @@ enabledByDefault="true" level="ERROR" implementationClass="org.rust.ide.inspections.RsWrongGenericParametersNumberInspection"/> + + + +Detects assignment of the matched value in a branch guard. + +Corresponds to E0510 Rust error. + + diff --git a/src/test/kotlin/org/rust/ide/inspections/RsAssignInMatchGuardInspectionTest.kt b/src/test/kotlin/org/rust/ide/inspections/RsAssignInMatchGuardInspectionTest.kt new file mode 100644 index 00000000000..0212e0514f0 --- /dev/null +++ b/src/test/kotlin/org/rust/ide/inspections/RsAssignInMatchGuardInspectionTest.kt @@ -0,0 +1,75 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.rust.ide.inspections + +class RsAssignInMatchGuardInspectionTest : RsInspectionsTestBase(RsAssignInMatchGuardInspection::class) { + fun `test E0510 assign to matched value simple`() = checkByText( + """ + fn main() { + let mut x = Some(0); + match x { + None => {} + Some(_) if { + x = None; + false + } => {} + Some(_) => {} + } + } + """ + ) + + fun `test E0510 with dereference`() = checkByText( + """ + fn main() { + let mut x = &Some(0); + match *x { + None => (), + Some(_) if { + x = &None; + false + } => (), + Some(_) => (), + } + } + """ + ) + + fun `test E0510 with lambda call`() = checkByText( + """ + fn main() { + let mut x = &mut &Some(&0); + match **x { + None => {} + Some(&_) if { + (|| { + x = &None; + })(); + } => {} + _ => {}, + } + } + """ + ) + + fun `test E0510 with nested block`() = checkByText( + """ + fn main() { + let mut x = &Some(0); + match *x { + None => (), + Some(_) if { + { + x = &None; + } + false + } => (), + Some(_) => (), + } + } + """ + ) +}