FIR IDE: forbid resolve in HLApplicator/HLApplicabilityRange/HLPresentation

This commit is contained in:
Ilya Kirillov
2021-02-07 17:16:25 +01:00
parent 7ab9583102
commit 2554065ae9
5 changed files with 81 additions and 20 deletions
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.idea.fir.api.applicator
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.frontend.api.ForbidKtResolve
import org.jetbrains.kotlin.idea.util.textRangeIn import org.jetbrains.kotlin.idea.util.textRangeIn
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
@@ -28,13 +29,17 @@ sealed class HLApplicabilityRange<in ELEMENT : PsiElement> {
* The ranges are relative to [element] * The ranges are relative to [element]
* i.e. if range covers the whole element when it should return `[0, element.length)` * i.e. if range covers the whole element when it should return `[0, element.length)`
*/ */
abstract fun getApplicabilityRanges(element: ELEMENT): List<TextRange> fun getApplicabilityRanges(element: ELEMENT): List<TextRange> = ForbidKtResolve.forbidResolveIn("getApplicabilityRanges") {
getApplicabilityRangesImpl(element)
}
protected abstract fun getApplicabilityRangesImpl(element: ELEMENT): List<TextRange>
} }
private class HLApplicabilityRangeImpl<ELEMENT : PsiElement>( private class HLApplicabilityRangeImpl<ELEMENT : PsiElement>(
private val getApplicabilityRanges: (ELEMENT) -> List<TextRange>, private val getApplicabilityRanges: (ELEMENT) -> List<TextRange>,
) : HLApplicabilityRange<ELEMENT>() { ) : HLApplicabilityRange<ELEMENT>() {
override fun getApplicabilityRanges(element: ELEMENT): List<TextRange> = override fun getApplicabilityRangesImpl(element: ELEMENT): List<TextRange> =
getApplicabilityRanges.invoke(element) getApplicabilityRanges.invoke(element)
} }
@@ -9,16 +9,31 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.fir.low.level.api.annotations.PrivateForInline import org.jetbrains.kotlin.idea.fir.low.level.api.annotations.PrivateForInline
import org.jetbrains.kotlin.idea.frontend.api.ForbidKtResolve
import kotlin.experimental.ExperimentalTypeInference import kotlin.experimental.ExperimentalTypeInference
import kotlin.reflect.KClass import kotlin.reflect.KClass
sealed class HLApplicator<in PSI : PsiElement, in INPUT : HLApplicatorInput> { sealed class HLApplicator<in PSI : PsiElement, in INPUT : HLApplicatorInput> {
abstract fun applyTo(psi: PSI, input: INPUT, project: Project?, editor: Editor?) fun applyTo(psi: PSI, input: INPUT, project: Project?, editor: Editor?) = ForbidKtResolve.forbidResolveIn("HLApplicator.applyTo") {
applyToImpl(psi, input, project, editor)
}
abstract fun isApplicableByPsi(psi: PSI): Boolean fun isApplicableByPsi(psi: PSI): Boolean = ForbidKtResolve.forbidResolveIn("HLApplicator.isApplicableByPsi") {
isApplicableByPsiImpl(psi)
}
abstract fun getActionName(psi: PSI, input: INPUT): String fun getActionName(psi: PSI, input: INPUT): String = ForbidKtResolve.forbidResolveIn("HLApplicator.getActionName") {
abstract fun getFamilyName(): String getActionNameImpl(psi, input)
}
fun getFamilyName(): String = ForbidKtResolve.forbidResolveIn("HLApplicator.getFamilyName") {
getFamilyNameImpl()
}
protected abstract fun applyToImpl(psi: PSI, input: INPUT, project: Project?, editor: Editor?)
protected abstract fun isApplicableByPsiImpl(psi: PSI): Boolean
protected abstract fun getActionNameImpl(psi: PSI, input: INPUT): String
protected abstract fun getFamilyNameImpl(): String
} }
fun <PSI : PsiElement, NEW_PSI : PSI, INPUT : HLApplicatorInput> HLApplicator<PSI, INPUT>.with( fun <PSI : PsiElement, NEW_PSI : PSI, INPUT : HLApplicatorInput> HLApplicator<PSI, INPUT>.with(
@@ -51,17 +66,17 @@ internal class HLApplicatorImpl<PSI : PsiElement, INPUT : HLApplicatorInput>(
val getActionName: (PSI, INPUT) -> String, val getActionName: (PSI, INPUT) -> String,
val getFamilyName: () -> String, val getFamilyName: () -> String,
) : HLApplicator<PSI, INPUT>() { ) : HLApplicator<PSI, INPUT>() {
override fun applyTo(psi: PSI, input: INPUT, project: Project?, editor: Editor?) { override fun applyToImpl(psi: PSI, input: INPUT, project: Project?, editor: Editor?) {
applyTo.invoke(psi, input, project, editor) applyTo.invoke(psi, input, project, editor)
} }
override fun isApplicableByPsi(psi: PSI): Boolean = override fun isApplicableByPsiImpl(psi: PSI): Boolean =
isApplicableByPsi.invoke(psi) isApplicableByPsi.invoke(psi)
override fun getActionName(psi: PSI, input: INPUT): String = override fun getActionNameImpl(psi: PSI, input: INPUT): String =
getActionName.invoke(psi, input) getActionName.invoke(psi, input)
override fun getFamilyName(): String = override fun getFamilyNameImpl(): String =
getFamilyName.invoke() getFamilyName.invoke()
} }
@@ -7,25 +7,34 @@ package org.jetbrains.kotlin.idea.fir.api.applicator
import com.intellij.codeInspection.ProblemHighlightType import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.frontend.api.ForbidKtResolve
abstract class HLPresentation<PSI: PsiElement> internal constructor() { abstract class HLPresentation<PSI : PsiElement> internal constructor() {
abstract fun getHighlightType(element: PSI): ProblemHighlightType fun getHighlightType(element: PSI): ProblemHighlightType = ForbidKtResolve.forbidResolveIn("HLPresentation.getHighlightType") {
abstract fun getMessage(element: PSI): String getHighlightTypeImpl(element)
}
fun getMessage(element: PSI): String = ForbidKtResolve.forbidResolveIn("HLPresentation.getMessage") {
getMessageImpl(element)
}
abstract fun getMessageImpl(element: PSI): String
abstract fun getHighlightTypeImpl(element: PSI): ProblemHighlightType
} }
private class HLPresentationImpl<PSI: PsiElement>( private class HLPresentationImpl<PSI : PsiElement>(
private val getHighlightType: (element: PSI) -> ProblemHighlightType, private val getHighlightType: (element: PSI) -> ProblemHighlightType,
private val getMessage: (element: PSI) -> String, private val getMessage: (element: PSI) -> String,
) : HLPresentation<PSI>() { ) : HLPresentation<PSI>() {
override fun getHighlightType(element: PSI): ProblemHighlightType = override fun getHighlightTypeImpl(element: PSI): ProblemHighlightType =
getHighlightType.invoke(element) getHighlightType.invoke(element)
override fun getMessage(element: PSI): String = override fun getMessageImpl(element: PSI): String =
getMessage.invoke(element) getMessage.invoke(element)
} }
class HLInspectionPresentationProviderBuilder<PSI: PsiElement> internal constructor() { class HLInspectionPresentationProviderBuilder<PSI : PsiElement> internal constructor() {
private var getHighlightType: ((element: PSI) -> ProblemHighlightType)? = null private var getHighlightType: ((element: PSI) -> ProblemHighlightType)? = null
private var getMessage: ((element: PSI) -> String)? = null private var getMessage: ((element: PSI) -> String)? = null
@@ -50,7 +59,7 @@ class HLInspectionPresentationProviderBuilder<PSI: PsiElement> internal construc
HLPresentationImpl(getHighlightType!!, getMessage!!) HLPresentationImpl(getHighlightType!!, getMessage!!)
} }
fun <PSI: PsiElement> presentation( fun <PSI : PsiElement> presentation(
init: HLInspectionPresentationProviderBuilder<PSI>.() -> Unit init: HLInspectionPresentationProviderBuilder<PSI>.() -> Unit
): HLPresentation<PSI> = ): HLPresentation<PSI> =
HLInspectionPresentationProviderBuilder<PSI>().apply(init).build() HLInspectionPresentationProviderBuilder<PSI>().apply(init).build()
@@ -0,0 +1,27 @@
/*
* 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.frontend.api
import kotlin.reflect.KProperty1
@RequiresOptIn
annotation class ForbidKtResolveInternals
object ForbidKtResolve {
@OptIn(ForbidKtResolveInternals::class)
inline fun <R> forbidResolveIn(actionName: String, action: () -> R): R {
if (resovleIsForbidenInActionWithName.get() != null) return action()
resovleIsForbidenInActionWithName.set(actionName)
return try {
action()
} finally {
resovleIsForbidenInActionWithName.set(null)
}
}
@ForbidKtResolveInternals
val resovleIsForbidenInActionWithName: ThreadLocal<String?> = ThreadLocal.withInitial { null }
}
@@ -23,22 +23,27 @@ class ReadActionConfinementValidityToken(project: Project) : ValidityToken() {
error("Getting invalidation reason for valid validity token") error("Getting invalidation reason for valid validity token")
} }
@OptIn(HackToForceAllowRunningAnalyzeOnEDT::class) @OptIn(HackToForceAllowRunningAnalyzeOnEDT::class, ForbidKtResolveInternals::class)
override fun isAccessible(): Boolean { override fun isAccessible(): Boolean {
val application = ApplicationManager.getApplication() val application = ApplicationManager.getApplication()
if (application.isDispatchThread && !allowOnEdt.get()) return false if (application.isDispatchThread && !allowOnEdt.get()) return false
if (ForbidKtResolve.resovleIsForbidenInActionWithName.get() != null) return false
if (!application.isReadAccessAllowed) return false if (!application.isReadAccessAllowed) return false
return true return true
} }
@OptIn(HackToForceAllowRunningAnalyzeOnEDT::class) @OptIn(HackToForceAllowRunningAnalyzeOnEDT::class, ForbidKtResolveInternals::class)
override fun getInaccessibilityReason(): String { override fun getInaccessibilityReason(): String {
val application = ApplicationManager.getApplication() val application = ApplicationManager.getApplication()
if (application.isDispatchThread && !allowOnEdt.get()) return "Called in EDT thread" if (application.isDispatchThread && !allowOnEdt.get()) return "Called in EDT thread"
if (!application.isReadAccessAllowed) return "Called outside read action" if (!application.isReadAccessAllowed) return "Called outside read action"
ForbidKtResolve.resovleIsForbidenInActionWithName.get()?.let { actionName ->
return "Resolve is forbidden in $actionName"
}
error("Getting inaccessibility reason for validity token when it is accessible") error("Getting inaccessibility reason for validity token when it is accessible")
} }
companion object { companion object {
@HackToForceAllowRunningAnalyzeOnEDT @HackToForceAllowRunningAnalyzeOnEDT
val allowOnEdt: ThreadLocal<Boolean> = ThreadLocal.withInitial { false } val allowOnEdt: ThreadLocal<Boolean> = ThreadLocal.withInitial { false }