diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/core/overrideImplement/KtImplementMembersHandler.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/core/overrideImplement/KtImplementMembersHandler.kt index fb1cf9e8750..9796f5be71a 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/core/overrideImplement/KtImplementMembersHandler.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/core/overrideImplement/KtImplementMembersHandler.kt @@ -12,7 +12,7 @@ import com.intellij.psi.PsiFile import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.idea.core.overrideImplement.KtImplementMembersHandler.Companion.getUnimplementedMembers import org.jetbrains.kotlin.idea.core.util.KotlinIdeaCoreBundle -import org.jetbrains.kotlin.idea.fir.api.fixes.diagnosticFixFactory +import org.jetbrains.kotlin.idea.fir.api.fixes.diagnosticFixFactoryFromIntentionActions import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession import org.jetbrains.kotlin.idea.frontend.api.analyse import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KtFirDiagnostic @@ -120,21 +120,25 @@ internal class KtImplementAsConstructorParameterQuickfix(private val members: Co object MemberNotImplementedQuickfixFactories { - val abstractMemberNotImplemented = diagnosticFixFactory(KtFirDiagnostic.AbstractMemberNotImplemented::class) { diagnostic -> - getUnimplementedMemberFixes(diagnostic.psi) - } + val abstractMemberNotImplemented = + diagnosticFixFactoryFromIntentionActions(KtFirDiagnostic.AbstractMemberNotImplemented::class) { diagnostic -> + getUnimplementedMemberFixes(diagnostic.psi) + } - val abstractClassMemberNotImplemented = diagnosticFixFactory(KtFirDiagnostic.AbstractClassMemberNotImplemented::class) { diagnostic -> - getUnimplementedMemberFixes(diagnostic.psi) - } + val abstractClassMemberNotImplemented = + diagnosticFixFactoryFromIntentionActions(KtFirDiagnostic.AbstractClassMemberNotImplemented::class) { diagnostic -> + getUnimplementedMemberFixes(diagnostic.psi) + } - val manyInterfacesMemberNotImplemented = diagnosticFixFactory(KtFirDiagnostic.ManyInterfacesMemberNotImplemented::class) { diagnostic -> - getUnimplementedMemberFixes(diagnostic.psi) - } + val manyInterfacesMemberNotImplemented = + diagnosticFixFactoryFromIntentionActions(KtFirDiagnostic.ManyInterfacesMemberNotImplemented::class) { diagnostic -> + getUnimplementedMemberFixes(diagnostic.psi) + } - val manyImplMemberNotImplemented = diagnosticFixFactory(KtFirDiagnostic.ManyImplMemberNotImplemented::class) { diagnostic -> - getUnimplementedMemberFixes(diagnostic.psi, false) - } + val manyImplMemberNotImplemented = + diagnosticFixFactoryFromIntentionActions(KtFirDiagnostic.ManyImplMemberNotImplemented::class) { diagnostic -> + getUnimplementedMemberFixes(diagnostic.psi, false) + } @OptIn(ExperimentalStdlibApi::class) private fun KtAnalysisSession.getUnimplementedMemberFixes( diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/AbstractHLIntention.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/AbstractHLIntention.kt index 1c75d41ef05..cfaa207d449 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/AbstractHLIntention.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/AbstractHLIntention.kt @@ -20,7 +20,8 @@ import kotlin.reflect.KClass abstract class AbstractHLIntention( elementType: KClass, -) : SelfTargetingIntention(elementType.java, { "" }, { "" }) { + val applicator: HLApplicator +) : SelfTargetingIntention(elementType.java, applicator::getFamilyName) { final override fun isApplicableTo(element: PSI, caretOffset: Int): Boolean { val project = element.project// TODO expensive operation, may require traversing the tree up to containing PsiFile if (!applicator.isApplicableByPsi(element, project)) return false @@ -61,6 +62,5 @@ abstract class AbstractHLIntention( } abstract val applicabilityRange: HLApplicabilityRange - abstract val applicator: HLApplicator abstract val inputProvider: HLApplicatorInputProvider } diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/fixes/HLDiagnosticFixFactory.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/fixes/HLDiagnosticFixFactory.kt index a955d8f484e..00453e67f43 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/fixes/HLDiagnosticFixFactory.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/fixes/HLDiagnosticFixFactory.kt @@ -6,15 +6,19 @@ package org.jetbrains.kotlin.idea.fir.api.fixes import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile import org.jetbrains.kotlin.idea.api.applicator.HLApplicator import org.jetbrains.kotlin.idea.api.applicator.HLApplicatorInput import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession import org.jetbrains.kotlin.idea.frontend.api.diagnostics.KtDiagnosticWithPsi +import org.jetbrains.kotlin.idea.quickfix.QuickFixActionBase import kotlin.reflect.KClass sealed class HLDiagnosticFixFactory> { - abstract fun KtAnalysisSession.createQuickFixes(diagnostic: DIAGNOSTIC): List + abstract fun KtAnalysisSession.createQuickFixes(diagnostic: DIAGNOSTIC): List> abstract val diagnosticClass: KClass } @@ -27,11 +31,11 @@ private class HLDiagnosticFixFactoryWithFixedApplicator HLQuickFix(target, input, applicator) } } -private class HLDiagnosticFixFactoryWithVariableApplicator>( +private class HLDiagnosticFixFactoryUsingQuickFixActionBase>( override val diagnosticClass: KClass, - private val createQuickFixes: KtAnalysisSession.(DIAGNOSTIC) -> List + private val createQuickFixes: KtAnalysisSession.(DIAGNOSTIC) -> List> ) : HLDiagnosticFixFactory() { - override fun KtAnalysisSession.createQuickFixes(diagnostic: DIAGNOSTIC): List = + override fun KtAnalysisSession.createQuickFixes(diagnostic: DIAGNOSTIC): List> = createQuickFixes.invoke(this, diagnostic) } @@ -52,19 +56,42 @@ fun , TARGET_PSI : PsiElement, INPUT : HLApp HLDiagnosticFixFactoryWithFixedApplicator(diagnosticClass, applicator, createTargets) /** - * Returns a [HLDiagnosticFixFactory] that creates [HLQuickFix]es from a diagnostic. + * Returns a [HLDiagnosticFixFactory] that creates [QuickFixActionBase]s from a diagnostic. */ fun > diagnosticFixFactory( diagnosticClass: KClass, - createQuickFixes: KtAnalysisSession.(DIAGNOSTIC) -> List + createQuickFixes: KtAnalysisSession.(DIAGNOSTIC) -> List> ): HLDiagnosticFixFactory = - HLDiagnosticFixFactoryWithVariableApplicator(diagnosticClass, createQuickFixes) + HLDiagnosticFixFactoryUsingQuickFixActionBase(diagnosticClass, createQuickFixes) /** - * Returns a [Collection] of [HLDiagnosticFixFactory] that creates [HLQuickFix]es from diagnostics that have the same type of [PsiElement]. + * Returns a [Collection] of [HLDiagnosticFixFactory] that creates [QuickFixActionBase]s from diagnostics that have the same type of + * [PsiElement]. */ fun > diagnosticFixFactories( vararg diagnosticClasses: KClass, - createQuickFixes: KtAnalysisSession.(DIAGNOSTIC) -> List + createQuickFixes: KtAnalysisSession.(DIAGNOSTIC) -> List> ): Collection> = - diagnosticClasses.map { HLDiagnosticFixFactoryWithVariableApplicator(it, createQuickFixes) } + diagnosticClasses.map { HLDiagnosticFixFactoryUsingQuickFixActionBase(it, createQuickFixes) } + +/** + * Returns a [HLDiagnosticFixFactory] that creates [IntentionAction]s from a diagnostic. + */ +fun > diagnosticFixFactoryFromIntentionActions( + diagnosticClass: KClass, + createIntentionActions: KtAnalysisSession.(DIAGNOSTIC) -> List +): HLDiagnosticFixFactory { + // Wrap the IntentionActions as QuickFixActionBase. This ensures all fixes are of type QuickFixActionBase. + val createQuickFixes: KtAnalysisSession.(DIAGNOSTIC) -> List> = { diagnostic -> + val intentionActions = createIntentionActions.invoke(this, diagnostic) + intentionActions.map { IntentionActionAsQuickFixWrapper(it, diagnostic.psi) } + } + return HLDiagnosticFixFactoryUsingQuickFixActionBase(diagnosticClass, createQuickFixes) +} + +private class IntentionActionAsQuickFixWrapper(val intentionAction: IntentionAction, element: T) : + QuickFixActionBase(element) { + override fun getText(): String = intentionAction.text + override fun getFamilyName(): String = intentionAction.familyName + override fun invoke(project: Project, editor: Editor?, file: PsiFile?) = intentionAction.invoke(project, editor, file) +} diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/intentions/HLAddAccessorIntentions.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/intentions/HLAddAccessorIntentions.kt index 56c4ad03304..8455e9e162a 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/intentions/HLAddAccessorIntentions.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/intentions/HLAddAccessorIntentions.kt @@ -25,18 +25,11 @@ import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier abstract class HLAddAccessorIntention(private val addGetter: Boolean, private val addSetter: Boolean) : - AbstractHLIntention(KtProperty::class) { + AbstractHLIntention(KtProperty::class, applicator(addGetter, addSetter)) { override val applicabilityRange: HLApplicabilityRange = applicabilityTarget { ktProperty -> if (ktProperty.hasInitializer()) ktProperty.nameIdentifier else ktProperty } - override val applicator: HLApplicator = applicator { - applyTo { ktProperty, _, _, editor -> - AbstractAddAccessorsIntention.applyTo(ktProperty, editor, addGetter, addSetter) - } - familyAndActionName(AbstractAddAccessorsIntention.createFamilyName(addGetter, addSetter)) - } - override val inputProvider: HLApplicatorInputProvider = inputProvider { ktProperty -> val symbol = ktProperty.getVariableSymbol() as? KtPropertySymbol ?: return@inputProvider null if (symbol.containsAnnotation(JVM_FIELD_CLASS_ID)) return@inputProvider null diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/intentions/declarations/HLSpecifyExplicitTypeForCallableDeclarationIntention.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/intentions/declarations/HLSpecifyExplicitTypeForCallableDeclarationIntention.kt index 61d7f475f5e..dfdd04c2fc0 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/intentions/declarations/HLSpecifyExplicitTypeForCallableDeclarationIntention.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/intentions/declarations/HLSpecifyExplicitTypeForCallableDeclarationIntention.kt @@ -6,8 +6,8 @@ package org.jetbrains.kotlin.idea.fir.intentions.declarations import org.jetbrains.kotlin.idea.KotlinBundle -import org.jetbrains.kotlin.idea.fir.api.* import org.jetbrains.kotlin.idea.api.applicator.with +import org.jetbrains.kotlin.idea.fir.api.* import org.jetbrains.kotlin.idea.fir.api.applicator.HLApplicabilityRange import org.jetbrains.kotlin.idea.fir.api.applicator.inputProvider import org.jetbrains.kotlin.idea.fir.applicators.ApplicabilityRanges @@ -15,16 +15,7 @@ import org.jetbrains.kotlin.idea.fir.applicators.CallableReturnTypeUpdaterApplic import org.jetbrains.kotlin.psi.* class HLSpecifyExplicitTypeForCallableDeclarationIntention : - AbstractHLIntention( - KtCallableDeclaration::class - ) { - override val applicator = CallableReturnTypeUpdaterApplicator.applicator.with { - isApplicableByPsi { declaration: KtCallableDeclaration -> - if (declaration is KtConstructor<*> || declaration is KtFunctionLiteral) return@isApplicableByPsi false - declaration.typeReference == null && (declaration as? KtNamedFunction)?.hasBlockBody() != true - } - familyAndActionName(KotlinBundle.lazyMessage("specify.return.type.explicitly")) - } + AbstractHLIntention(KtCallableDeclaration::class, applicator) { override val applicabilityRange: HLApplicabilityRange = ApplicabilityRanges.SELF override val inputProvider = inputProvider { declaration -> @@ -32,4 +23,14 @@ class HLSpecifyExplicitTypeForCallableDeclarationIntention : val denotableType = returnType.approximateToSuperPublicDenotable() ?: returnType with(CallableReturnTypeUpdaterApplicator.Type) { createByKtType(denotableType) } } + + companion object { + private val applicator = CallableReturnTypeUpdaterApplicator.applicator.with { + isApplicableByPsi { declaration: KtCallableDeclaration -> + if (declaration is KtConstructor<*> || declaration is KtFunctionLiteral) return@isApplicableByPsi false + declaration.typeReference == null && (declaration as? KtNamedFunction)?.hasBlockBody() != true + } + familyAndActionName(KotlinBundle.lazyMessage("specify.return.type.explicitly")) + } + } } \ No newline at end of file diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/AddAccessorsFactories.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/AddAccessorsFactories.kt index a1f29cdd2d2..e8273f19973 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/AddAccessorsFactories.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/AddAccessorsFactories.kt @@ -5,7 +5,7 @@ package org.jetbrains.kotlin.idea.quickfix.fixes -import org.jetbrains.kotlin.idea.fir.api.fixes.diagnosticFixFactory +import org.jetbrains.kotlin.idea.fir.api.fixes.diagnosticFixFactoryFromIntentionActions import org.jetbrains.kotlin.idea.fir.intentions.HLAddGetterAndSetterIntention import org.jetbrains.kotlin.idea.fir.intentions.HLAddGetterIntention import org.jetbrains.kotlin.idea.fir.intentions.HLAddSetterIntention @@ -13,15 +13,16 @@ import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KtFirDiagnostic import org.jetbrains.kotlin.psi.KtProperty object AddAccessorsFactories { - val addAccessorsToUninitializedProperty = diagnosticFixFactory(KtFirDiagnostic.MustBeInitializedOrBeAbstract::class) { diagnostic -> - val property: KtProperty = diagnostic.psi - val addGetter = property.getter == null - val addSetter = property.isVar && property.setter == null - when { - addGetter && addSetter -> listOf(HLAddGetterAndSetterIntention()) - addGetter -> listOf(HLAddGetterIntention()) - addSetter -> listOf(HLAddSetterIntention()) - else -> emptyList() + val addAccessorsToUninitializedProperty = + diagnosticFixFactoryFromIntentionActions(KtFirDiagnostic.MustBeInitializedOrBeAbstract::class) { diagnostic -> + val property: KtProperty = diagnostic.psi + val addGetter = property.getter == null + val addSetter = property.isVar && property.setter == null + when { + addGetter && addSetter -> listOf(HLAddGetterAndSetterIntention()) + addGetter -> listOf(HLAddGetterIntention()) + addSetter -> listOf(HLAddSetterIntention()) + else -> emptyList() + } } - } } \ No newline at end of file diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/AddExclExclCallFixFactories.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/AddExclExclCallFixFactories.kt index b471fa0bdd1..ed3eb11eb02 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/AddExclExclCallFixFactories.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/AddExclExclCallFixFactories.kt @@ -5,7 +5,6 @@ package org.jetbrains.kotlin.idea.quickfix.fixes -import com.intellij.codeInsight.intention.IntentionAction import com.intellij.psi.PsiElement import org.jetbrains.kotlin.idea.fir.api.fixes.diagnosticFixFactory import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession @@ -30,7 +29,7 @@ object AddExclExclCallFixFactories { getFixForUnsafeCall(diagnostic.psi) } - private fun KtAnalysisSession.getFixForUnsafeCall(psi: PsiElement): List { + private fun KtAnalysisSession.getFixForUnsafeCall(psi: PsiElement): List { val (target, hasImplicitReceiver) = when (val unwrapped = psi.unwrapParenthesesLabelsAndAnnotations()) { // `foo.bar` -> `foo!!.bar` is KtDotQualifiedExpression -> unwrapped.receiverExpression to false diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/TypeMismatchFactories.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/TypeMismatchFactories.kt index accdde3180d..a68bd796fa4 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/TypeMismatchFactories.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/TypeMismatchFactories.kt @@ -5,13 +5,13 @@ package org.jetbrains.kotlin.idea.quickfix.fixes -import com.intellij.codeInsight.intention.IntentionAction import com.intellij.psi.PsiElement import org.jetbrains.kotlin.idea.fir.api.fixes.diagnosticFixFactory import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KtFirDiagnostic import org.jetbrains.kotlin.idea.frontend.api.types.KtType import org.jetbrains.kotlin.idea.frontend.api.types.KtTypeNullability +import org.jetbrains.kotlin.idea.quickfix.AddExclExclCallFix import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.utils.addToStdlib.safeAs @@ -36,7 +36,7 @@ object TypeMismatchFactories { psi: PsiElement, expectedType: KtType, actualType: KtType - ): List { + ): List { // TODO: Add more fixes than just AddExclExclCallFix when available. if (!expectedType.canBeNull && actualType.canBeNull) { // We don't want to offer AddExclExclCallFix if we know the expression is definitely null, e.g.: diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/WrapWithSafeLetCallFixFactories.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/WrapWithSafeLetCallFixFactories.kt index 919cff115c6..51aa197bce2 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/WrapWithSafeLetCallFixFactories.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/WrapWithSafeLetCallFixFactories.kt @@ -5,7 +5,6 @@ package org.jetbrains.kotlin.idea.quickfix.fixes -import com.intellij.codeInsight.intention.IntentionAction import com.intellij.openapi.diagnostic.Logger import com.intellij.psi.PsiElement import com.intellij.psi.SmartPsiElementPointer @@ -180,7 +179,7 @@ object WrapWithSafeLetCallFixFactories { private fun KtAnalysisSession.createWrapWithSafeLetCallInputForNullableExpressionIfMoreThanImmediateParentIsWrapped( nullableExpression: KtExpression?, isImplicitInvokeCallToMemberProperty: Boolean = false, - ): List { + ): List> { val surroundingExpression = nullableExpression?.surroundingExpression if ( surroundingExpression == null || @@ -205,7 +204,7 @@ object WrapWithSafeLetCallFixFactories { isImplicitInvokeCallToMemberProperty: Boolean = false, surroundingExpression: KtExpression? = findParentExpressionAtNullablePosition(nullableExpression) ?: nullableExpression?.surroundingExpression - ): List { + ): List> { if (nullableExpression == null || surroundingExpression == null) return emptyList() val existingNames = nullableExpression.containingKtFile.getScopeContextForPosition(nullableExpression).scopes.getPossibleCallableNames()