diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/AbstractHLInspection.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/AbstractHLInspection.kt index 522eabb46c7..8523de2ce30 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/AbstractHLInspection.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/AbstractHLInspection.kt @@ -84,6 +84,7 @@ abstract class AbstractHLInspection( } } + abstract val presentation: HLPresentation abstract val applicabilityRange: HLApplicabilityRange abstract val inputProvider: HLApplicatorInputProvider @@ -96,7 +97,6 @@ private fun HLApplicator { + + /** + * Applies some fix to given [psi], can not use resolve, so all needed data should be precalculated and stored in [input] + * + * @param psi a [PsiElement] to apply fix to + * @param input additional data needed to apply the fix, the [input] can be collected by [HLApplicatorInputProvider + */ fun applyTo(psi: PSI, input: INPUT, project: Project?, editor: Editor?) = ForbidKtResolve.forbidResolveIn("HLApplicator.applyTo") { applyToImpl(psi, input, project, editor) } + /** + * Checks if applicator is applicable to specific element, can not use resolve inside + */ fun isApplicableByPsi(psi: PSI): Boolean = ForbidKtResolve.forbidResolveIn("HLApplicator.isApplicableByPsi") { isApplicableByPsiImpl(psi) } + /** + * Action name which will be as text in inspections/intentions + * + * @see com.intellij.codeInsight.intention.IntentionAction.getText + */ fun getActionName(psi: PSI, input: INPUT): String = ForbidKtResolve.forbidResolveIn("HLApplicator.getActionName") { getActionNameImpl(psi, input) } + /** + * Family name which will be used in inspections/intentions + * + * @see com.intellij.codeInsight.intention.IntentionAction.getFamilyName + */ fun getFamilyName(): String = ForbidKtResolve.forbidResolveIn("HLApplicator.getFamilyName") { getFamilyNameImpl() } @@ -36,6 +62,9 @@ sealed class HLApplicator { protected abstract fun getFamilyNameImpl(): String } +/** + * Create a copy of an applicator with some components replaced + */ fun HLApplicator.with( init: HLApplicatorBuilder.(olApplicator: HLApplicator) -> Unit ): HLApplicator = when (this@with) { @@ -47,8 +76,12 @@ fun HLApplicator HLApplicator.with( - newPsiTypeTag: KClass, + @Suppress("UNUSED_PARAMETER") newPsiTypeTag: KClass, init: HLApplicatorBuilder.(olApplicator: HLApplicator) -> Unit ): HLApplicator = when (this@with) { is HLApplicatorImpl -> { @@ -128,15 +161,31 @@ class HLApplicatorBuilder internal @OptIn(PrivateForInline::class) - fun build(): HLApplicator = HLApplicatorImpl( - applyTo = applyTo!!, - isApplicableByPsi = isApplicableByPsi ?: { true }, - getActionName = getActionName ?: getFamilyName?.let { familyName -> { _, _ -> familyName.invoke() } }!!, - getFamilyName = getFamilyName!! - ) + fun build(): HLApplicator { + val applyTo = applyTo + ?: error("Please, specify applyTo") + val getActionName = getActionName + ?: error("Please, specify actionName or familyName via either of: actionName,familyAndActionName") + val isApplicableByPsi = isApplicableByPsi ?: { true } + val getFamilyName = getFamilyName + ?: error("Please, specify or familyName via either of: familyName, familyAndActionName") + return HLApplicatorImpl( + applyTo = applyTo, + isApplicableByPsi = isApplicableByPsi, + getActionName = getActionName, + getFamilyName = getFamilyName + ) + } } +/** + * Builds a new applicator with HLApplicatorBuilder + * + * Should specify at least applyTo and familyAndActionName + * + * @see HLApplicatorBuilder + */ fun applicator( init: HLApplicatorBuilder.() -> Unit, ): HLApplicator = diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/applicator/HLApplicatorInput.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/applicator/HLApplicatorInput.kt index aa7e5a954a5..2cf07c91bc7 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/applicator/HLApplicatorInput.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/applicator/HLApplicatorInput.kt @@ -7,6 +7,20 @@ package org.jetbrains.kotlin.idea.fir.api.applicator import com.intellij.psi.PsiElement +/** + * Data which [HLApplicator] is needed to perform the fix + * + * Created by [HLApplicatorInputProvider] or via [org.jetbrains.kotlin.idea.fir.api.fixes.HLDiagnosticFixFactory] + * + * Should not store inside + * - Everything that came from [org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession] like : + * - [org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbol] consider using [org.jetbrains.kotlin.idea.frontend.api.symbols.pointers.KtSymbolPointer] instead + * - [org.jetbrains.kotlin.idea.frontend.api.types.KtType] + * - [org.jetbrains.kotlin.idea.frontend.api.calls.KtCall] + * - [org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession] instance itself + * - [PsiElement] consider using [com.intellij.psi.SmartPsiElementPointer] instead + * + */ interface HLApplicatorInput { fun isValidFor(psi: PsiElement): Boolean = true } diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/applicator/HLApplicatorInputProvider.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/applicator/HLApplicatorInputProvider.kt index 199ba4463c7..9cd5cffbf74 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/applicator/HLApplicatorInputProvider.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/applicator/HLApplicatorInputProvider.kt @@ -8,7 +8,14 @@ package org.jetbrains.kotlin.idea.fir.api.applicator import com.intellij.psi.PsiElement import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession +/** + * Resolves the code to provide [HLApplicator] some input + */ abstract class HLApplicatorInputProvider { + /** + * Provide input to the applicator, if returns `null` then the applicator is not applicable and will not be called + * Guaranteed to be executed from read action, should not be called from EDT thread + */ abstract fun KtAnalysisSession.provideInput(element: PSI): INPUT? } @@ -18,6 +25,10 @@ private class HLApplicatorInputProviderImpl inputProvider( provideInput: KtAnalysisSession.(PSI) -> INPUT? ): HLApplicatorInputProvider = diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/applicator/HLPresentation.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/applicator/HLPresentation.kt index baa316640fd..970298d56d3 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/applicator/HLPresentation.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/api/applicator/HLPresentation.kt @@ -9,7 +9,11 @@ import com.intellij.codeInspection.ProblemHighlightType import com.intellij.psi.PsiElement import org.jetbrains.kotlin.idea.frontend.api.ForbidKtResolve -abstract class HLPresentation internal constructor() { +/** + * Provides a presentation to display an message in the editor which may be latter fixed by [HLApplicator] + * Used by [org.jetbrains.kotlin.idea.fir.api.AbstractHLInspection] to provide higlighting message and type + */ +sealed class HLPresentation { fun getHighlightType(element: PSI): ProblemHighlightType = ForbidKtResolve.forbidResolveIn("HLPresentation.getHighlightType") { getHighlightTypeImpl(element) } @@ -54,9 +58,13 @@ class HLInspectionPresentationProviderBuilder internal constru getMessage = { text } } - - internal fun build(): HLPresentation = - HLPresentationImpl(getHighlightType!!, getMessage!!) + internal fun build(): HLPresentation { + val getHighlightType = getHighlightType + ?: error("Please, provide highlightType") + val getMessage = getMessage + ?: error("Please, provide getMessage") + return HLPresentationImpl(getHighlightType, getMessage) + } } fun presentation( diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/applicators/CallableReturnTypeUpdaterApplicator.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/applicators/CallableReturnTypeUpdaterApplicator.kt index ad447fde7af..43029164383 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/applicators/CallableReturnTypeUpdaterApplicator.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/applicators/CallableReturnTypeUpdaterApplicator.kt @@ -20,11 +20,11 @@ import org.jetbrains.kotlin.psi.KtPsiFactory object CallableReturnTypeUpdaterApplicator { val applicator = applicator { - familyName(KotlinBundle.message("fix.change.return.type.family")) + familyAndActionName(KotlinBundle.lazyMessage("fix.change.return.type.family")) applyTo { declaration, type, project -> val newTypeRef = if (!declaration.isProcedure(type)) { - // TODO use longTypeRepresentation and the shorten + // TODO use longTypeRepresentation and then shorten KtPsiFactory(project ?: declaration.project).createType(type.shortTypeRepresentation) } else null runWriteAction {