From bb2246ebaf20808329dc003bd0223541b44d24e3 Mon Sep 17 00:00:00 2001 From: Mark Punzalan Date: Sat, 5 Jun 2021 04:51:34 +0000 Subject: [PATCH] FIR IDE: Add diagnosticFixFactories() API to share quickfix-creation across multiple diagnostics with the same PSI. --- .../KtImplementMembersHandler.kt | 8 +++---- .../fir/api/fixes/HLDiagnosticFixFactory.kt | 23 ++++++++++++++---- .../idea/fir/api/fixes/KtQuickFixesList.kt | 24 +++++++++---------- .../quickfix/fixes/AddAccessorsFactories.kt | 2 +- .../fixes/AddExclExclCallFixFactories.kt | 8 +++---- .../idea/quickfix/fixes/AddLateInitFactory.kt | 2 +- .../fixes/ChangeTypeQuickFixFactories.kt | 8 +++---- .../idea/quickfix/fixes/ImportQuickFix.kt | 2 +- .../InitializePropertyQuickFixFactory.kt | 2 +- .../quickfix/fixes/ReplaceCallFixFactories.kt | 8 +++---- .../quickfix/fixes/TypeMismatchFactories.kt | 4 ++-- .../fixes/WrapWithSafeLetCallFixFactories.kt | 10 ++++---- 12 files changed, 57 insertions(+), 44 deletions(-) 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 ece783eef11..fb1cf9e8750 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 @@ -120,19 +120,19 @@ internal class KtImplementAsConstructorParameterQuickfix(private val members: Co object MemberNotImplementedQuickfixFactories { - val abstractMemberNotImplemented = diagnosticFixFactory { diagnostic -> + val abstractMemberNotImplemented = diagnosticFixFactory(KtFirDiagnostic.AbstractMemberNotImplemented::class) { diagnostic -> getUnimplementedMemberFixes(diagnostic.psi) } - val abstractClassMemberNotImplemented = diagnosticFixFactory { diagnostic -> + val abstractClassMemberNotImplemented = diagnosticFixFactory(KtFirDiagnostic.AbstractClassMemberNotImplemented::class) { diagnostic -> getUnimplementedMemberFixes(diagnostic.psi) } - val manyInterfacesMemberNotImplemented = diagnosticFixFactory { diagnostic -> + val manyInterfacesMemberNotImplemented = diagnosticFixFactory(KtFirDiagnostic.ManyInterfacesMemberNotImplemented::class) { diagnostic -> getUnimplementedMemberFixes(diagnostic.psi) } - val manyImplMemberNotImplemented = diagnosticFixFactory { diagnostic -> + val manyImplMemberNotImplemented = diagnosticFixFactory(KtFirDiagnostic.ManyImplMemberNotImplemented::class) { diagnostic -> getUnimplementedMemberFixes(diagnostic.psi, false) } 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 630ab0689f7..a955d8f484e 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 @@ -11,20 +11,24 @@ 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 kotlin.reflect.KClass -sealed class HLDiagnosticFixFactory> { +sealed class HLDiagnosticFixFactory> { abstract fun KtAnalysisSession.createQuickFixes(diagnostic: DIAGNOSTIC): List + abstract val diagnosticClass: KClass } private class HLDiagnosticFixFactoryWithFixedApplicator, TARGET_PSI : PsiElement, INPUT : HLApplicatorInput>( + override val diagnosticClass: KClass, private val applicator: HLApplicator, - private val createTargets: KtAnalysisSession.(DIAGNOSTIC) -> List> + private val createTargets: KtAnalysisSession.(DIAGNOSTIC) -> List>, ) : HLDiagnosticFixFactory() { override fun KtAnalysisSession.createQuickFixes(diagnostic: DIAGNOSTIC): List> = createTargets.invoke(this, diagnostic).map { (target, input) -> HLQuickFix(target, input, applicator) } } private class HLDiagnosticFixFactoryWithVariableApplicator>( + override val diagnosticClass: KClass, private val createQuickFixes: KtAnalysisSession.(DIAGNOSTIC) -> List ) : HLDiagnosticFixFactory() { override fun KtAnalysisSession.createQuickFixes(diagnostic: DIAGNOSTIC): List = @@ -41,15 +45,26 @@ internal fun > KtAnalysisSession.cr * The targets and inputs are consumed by the given applicator to apply fixes. */ fun , TARGET_PSI : PsiElement, INPUT : HLApplicatorInput> diagnosticFixFactory( + diagnosticClass: KClass, applicator: HLApplicator, createTargets: KtAnalysisSession.(DIAGNOSTIC) -> List> ): HLDiagnosticFixFactory = - HLDiagnosticFixFactoryWithFixedApplicator(applicator, createTargets) + HLDiagnosticFixFactoryWithFixedApplicator(diagnosticClass, applicator, createTargets) /** * Returns a [HLDiagnosticFixFactory] that creates [HLQuickFix]es from a diagnostic. */ fun > diagnosticFixFactory( + diagnosticClass: KClass, createQuickFixes: KtAnalysisSession.(DIAGNOSTIC) -> List ): HLDiagnosticFixFactory = - HLDiagnosticFixFactoryWithVariableApplicator(createQuickFixes) + HLDiagnosticFixFactoryWithVariableApplicator(diagnosticClass, createQuickFixes) + +/** + * Returns a [Collection] of [HLDiagnosticFixFactory] that creates [HLQuickFix]es from diagnostics that have the same type of [PsiElement]. + */ +fun > diagnosticFixFactories( + vararg diagnosticClasses: KClass, + createQuickFixes: KtAnalysisSession.(DIAGNOSTIC) -> List +): Collection> = + diagnosticClasses.map { HLDiagnosticFixFactoryWithVariableApplicator(it, createQuickFixes) } diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/fixes/KtQuickFixesList.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/fixes/KtQuickFixesList.kt index 9521317fdd2..7cc35e6e651 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/fixes/KtQuickFixesList.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/fixes/KtQuickFixesList.kt @@ -65,13 +65,6 @@ class KtQuickFixesListBuilder private constructor() { } } - @OptIn(PrivateForInline::class) - inline fun > registerApplicator( - quickFixFactory: HLDiagnosticFixFactory - ) { - registerApplicator(DIAGNOSTIC::class, quickFixFactory) - } - @PrivateForInline fun > registerPsiQuickFix( diagnosticClass: KClass, @@ -80,13 +73,18 @@ class KtQuickFixesListBuilder private constructor() { quickFixes.getOrPut(diagnosticClass) { mutableListOf() }.add(HLQuickFixFactory.HLQuickFixesPsiBasedFactory(quickFixFactory)) } - - @PrivateForInline - fun > registerApplicator( - diagnosticClass: KClass, - quickFixFactory: HLDiagnosticFixFactory + @OptIn(PrivateForInline::class) + fun > registerApplicators( + quickFixFactories: Collection> ) { - quickFixes.getOrPut(diagnosticClass) { mutableListOf() } + quickFixFactories.forEach(::registerApplicator) + } + + @OptIn(PrivateForInline::class) + fun > registerApplicator( + quickFixFactory: HLDiagnosticFixFactory + ) { + quickFixes.getOrPut(quickFixFactory.diagnosticClass) { mutableListOf() } .add(HLQuickFixFactory.HLApplicatorBasedFactory(quickFixFactory)) } 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 1a411fe498e..6533c30fcc1 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 @@ -12,7 +12,7 @@ import org.jetbrains.kotlin.idea.intentions.fir.AddAccessorsIntention import org.jetbrains.kotlin.psi.KtProperty object AddAccessorsFactories { - val addAccessorsToUninitializedProperty = diagnosticFixFactory { diagnostic -> + val addAccessorsToUninitializedProperty = diagnosticFixFactory(KtFirDiagnostic.MustBeInitializedOrBeAbstract::class) { diagnostic -> val property: KtProperty = diagnostic.psi val addGetter = property.getter == null val addSetter = property.isVar && property.setter == null 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 e10ea9a4e9e..b471fa0bdd1 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 @@ -18,15 +18,15 @@ import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.addToStdlib.safeAs object AddExclExclCallFixFactories { - val unsafeCallFactory = diagnosticFixFactory { diagnostic -> + val unsafeCallFactory = diagnosticFixFactory(KtFirDiagnostic.UnsafeCall::class) { diagnostic -> getFixForUnsafeCall(diagnostic.psi) } - val unsafeInfixCallFactory = diagnosticFixFactory { diagnostic -> + val unsafeInfixCallFactory = diagnosticFixFactory(KtFirDiagnostic.UnsafeInfixCall::class) { diagnostic -> getFixForUnsafeCall(diagnostic.psi) } - val unsafeOperatorCallFactory = diagnosticFixFactory { diagnostic -> + val unsafeOperatorCallFactory = diagnosticFixFactory(KtFirDiagnostic.UnsafeOperatorCall::class) { diagnostic -> getFixForUnsafeCall(diagnostic.psi) } @@ -92,7 +92,7 @@ object AddExclExclCallFixFactories { return listOfNotNull(target.asAddExclExclCallFix(hasImplicitReceiver = hasImplicitReceiver)) } - val iteratorOnNullableFactory = diagnosticFixFactory { diagnostic -> + val iteratorOnNullableFactory = diagnosticFixFactory(KtFirDiagnostic.IteratorOnNullable::class) { diagnostic -> val expression = diagnostic.psi as? KtExpression ?: return@diagnosticFixFactory emptyList() val type = expression.getKtType() if (!type.canBeNull) return@diagnosticFixFactory emptyList() diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/AddLateInitFactory.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/AddLateInitFactory.kt index 191c5a26636..3a08b40498e 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/AddLateInitFactory.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/AddLateInitFactory.kt @@ -12,7 +12,7 @@ import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtProperty object AddLateInitFactory { - val addLateInitFactory = diagnosticFixFactory { diagnostic -> + val addLateInitFactory = diagnosticFixFactory(KtFirDiagnostic.MustBeInitializedOrBeAbstract::class) { diagnostic -> val property: KtProperty = diagnostic.psi if (!property.isVar) return@diagnosticFixFactory emptyList() diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/ChangeTypeQuickFixFactories.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/ChangeTypeQuickFixFactories.kt index 02603f6e8dd..077f163a29c 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/ChangeTypeQuickFixFactories.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/ChangeTypeQuickFixFactories.kt @@ -118,14 +118,14 @@ object ChangeTypeQuickFixFactories { } val returnTypeMismatch = - diagnosticFixFactory(applicator) { diagnostic -> + diagnosticFixFactory(KtFirDiagnostic.ReturnTypeMismatch::class, applicator) { diagnostic -> val function = diagnostic.targetFunction.psi as? KtCallableDeclaration ?: return@diagnosticFixFactory emptyList() listOf(function withInput Input(TargetType.ENCLOSING_DECLARATION, createTypeInfo(diagnostic.actualType))) } @OptIn(ExperimentalStdlibApi::class) val componentFunctionReturnTypeMismatch = - diagnosticFixFactory(applicator) { diagnostic -> + diagnosticFixFactory(KtFirDiagnostic.ComponentFunctionReturnTypeMismatch::class, applicator) { diagnostic -> val entryWithWrongType = getDestructuringDeclarationEntryThatTypeMismatchComponentFunction( diagnostic.componentFunctionName, @@ -143,9 +143,9 @@ object ChangeTypeQuickFixFactories { } } - private inline fun > changeReturnTypeOnOverride( + private inline fun > changeReturnTypeOnOverride( crossinline getCallableSymbol: (DIAGNOSTIC) -> KtCallableSymbol? - ) = diagnosticFixFactory(applicator) { diagnostic -> + ) = diagnosticFixFactory(DIAGNOSTIC::class, applicator) { diagnostic -> val declaration = diagnostic.psi as? KtCallableDeclaration ?: return@diagnosticFixFactory emptyList() val callable = getCallableSymbol(diagnostic) ?: return@diagnosticFixFactory emptyList() listOfNotNull( diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/ImportQuickFix.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/ImportQuickFix.kt index 2e5bc13292d..d084ce14d11 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/ImportQuickFix.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/ImportQuickFix.kt @@ -134,7 +134,7 @@ internal class ImportQuickFix( } internal companion object { - val FACTORY = diagnosticFixFactory { diagnostic -> + val FACTORY = diagnosticFixFactory(KtFirDiagnostic.UnresolvedReference::class) { diagnostic -> val element = diagnostic.psi val indexHelper = IndexHelper(element.project, createSearchScope(element)) diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/InitializePropertyQuickFixFactory.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/InitializePropertyQuickFixFactory.kt index e40317a0a17..e8189947323 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/InitializePropertyQuickFixFactory.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/InitializePropertyQuickFixFactory.kt @@ -38,7 +38,7 @@ object InitializePropertyQuickFixFactory { @OptIn(ExperimentalStdlibApi::class) val initializePropertyFactory = - diagnosticFixFactory { diagnostic -> + diagnosticFixFactory(KtFirDiagnostic.MustBeInitializedOrBeAbstract::class) { diagnostic -> val property: KtProperty = diagnostic.psi buildList { add( diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/ReplaceCallFixFactories.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/ReplaceCallFixFactories.kt index 82e5203de67..9b2460de5c9 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/ReplaceCallFixFactories.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/ReplaceCallFixFactories.kt @@ -22,7 +22,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs object ReplaceCallFixFactories { val unsafeCallFactory = - diagnosticFixFactory { diagnostic -> + diagnosticFixFactory(KtFirDiagnostic.UnsafeCall::class) { diagnostic -> val psi = diagnostic.psi val target = if (psi is KtBinaryExpression && psi.operationToken in KtTokens.ALL_ASSIGNMENTS) { // UNSAFE_CALL for assignments (e.g., `foo.bar = value`) is reported on the entire statement (KtBinaryExpression). @@ -48,14 +48,14 @@ object ReplaceCallFixFactories { } val unsafeInfixCallFactory = - diagnosticFixFactory { diagnostic -> + diagnosticFixFactory(KtFirDiagnostic.UnsafeInfixCall::class) { diagnostic -> val psi = diagnostic.psi val target = psi.parent as? KtBinaryExpression ?: return@diagnosticFixFactory emptyList() listOf(ReplaceInfixOrOperatorCallFix(target, shouldHaveNotNullType(target), diagnostic.operator)) } val unsafeOperatorCallFactory = - diagnosticFixFactory { diagnostic -> + diagnosticFixFactory(KtFirDiagnostic.UnsafeOperatorCall::class) { diagnostic -> val psi = diagnostic.psi val operationToken = psi.safeAs()?.getReferencedNameElementType() if (operationToken == KtTokens.EQ || operationToken in OperatorConventions.COMPARISON_OPERATIONS) { @@ -68,7 +68,7 @@ object ReplaceCallFixFactories { } val unsafeImplicitInvokeCallFactory = - diagnosticFixFactory { diagnostic -> + diagnosticFixFactory(KtFirDiagnostic.UnsafeImplicitInvokeCall::class) { diagnostic -> val target = diagnostic.psi as? KtNameReferenceExpression ?: return@diagnosticFixFactory emptyList() val callExpression = target.parent as? KtCallExpression ?: return@diagnosticFixFactory emptyList() 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 e55e46da526..db24fe6723d 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 @@ -16,11 +16,11 @@ import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.utils.addToStdlib.safeAs object TypeMismatchFactories { - val argumentTypeMismatchFactory = diagnosticFixFactory { diagnostic -> + val argumentTypeMismatchFactory = diagnosticFixFactory(KtFirDiagnostic.ArgumentTypeMismatch::class) { diagnostic -> getFixesForTypeMismatch(diagnostic.psi, diagnostic.expectedType, diagnostic.actualType) } - val returnTypeMismatchFactory = diagnosticFixFactory { diagnostic -> + val returnTypeMismatchFactory = diagnosticFixFactory(KtFirDiagnostic.ReturnTypeMismatch::class) { diagnostic -> getFixesForTypeMismatch(diagnostic.psi, diagnostic.expectedType, diagnostic.actualType) } 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 83c512b6e4a..919cff115c6 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 @@ -140,12 +140,12 @@ object WrapWithSafeLetCallFixFactories { } } - val forUnsafeCall = diagnosticFixFactory { diagnostic -> + val forUnsafeCall = diagnosticFixFactory(KtFirDiagnostic.UnsafeCall::class) { diagnostic -> val nullableExpression = diagnostic.receiverExpression createWrapWithSafeLetCallInputForNullableExpressionIfMoreThanImmediateParentIsWrapped(nullableExpression) } - val forUnsafeImplicitInvokeCall = diagnosticFixFactory { diagnostic -> + val forUnsafeImplicitInvokeCall = diagnosticFixFactory(KtFirDiagnostic.UnsafeImplicitInvokeCall::class) { diagnostic -> val callExpression = diagnostic.psi.parentOfType(withSelf = true) ?: return@diagnosticFixFactory emptyList() val callingFunctionalVariableInLocalScope = isCallingFunctionalTypeVariableInLocalScope(callExpression) ?: return@diagnosticFixFactory emptyList() @@ -164,15 +164,15 @@ object WrapWithSafeLetCallFixFactories { return localScope.scopes.getCallableSymbols { it.identifierOrNullIfSpecial == calleeName }.any { it == functionalVariableSymbol } } - val forUnsafeInfixCall = diagnosticFixFactory { diagnostic -> + val forUnsafeInfixCall = diagnosticFixFactory(KtFirDiagnostic.UnsafeInfixCall::class) { diagnostic -> createWrapWithSafeLetCallInputForNullableExpressionIfMoreThanImmediateParentIsWrapped(diagnostic.receiverExpression) } - val forUnsafeOperatorCall = diagnosticFixFactory { diagnostic -> + val forUnsafeOperatorCall = diagnosticFixFactory(KtFirDiagnostic.UnsafeOperatorCall::class) { diagnostic -> createWrapWithSafeLetCallInputForNullableExpressionIfMoreThanImmediateParentIsWrapped(diagnostic.receiverExpression) } - val forArgumentTypeMismatch = diagnosticFixFactory { diagnostic -> + val forArgumentTypeMismatch = diagnosticFixFactory(KtFirDiagnostic.ArgumentTypeMismatch::class) { diagnostic -> if (diagnostic.isMismatchDueToNullability) createWrapWithSafeLetCallInputForNullableExpression(diagnostic.psi.wrappingExpressionOrSelf) else emptyList() }