FIR IDE: add more KDoc to HLApplicator stuff

This commit is contained in:
Ilya Kirillov
2021-02-07 18:34:52 +01:00
parent 2554065ae9
commit cf3defbc9c
6 changed files with 96 additions and 14 deletions
@@ -84,6 +84,7 @@ abstract class AbstractHLInspection<PSI : KtElement, INPUT : HLApplicatorInput>(
}
}
abstract val presentation: HLPresentation<PSI>
abstract val applicabilityRange: HLApplicabilityRange<PSI>
abstract val inputProvider: HLApplicatorInputProvider<PSI, INPUT>
@@ -96,7 +97,6 @@ private fun <PSI : PsiElement, INPUT : HLApplicatorInput> HLApplicator<PSI, INPU
): LocalQuickFix = object : LocalQuickFix {
override fun startInWriteAction() = false
@OptIn(HackToForceAllowRunningAnalyzeOnEDT::class)
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
@Suppress("UNCHECKED_CAST")
val element = descriptor.psiElement as PSI
@@ -13,19 +13,45 @@ import org.jetbrains.kotlin.idea.frontend.api.ForbidKtResolve
import kotlin.experimental.ExperimentalTypeInference
import kotlin.reflect.KClass
/**
* Applies a fix to the PSI, used as intention/inspection/quickfix action
* Also, knows if a fix is applicable by [isApplicableByPsi]
*
* Uses some additional information from [INPUT] to apply the element
*/
sealed class HLApplicator<in PSI : PsiElement, in INPUT : HLApplicatorInput> {
/**
* 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<in PSI : PsiElement, in INPUT : HLApplicatorInput> {
protected abstract fun getFamilyNameImpl(): String
}
/**
* Create a copy of an applicator with some components replaced
*/
fun <PSI : PsiElement, NEW_PSI : PSI, INPUT : HLApplicatorInput> HLApplicator<PSI, INPUT>.with(
init: HLApplicatorBuilder<NEW_PSI, INPUT>.(olApplicator: HLApplicator<PSI, INPUT>) -> Unit
): HLApplicator<NEW_PSI, INPUT> = when (this@with) {
@@ -47,8 +76,12 @@ fun <PSI : PsiElement, NEW_PSI : PSI, INPUT : HLApplicatorInput> HLApplicator<PS
}
}
/**
* Create a copy of an applicator with some components replaced
* The PSI type of a new applicator will be a class passed in [newPsiTypeTag]
*/
fun <PSI : PsiElement, NEW_PSI : PSI, INPUT : HLApplicatorInput> HLApplicator<PSI, INPUT>.with(
newPsiTypeTag: KClass<NEW_PSI>,
@Suppress("UNUSED_PARAMETER") newPsiTypeTag: KClass<NEW_PSI>,
init: HLApplicatorBuilder<NEW_PSI, INPUT>.(olApplicator: HLApplicator<PSI, INPUT>) -> Unit
): HLApplicator<NEW_PSI, INPUT> = when (this@with) {
is HLApplicatorImpl -> {
@@ -128,15 +161,31 @@ class HLApplicatorBuilder<PSI : PsiElement, INPUT : HLApplicatorInput> internal
@OptIn(PrivateForInline::class)
fun build(): HLApplicator<PSI, INPUT> = HLApplicatorImpl(
applyTo = applyTo!!,
isApplicableByPsi = isApplicableByPsi ?: { true },
getActionName = getActionName ?: getFamilyName?.let { familyName -> { _, _ -> familyName.invoke() } }!!,
getFamilyName = getFamilyName!!
)
fun build(): HLApplicator<PSI, INPUT> {
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 <PSI : PsiElement, INPUT : HLApplicatorInput> applicator(
init: HLApplicatorBuilder<PSI, INPUT>.() -> Unit,
): HLApplicator<PSI, INPUT> =
@@ -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
}
@@ -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<PSI : PsiElement, out INPUT : HLApplicatorInput> {
/**
* 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<PSI : PsiElement, out INPUT : HLAppl
override fun KtAnalysisSession.provideInput(element: PSI): INPUT? = provideInput.invoke(this, element)
}
/**
* Creates [HLApplicatorInputProvider]
* The [provideInput] is guaranteed to be executed from read action, should not be called from EDT thread
*/
fun <PSI : PsiElement, INPUT : HLApplicatorInput> inputProvider(
provideInput: KtAnalysisSession.(PSI) -> INPUT?
): HLApplicatorInputProvider<PSI, INPUT> =
@@ -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<PSI : PsiElement> 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<PSI : PsiElement> {
fun getHighlightType(element: PSI): ProblemHighlightType = ForbidKtResolve.forbidResolveIn("HLPresentation.getHighlightType") {
getHighlightTypeImpl(element)
}
@@ -54,9 +58,13 @@ class HLInspectionPresentationProviderBuilder<PSI : PsiElement> internal constru
getMessage = { text }
}
internal fun build(): HLPresentation<PSI> =
HLPresentationImpl(getHighlightType!!, getMessage!!)
internal fun build(): HLPresentation<PSI> {
val getHighlightType = getHighlightType
?: error("Please, provide highlightType")
val getMessage = getMessage
?: error("Please, provide getMessage")
return HLPresentationImpl(getHighlightType, getMessage)
}
}
fun <PSI : PsiElement> presentation(
@@ -20,11 +20,11 @@ import org.jetbrains.kotlin.psi.KtPsiFactory
object CallableReturnTypeUpdaterApplicator {
val applicator = applicator<KtCallableDeclaration, Type> {
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 {