diff --git a/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/quickfix/AddExclExclCallFix.kt b/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/quickfix/AddExclExclCallFix.kt new file mode 100644 index 00000000000..0211e1e6c39 --- /dev/null +++ b/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/quickfix/AddExclExclCallFix.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.quickfix + +import com.intellij.codeInsight.FileModificationService +import com.intellij.codeInsight.intention.LowPriorityAction +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.idea.KotlinBundle +import org.jetbrains.kotlin.psi.KtCallableReferenceExpression +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtPsiFactory +import org.jetbrains.kotlin.psi.createExpressionByPattern + +class AddExclExclCallFix(psiElement: PsiElement, val fixImplicitReceiver: Boolean = false) : ExclExclCallFix(psiElement), + LowPriorityAction { + + override fun getText() = KotlinBundle.message("fix.introduce.non.null.assertion") + + override fun invoke(project: Project, editor: Editor?, file: KtFile) { + if (!FileModificationService.getInstance().prepareFileForWrite(file)) return + + val modifiedExpression = element ?: return + val psiFactory = KtPsiFactory(project) + val exclExclExpression = if (fixImplicitReceiver) { + if (modifiedExpression is KtCallableReferenceExpression) { + psiFactory.createExpressionByPattern("this!!::$0", modifiedExpression.callableReference) + } else { + psiFactory.createExpressionByPattern("this!!.$0", modifiedExpression) + } + } else { + psiFactory.createExpressionByPattern("$0!!", modifiedExpression) + } + modifiedExpression.replace(exclExclExpression) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/HasPlatformTypeInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/HasPlatformTypeInspection.kt index 8667ab190ae..bb463d5e9a5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/HasPlatformTypeInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/HasPlatformTypeInspection.kt @@ -22,7 +22,7 @@ import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention import org.jetbrains.kotlin.idea.intentions.isFlexibleRecursive -import org.jetbrains.kotlin.idea.quickfix.AddExclExclCallFix +import org.jetbrains.kotlin.idea.quickfix.getAddExclExclCallFix import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtCallableDeclaration import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments @@ -58,7 +58,7 @@ class HasPlatformTypeInspection( if (expression != null && (!reportPlatformArguments || !TypeUtils.makeNotNullable(type).isFlexibleRecursive()) ) { - return listOf(IntentionWrapper(AddExclExclCallFix(expression), element.containingFile)) + return listOfNotNull(getAddExclExclCallFix(expression)?.let { IntentionWrapper(it, element.containingFile) }) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/PlatformExtensionReceiverOfInlineInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/PlatformExtensionReceiverOfInlineInspection.kt index 2b0436ec83f..2232af4dc9a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/PlatformExtensionReceiverOfInlineInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/PlatformExtensionReceiverOfInlineInspection.kt @@ -20,7 +20,7 @@ import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.project.languageVersionSettings -import org.jetbrains.kotlin.idea.quickfix.AddExclExclCallFix +import org.jetbrains.kotlin.idea.quickfix.getAddExclExclCallFix import org.jetbrains.kotlin.idea.resolve.getDataFlowValueFactory import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtDotQualifiedExpression @@ -78,12 +78,14 @@ class PlatformExtensionReceiverOfInlineInspection : AbstractKotlinInspection() { val stableNullability = context.getDataFlowInfoBefore(receiverExpression).getStableNullability(dataFlow) if (!stableNullability.canBeNull()) return - holder.registerProblem( - receiverExpression, - KotlinBundle.message("call.of.inline.function.with.nullable.extension.receiver.can.provoke.npe.in.kotlin.1.2"), - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, - IntentionWrapper(AddExclExclCallFix(receiverExpression), receiverExpression.containingKtFile) - ) + getAddExclExclCallFix(receiverExpression)?.let { + holder.registerProblem( + receiverExpression, + KotlinBundle.message("call.of.inline.function.with.nullable.extension.receiver.can.provoke.npe.in.kotlin.1.2"), + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + IntentionWrapper(it, receiverExpression.containingKtFile) + ) + } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddExclExclCallFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddExclExclCallFix.kt index dcd92b08ef6..c7a775d893b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddExclExclCallFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddExclExclCallFix.kt @@ -5,11 +5,7 @@ package org.jetbrains.kotlin.idea.quickfix -import com.intellij.codeInsight.FileModificationService import com.intellij.codeInsight.intention.IntentionAction -import com.intellij.codeInsight.intention.LowPriorityAction -import com.intellij.openapi.editor.Editor -import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement import com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.descriptors.ClassDescriptor @@ -18,7 +14,6 @@ import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors.UNSAFE_CALL -import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade @@ -38,91 +33,62 @@ import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.isValidOperator import org.jetbrains.kotlin.utils.addToStdlib.safeAs -class AddExclExclCallFix(psiElement: PsiElement, val checkImplicitReceivers: Boolean = true) : ExclExclCallFix(psiElement), - LowPriorityAction { +fun getAddExclExclCallFix(element: PsiElement?, checkImplicitReceivers: Boolean = false): AddExclExclCallFix? { + fun KtExpression?.asFix(implicitReceiver: Boolean = false) = this?.let { AddExclExclCallFix(it, implicitReceiver) } - override fun getText() = KotlinBundle.message("fix.introduce.non.null.assertion") - - override fun isAvailable(project: Project, editor: Editor?, file: KtFile): Boolean = - getExpressionForIntroduceCall() != null - - override fun invoke(project: Project, editor: Editor?, file: KtFile) { - if (!FileModificationService.getInstance().prepareFileForWrite(file)) return - - val expr = getExpressionForIntroduceCall() ?: return - val modifiedExpression = expr.expression - val psiFactory = KtPsiFactory(project) - val exclExclExpression = if (expr.implicitReceiver) { - if (modifiedExpression is KtCallableReferenceExpression) { - psiFactory.createExpressionByPattern("this!!::$0", modifiedExpression.callableReference) + val psiElement = element ?: return null + if ((psiElement as? KtExpression).isNullExpression()) { + return null + } + if (psiElement is LeafPsiElement && psiElement.elementType == KtTokens.DOT) { + return (psiElement.prevSibling as? KtExpression).asFix() + } + return when (psiElement) { + is KtArrayAccessExpression -> psiElement.asFix() + is KtOperationReferenceExpression -> { + when (val parent = psiElement.parent) { + is KtUnaryExpression -> parent.baseExpression.asFix() + is KtBinaryExpression -> { + val receiver = if (KtPsiUtil.isInOrNotInOperation(parent)) parent.right else parent.left + receiver.asFix() + } + else -> null + } + } + is KtExpression -> { + val parent = psiElement.parent + val context = psiElement.analyze() + if (checkImplicitReceivers && psiElement.getResolvedCall(context)?.getImplicitReceiverValue() is ExtensionReceiver) { + val expressionToReplace = parent as? KtCallExpression ?: parent as? KtCallableReferenceExpression ?: psiElement + expressionToReplace.asFix(implicitReceiver = true) } else { - psiFactory.createExpressionByPattern("this!!.$0", modifiedExpression) - } - } else { - psiFactory.createExpressionByPattern("$0!!", modifiedExpression) - } - modifiedExpression.replace(exclExclExpression) - } + val targetElement = parent.safeAs()?.receiverExpression ?: psiElement + context[BindingContext.EXPRESSION_TYPE_INFO, targetElement]?.let { + val type = it.type - private class ExpressionForCall(val expression: KtExpression, val implicitReceiver: Boolean) + val dataFlowValueFactory = targetElement.getResolutionFacade().getDataFlowValueFactory() - private fun KtExpression?.expressionForCall(implicitReceiver: Boolean = false) = this?.let { ExpressionForCall(it, implicitReceiver) } - - private fun getExpressionForIntroduceCall(): ExpressionForCall? { - val psiElement = element ?: return null - if ((psiElement as? KtExpression).isNullExpression()) { - return null - } - if (psiElement is LeafPsiElement && psiElement.elementType == KtTokens.DOT) { - return (psiElement.prevSibling as? KtExpression).expressionForCall() - } - return when (psiElement) { - is KtArrayAccessExpression -> psiElement.expressionForCall() - is KtOperationReferenceExpression -> { - when (val parent = psiElement.parent) { - is KtUnaryExpression -> parent.baseExpression.expressionForCall() - is KtBinaryExpression -> { - val receiver = if (KtPsiUtil.isInOrNotInOperation(parent)) parent.right else parent.left - receiver.expressionForCall() + if (type != null) { + val nullability = it.dataFlowInfo.getStableNullability( + dataFlowValueFactory.createDataFlowValue(targetElement, type, context, targetElement.findModuleDescriptor()) + ) + if (!nullability.canBeNonNull()) return null } - else -> null } + targetElement.asFix() } - is KtExpression -> { - val parent = psiElement.parent - val context = psiElement.analyze() - if (checkImplicitReceivers && psiElement.getResolvedCall(context)?.getImplicitReceiverValue() is ExtensionReceiver) { - val expressionToReplace = parent as? KtCallExpression ?: parent as? KtCallableReferenceExpression ?: psiElement - expressionToReplace.expressionForCall(implicitReceiver = true) - } else { - val targetElement = parent.safeAs()?.receiverExpression ?: psiElement - context[BindingContext.EXPRESSION_TYPE_INFO, targetElement]?.let { - val type = it.type - - val dataFlowValueFactory = targetElement.getResolutionFacade().getDataFlowValueFactory() - - if (type != null) { - val nullability = it.dataFlowInfo.getStableNullability( - dataFlowValueFactory.createDataFlowValue(targetElement, type, context, targetElement.findModuleDescriptor()) - ) - if (!nullability.canBeNonNull()) return null - } - } - targetElement.expressionForCall() - } - } - else -> null } + else -> null } +} - companion object : KotlinSingleIntentionActionFactory() { - override fun createAction(diagnostic: Diagnostic): IntentionAction { - val psiElement = diagnostic.psiElement - if (diagnostic.factory == UNSAFE_CALL && psiElement is KtArrayAccessExpression) { - psiElement.arrayExpression?.let { return AddExclExclCallFix(it) } - } - return AddExclExclCallFix(psiElement) +object UnsafeCallExclExclFixFactory : KotlinSingleIntentionActionFactory() { + override fun createAction(diagnostic: Diagnostic): IntentionAction? { + val psiElement = diagnostic.psiElement + if (diagnostic.factory == UNSAFE_CALL && psiElement is KtArrayAccessExpression) { + psiElement.arrayExpression?.let { return getAddExclExclCallFix(it) } } + return getAddExclExclCallFix(psiElement, checkImplicitReceivers = true) } } @@ -141,7 +107,7 @@ object SmartCastImpossibleExclExclFixFactory : KotlinSingleIntentionActionFactor val nullableExpectedType = TypeUtils.makeNullable(expectedType) if (!type.isSubtypeOf(nullableExpectedType)) return null - return AddExclExclCallFix(element, checkImplicitReceivers = false) + return getAddExclExclCallFix(element) } } @@ -174,6 +140,6 @@ object MissingIteratorExclExclFixFactory : KotlinSingleIntentionActionFactory() else -> return null } - return AddExclExclCallFix(element, checkImplicitReceivers = false) + return getAddExclExclCallFix(element) } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt index eca380ad32c..a479cda2ea3 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt @@ -166,7 +166,7 @@ class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() { val nullableExpected = expectedType.makeNullable() if (expressionType.isSubtypeOf(nullableExpected)) { val targetExpression = diagnosticElement.getTopMostQualifiedForSelectorIfAny() - actions.add(AddExclExclCallFix(targetExpression, checkImplicitReceivers = false)) + getAddExclExclCallFix(targetExpression)?.let { actions.add(it) } if (expectedType.isBoolean()) { actions.add(AddEqEqTrueFix(targetExpression)) } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index 08bd80e0791..7ddddf51db6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -262,9 +262,9 @@ class QuickFixRegistrar : QuickFixContributor { UNSAFE_OPERATOR_CALL.registerFactory(WrapWithSafeLetCallFix.UnsafeFactory) TYPE_MISMATCH.registerFactory(WrapWithSafeLetCallFix.TypeMismatchFactory) - UNSAFE_CALL.registerFactory(AddExclExclCallFix) - UNSAFE_INFIX_CALL.registerFactory(AddExclExclCallFix) - UNSAFE_OPERATOR_CALL.registerFactory(AddExclExclCallFix) + UNSAFE_CALL.registerFactory(UnsafeCallExclExclFixFactory) + UNSAFE_INFIX_CALL.registerFactory(UnsafeCallExclExclFixFactory) + UNSAFE_OPERATOR_CALL.registerFactory(UnsafeCallExclExclFixFactory) UNNECESSARY_NOT_NULL_ASSERTION.registerFactory(RemoveExclExclCallFix) UNSAFE_INFIX_CALL.registerFactory(ReplaceInfixOrOperatorCallFix) UNSAFE_OPERATOR_CALL.registerFactory(ReplaceInfixOrOperatorCallFix) diff --git a/idea/testData/quickfix/allowResolveInWriteAction.txt b/idea/testData/quickfix/allowResolveInWriteAction.txt index cf806580272..54425449711 100644 --- a/idea/testData/quickfix/allowResolveInWriteAction.txt +++ b/idea/testData/quickfix/allowResolveInWriteAction.txt @@ -20,9 +20,7 @@ org.jetbrains.kotlin.idea.intentions.MoveMemberToCompanionObjectIntention org.jetbrains.kotlin.idea.intentions.MovePropertyToConstructorIntention org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention org.jetbrains.kotlin.idea.quickfix.AddAnnotationTargetFix -org.jetbrains.kotlin.idea.quickfix.AddExclExclCallFix org.jetbrains.kotlin.idea.quickfix.AddFunctionToSupertypeFix -org.jetbrains.kotlin.idea.quickfix.AddModifierFix org.jetbrains.kotlin.idea.quickfix.AddNameToArgumentFix org.jetbrains.kotlin.idea.quickfix.AddPropertyToSupertypeFix org.jetbrains.kotlin.idea.quickfix.AddStartProjectionsForInnerClass diff --git a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/J2kPostProcessor.kt b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/J2kPostProcessor.kt index a8c0edf099b..5d93b34c8c8 100644 --- a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/J2kPostProcessor.kt +++ b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/J2kPostProcessor.kt @@ -101,13 +101,13 @@ private val errorsFixingDiagnosticBasedPostProcessingGroup = }, diagnosticBasedProcessing( - addExclExclFactoryNoImplicitReceiver(AddExclExclCallFix), + UnsafeCallExclExclFixFactory, Errors.UNSAFE_CALL, Errors.UNSAFE_INFIX_CALL, Errors.UNSAFE_OPERATOR_CALL ), diagnosticBasedProcessing( - addExclExclFactoryNoImplicitReceiver(MissingIteratorExclExclFixFactory), + MissingIteratorExclExclFixFactory, Errors.ITERATOR_ON_NULLABLE ), diagnosticBasedProcessing( diff --git a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/diagnosticBasedPostProcessing.kt b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/diagnosticBasedPostProcessing.kt index 6b532712d0d..7e51d25e2fb 100644 --- a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/diagnosticBasedPostProcessing.kt +++ b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/diagnosticBasedPostProcessing.kt @@ -5,15 +5,12 @@ package org.jetbrains.kotlin.nj2k.postProcessing -import com.intellij.codeInsight.intention.IntentionAction import com.intellij.openapi.editor.RangeMarker import com.intellij.psi.PsiElement import org.jetbrains.kotlin.caches.resolve.KotlinCacheService import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.DiagnosticFactory import org.jetbrains.kotlin.idea.core.util.range -import org.jetbrains.kotlin.idea.quickfix.AddExclExclCallFix -import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory import org.jetbrains.kotlin.idea.quickfix.QuickFixFactory import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.util.application.runReadAction @@ -22,7 +19,6 @@ import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.psiUtil.elementsInRange import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics -import org.jetbrains.kotlin.utils.addToStdlib.safeAs class DiagnosticBasedPostProcessingGroup(diagnosticBasedProcessings: List) : FileBasedPostProcessing() { constructor(vararg diagnosticBasedProcessings: DiagnosticBasedProcessing) : this(diagnosticBasedProcessings.toList()) @@ -97,14 +93,3 @@ fun diagnosticBasedProcessing(fixFactory: QuickFixFactory, vararg diagnosticFact } } } - -fun addExclExclFactoryNoImplicitReceiver(initialFactory: KotlinSingleIntentionActionFactory) = - object : KotlinSingleIntentionActionFactory() { - override fun createAction(diagnostic: Diagnostic): IntentionAction? = - initialFactory.createActions(diagnostic).singleOrNull() - ?.safeAs() - ?.let { - AddExclExclCallFix(diagnostic.psiElement, checkImplicitReceivers = false) - } - } -