diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/SuspiciousCollectionReassignmentInspection.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/SuspiciousCollectionReassignmentInspection.kt index a25991bf5540..92fabb325f73 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/SuspiciousCollectionReassignmentInspection.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/SuspiciousCollectionReassignmentInspection.kt @@ -4,17 +4,19 @@ package org.jetbrains.kotlin.idea.inspections import com.intellij.codeInsight.intention.FileModifier.SafeFieldForPreview import com.intellij.codeInspection.LocalQuickFix -import com.intellij.codeInspection.ProblemDescriptor import com.intellij.codeInspection.ProblemHighlightType import com.intellij.codeInspection.ProblemsHolder +import com.intellij.modcommand.ModPsiUpdater import com.intellij.openapi.project.Project import com.intellij.psi.PsiElementVisitor import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.diagnostics.Severity -import org.jetbrains.kotlin.idea.base.resources.KotlinBundle import org.jetbrains.kotlin.idea.base.psi.replaced +import org.jetbrains.kotlin.idea.base.resources.KotlinBundle import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.codeinsight.api.applicable.inspections.KotlinModCommandQuickFix +import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractKotlinInspection import org.jetbrains.kotlin.idea.project.builtIns import org.jetbrains.kotlin.idea.quickfix.ChangeToMutableCollectionFix import org.jetbrains.kotlin.idea.references.mainReference @@ -33,9 +35,6 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.SimpleType -import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractKotlinInspection -import org.jetbrains.kotlin.idea.codeinsight.utils.findExistingEditor - class SuspiciousCollectionReassignmentInspection : AbstractKotlinInspection() { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor = binaryExpressionVisitor(fun(binaryExpression) { @@ -75,19 +74,19 @@ class SuspiciousCollectionReassignmentInspection : AbstractKotlinInspection() { ) }) - private class ChangeTypeToMutableFix(@SafeFieldForPreview private val type: KotlinType) : LocalQuickFix { + private class ChangeTypeToMutableFix(@SafeFieldForPreview private val type: KotlinType) : KotlinModCommandQuickFix() { override fun getName() = KotlinBundle.message("change.type.to.mutable.fix.text") override fun getFamilyName() = name - override fun applyFix(project: Project, descriptor: ProblemDescriptor) { - val operationReference = descriptor.psiElement as? KtOperationReferenceExpression ?: return + override fun applyFix(project: Project, element: KtOperationReferenceExpression, updater: ModPsiUpdater) { + val operationReference = element val binaryExpression = operationReference.parent as? KtBinaryExpression ?: return val left = binaryExpression.left ?: return val property = left.mainReference?.resolve() as? KtProperty ?: return ChangeToMutableCollectionFix.applyFix(property, type) property.valOrVarKeyword.replace(KtPsiFactory(project).createValKeyword()) - binaryExpression.findExistingEditor()?.caretModel?.moveToOffset(property.endOffset) + updater.moveCaretTo(property.endOffset) } companion object { @@ -97,13 +96,13 @@ class SuspiciousCollectionReassignmentInspection : AbstractKotlinInspection() { } } - private class ReplaceWithFilterFix : LocalQuickFix { + private class ReplaceWithFilterFix : KotlinModCommandQuickFix() { override fun getName() = KotlinBundle.message("replace.with.filter.fix.text") override fun getFamilyName() = name - override fun applyFix(project: Project, descriptor: ProblemDescriptor) { - val operationReference = descriptor.psiElement as? KtOperationReferenceExpression ?: return + override fun applyFix(project: Project, element: KtOperationReferenceExpression, updater: ModPsiUpdater) { + val operationReference = element val binaryExpression = operationReference.parent as? KtBinaryExpression ?: return val left = binaryExpression.left ?: return val right = binaryExpression.right ?: return @@ -121,13 +120,13 @@ class SuspiciousCollectionReassignmentInspection : AbstractKotlinInspection() { } } - private class ReplaceWithAssignmentFix : LocalQuickFix { + private class ReplaceWithAssignmentFix : KotlinModCommandQuickFix() { override fun getName() = KotlinBundle.message("replace.with.assignment.fix.text") override fun getFamilyName() = name - override fun applyFix(project: Project, descriptor: ProblemDescriptor) { - val operationReference = descriptor.psiElement as? KtOperationReferenceExpression ?: return + override fun applyFix(project: Project, element: KtOperationReferenceExpression, updater: ModPsiUpdater) { + val operationReference = element val psiFactory = KtPsiFactory(project) operationReference.replace(psiFactory.createOperationName(KtTokens.EQ.value)) } @@ -161,13 +160,13 @@ class SuspiciousCollectionReassignmentInspection : AbstractKotlinInspection() { } } - private class JoinWithInitializerFix(@SafeFieldForPreview private val op: KtSingleValueToken) : LocalQuickFix { + private class JoinWithInitializerFix(@SafeFieldForPreview private val op: KtSingleValueToken) : KotlinModCommandQuickFix() { override fun getName() = KotlinBundle.message("join.with.initializer.fix.text") override fun getFamilyName() = name - override fun applyFix(project: Project, descriptor: ProblemDescriptor) { - val operationReference = descriptor.psiElement as? KtOperationReferenceExpression ?: return + override fun applyFix(project: Project, element: KtOperationReferenceExpression, updater: ModPsiUpdater) { + val operationReference = element val binaryExpression = operationReference.parent as? KtBinaryExpression ?: return val left = binaryExpression.left ?: return val right = binaryExpression.right ?: return @@ -178,7 +177,7 @@ class SuspiciousCollectionReassignmentInspection : AbstractKotlinInspection() { val newOp = if (op == KtTokens.PLUSEQ) KtTokens.PLUS else KtTokens.MINUS val replaced = initializer.replaced(psiFactory.createExpressionByPattern("$0 $1 $2", initializer, newOp.value, right)) binaryExpression.delete() - property.findExistingEditor()?.caretModel?.moveToOffset(replaced.endOffset) + updater.moveCaretTo(replaced.endOffset) } companion object { diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeToMutableCollectionFix.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeToMutableCollectionFix.kt index d0bd3f907f2a..8de640c96c59 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeToMutableCollectionFix.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeToMutableCollectionFix.kt @@ -2,8 +2,11 @@ package org.jetbrains.kotlin.idea.quickfix -import com.intellij.openapi.editor.Editor -import com.intellij.openapi.project.Project +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.codeInspection.util.IntentionFamilyName +import com.intellij.modcommand.ActionContext +import com.intellij.modcommand.ModPsiUpdater +import com.intellij.modcommand.Presentation import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Errors @@ -11,7 +14,7 @@ import org.jetbrains.kotlin.idea.base.resources.KotlinBundle import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall import org.jetbrains.kotlin.idea.base.psi.replaced -import org.jetbrains.kotlin.idea.codeinsight.api.classic.quickfixes.KotlinQuickFixAction +import org.jetbrains.kotlin.idea.codeinsight.api.applicable.intentions.KotlinPsiUpdateModCommandAction import org.jetbrains.kotlin.idea.project.builtIns import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.psi.* @@ -21,21 +24,30 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.types.KotlinType -class ChangeToMutableCollectionFix(property: KtProperty, private val type: String) : KotlinQuickFixAction(property) { - override fun getText() = KotlinBundle.message("fix.change.to.mutable.type.text", "Mutable$type") +class ChangeToMutableCollectionFix(property: KtProperty, context: ElementContext) : + KotlinPsiUpdateModCommandAction.ElementBased(property, context) { - override fun getFamilyName() = text + class ElementContext( + val typeName: String, + ) - override fun invoke(project: Project, editor: Editor?, file: KtFile) { - val property = element ?: return + override fun getPresentation(context: ActionContext, element: KtProperty): Presentation { + val elementContext = getElementContext(context, element) + return Presentation.of(KotlinBundle.message("fix.change.to.mutable.type.text", "Mutable${elementContext.typeName}")) + } + + override fun getFamilyName(): @IntentionFamilyName String = KotlinBundle.message("fix.change.to.mutable.type.family") + + override fun invoke(actionContext: ActionContext, element: KtProperty, elementContext: ElementContext, updater: ModPsiUpdater) { + val property = element val context = property.analyze(BodyResolveMode.PARTIAL) val type = property.initializer?.getType(context) ?: return applyFix(property, type) - editor?.caretModel?.moveToOffset(property.endOffset) + updater.moveCaretTo(property.endOffset) } companion object : KotlinSingleIntentionActionFactory() { - override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction? { + override fun createAction(diagnostic: Diagnostic): IntentionAction? { val element = Errors.NO_SET_METHOD.cast(diagnostic).psiElement as? KtArrayAccessExpression ?: return null val arrayExpr = element.arrayExpression ?: return null val context = arrayExpr.analyze(BodyResolveMode.PARTIAL) @@ -44,7 +56,7 @@ class ChangeToMutableCollectionFix(property: KtProperty, private val type: Strin val property = arrayExpr.mainReference?.resolve() as? KtProperty ?: return null if (!isApplicable(property)) return null val typeName = type.constructor.declarationDescriptor?.name?.asString() ?: return null - return ChangeToMutableCollectionFix(property, typeName) + return ChangeToMutableCollectionFix(property, ElementContext(typeName)).asIntention() } private fun KotlinType.isReadOnlyListOrMap(builtIns: KotlinBuiltIns): Boolean { diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/suspiciousCollectionReassignment/simple.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/suspiciousCollectionReassignment/simple.kt.after index fc52caae367c..fc4ddec41823 100644 --- a/plugins/kotlin/idea/tests/testData/inspectionsLocal/suspiciousCollectionReassignment/simple.kt.after +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/suspiciousCollectionReassignment/simple.kt.after @@ -3,6 +3,6 @@ // WITH_STDLIB fun test() { val list = mutableListOf(1) - list += 2 + list += 2 } // OFFLINE_REPORT: "'+=' on a read-only list creates a new list under the hood" \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/quickfix/allowResolveInWriteAction.txt b/plugins/kotlin/idea/tests/testData/quickfix/allowResolveInWriteAction.txt index a57413fd856f..074159a058d2 100644 --- a/plugins/kotlin/idea/tests/testData/quickfix/allowResolveInWriteAction.txt +++ b/plugins/kotlin/idea/tests/testData/quickfix/allowResolveInWriteAction.txt @@ -9,8 +9,6 @@ org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.IntentionBasedInsp org.jetbrains.kotlin.idea.codeInsight.inspections.shared.KotlinOptimizeImportsQuickFix org.jetbrains.kotlin.idea.inspections.migration.ObsoleteCoroutinesDelegateQuickFix org.jetbrains.kotlin.idea.inspections.SafeDeleteFix -org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection$ChangeTypeToMutableFix -org.jetbrains.kotlin.idea.inspections.SuspiciousCollectionReassignmentInspection$JoinWithInitializerFix org.jetbrains.kotlin.idea.inspections.UnusedReceiverParameterInspection$RemoveReceiverFix org.jetbrains.kotlin.idea.intentions.AddPropertyAccessorsIntention org.jetbrains.kotlin.idea.intentions.AddPropertyGetterIntention @@ -37,7 +35,6 @@ org.jetbrains.kotlin.idea.quickfix.ChangeFunctionLiteralSignatureFix org.jetbrains.kotlin.idea.quickfix.ChangeFunctionLiteralReturnTypeFix org.jetbrains.kotlin.idea.quickfix.ChangeFunctionSignatureFix$Companion$RemoveParameterFix org.jetbrains.kotlin.idea.quickfix.ChangeParameterTypeFix -org.jetbrains.kotlin.idea.quickfix.ChangeToMutableCollectionFix org.jetbrains.kotlin.idea.quickfix.ChangeVariableTypeFix org.jetbrains.kotlin.idea.quickfix.ChangeVariableTypeFix$ForOverridden org.jetbrains.kotlin.idea.quickfix.ChangeVisibilityFix$ChangeToInternalFix