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.psi.PsiElement
import org.jetbrains.kotlin.idea.frontend.api.ForbidKtResolve
import org.jetbrains.kotlin.idea.util.textRangeIn
import org.jetbrains.kotlin.psi.KtElement
@@ -28,13 +29,17 @@ sealed class HLApplicabilityRange<in ELEMENT : PsiElement> {
* The ranges are relative to [element]
* 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 val getApplicabilityRanges: (ELEMENT) -> List<TextRange>,
) : HLApplicabilityRange<ELEMENT>() {
override fun getApplicabilityRanges(element: ELEMENT): List<TextRange> =
override fun getApplicabilityRangesImpl(element: ELEMENT): List<TextRange> =
getApplicabilityRanges.invoke(element)
}
@@ -9,16 +9,31 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
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.reflect.KClass
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
abstract fun getFamilyName(): String
fun getActionName(psi: PSI, input: INPUT): String = ForbidKtResolve.forbidResolveIn("HLApplicator.getActionName") {
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(
@@ -51,17 +66,17 @@ internal class HLApplicatorImpl<PSI : PsiElement, INPUT : HLApplicatorInput>(
val getActionName: (PSI, INPUT) -> String,
val getFamilyName: () -> String,
) : 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)
}
override fun isApplicableByPsi(psi: PSI): Boolean =
override fun isApplicableByPsiImpl(psi: PSI): Boolean =
isApplicableByPsi.invoke(psi)
override fun getActionName(psi: PSI, input: INPUT): String =
override fun getActionNameImpl(psi: PSI, input: INPUT): String =
getActionName.invoke(psi, input)
override fun getFamilyName(): String =
override fun getFamilyNameImpl(): String =
getFamilyName.invoke()
}
@@ -7,25 +7,34 @@ package org.jetbrains.kotlin.idea.fir.api.applicator
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() {
abstract fun getHighlightType(element: PSI): ProblemHighlightType
abstract fun getMessage(element: PSI): String
abstract class HLPresentation<PSI : PsiElement> internal constructor() {
fun getHighlightType(element: PSI): ProblemHighlightType = ForbidKtResolve.forbidResolveIn("HLPresentation.getHighlightType") {
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 getMessage: (element: PSI) -> String,
) : HLPresentation<PSI>() {
override fun getHighlightType(element: PSI): ProblemHighlightType =
override fun getHighlightTypeImpl(element: PSI): ProblemHighlightType =
getHighlightType.invoke(element)
override fun getMessage(element: PSI): String =
override fun getMessageImpl(element: PSI): String =
getMessage.invoke(element)
}
class HLInspectionPresentationProviderBuilder<PSI: PsiElement> internal constructor() {
class HLInspectionPresentationProviderBuilder<PSI : PsiElement> internal constructor() {
private var getHighlightType: ((element: PSI) -> ProblemHighlightType)? = null
private var getMessage: ((element: PSI) -> String)? = null
@@ -50,7 +59,7 @@ class HLInspectionPresentationProviderBuilder<PSI: PsiElement> internal construc
HLPresentationImpl(getHighlightType!!, getMessage!!)
}
fun <PSI: PsiElement> presentation(
fun <PSI : PsiElement> presentation(
init: HLInspectionPresentationProviderBuilder<PSI>.() -> Unit
): HLPresentation<PSI> =
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")
}
@OptIn(HackToForceAllowRunningAnalyzeOnEDT::class)
@OptIn(HackToForceAllowRunningAnalyzeOnEDT::class, ForbidKtResolveInternals::class)
override fun isAccessible(): Boolean {
val application = ApplicationManager.getApplication()
if (application.isDispatchThread && !allowOnEdt.get()) return false
if (ForbidKtResolve.resovleIsForbidenInActionWithName.get() != null) return false
if (!application.isReadAccessAllowed) return false
return true
}
@OptIn(HackToForceAllowRunningAnalyzeOnEDT::class)
@OptIn(HackToForceAllowRunningAnalyzeOnEDT::class, ForbidKtResolveInternals::class)
override fun getInaccessibilityReason(): String {
val application = ApplicationManager.getApplication()
if (application.isDispatchThread && !allowOnEdt.get()) return "Called in EDT thread"
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")
}
companion object {
@HackToForceAllowRunningAnalyzeOnEDT
val allowOnEdt: ThreadLocal<Boolean> = ThreadLocal.withInitial { false }