IntentionBasedInspection: Removing synchronizing on intention instances
Recreate intention instance in `buildVisitor` Lock caused contention for UpSource
This commit is contained in:
+28
-23
@@ -31,34 +31,34 @@ import com.intellij.psi.PsiElementVisitor
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.util.SmartList
|
||||
import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
abstract class IntentionBasedInspection<TElement : PsiElement>(
|
||||
val intentions: List<IntentionBasedInspection.IntentionData<TElement>>,
|
||||
protected open val problemText: String?,
|
||||
protected val elementType: Class<TElement>
|
||||
val intentionInfos: List<IntentionBasedInspection.IntentionData<TElement>>,
|
||||
protected open val problemText: String?
|
||||
) : AbstractKotlinInspection() {
|
||||
|
||||
constructor(
|
||||
intention: SelfTargetingRangeIntention<TElement>,
|
||||
intention: KClass<out SelfTargetingRangeIntention<TElement>>,
|
||||
problemText: String? = null
|
||||
) : this(listOf(IntentionData(intention)), problemText, intention.elementType)
|
||||
) : this(listOf(IntentionData(intention)), problemText)
|
||||
|
||||
constructor(
|
||||
intention: SelfTargetingRangeIntention<TElement>,
|
||||
intention: KClass<out SelfTargetingRangeIntention<TElement>>,
|
||||
additionalChecker: (TElement, IntentionBasedInspection<TElement>) -> Boolean,
|
||||
problemText: String? = null
|
||||
) : this(listOf(IntentionData(intention, additionalChecker)), problemText, intention.elementType)
|
||||
) : this(listOf(IntentionData(intention, additionalChecker)), problemText)
|
||||
|
||||
constructor(
|
||||
intention: SelfTargetingRangeIntention<TElement>,
|
||||
intention: KClass<out SelfTargetingRangeIntention<TElement>>,
|
||||
additionalChecker: (TElement) -> Boolean,
|
||||
problemText: String? = null
|
||||
) : this(listOf(IntentionData(intention, { element, inspection -> additionalChecker(element) } )), problemText, intention.elementType)
|
||||
) : this(listOf(IntentionData(intention, { element, inspection -> additionalChecker(element) } )), problemText)
|
||||
|
||||
|
||||
|
||||
data class IntentionData<TElement : PsiElement>(
|
||||
val intention: SelfTargetingRangeIntention<TElement>,
|
||||
val intention: KClass<out SelfTargetingRangeIntention<TElement>>,
|
||||
val additionalChecker: (TElement, IntentionBasedInspection<TElement>) -> Boolean = { element, inspection -> true }
|
||||
)
|
||||
|
||||
@@ -67,6 +67,13 @@ abstract class IntentionBasedInspection<TElement : PsiElement>(
|
||||
open fun inspectionRange(element: TElement): TextRange? = null
|
||||
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
||||
|
||||
val intentionsAndCheckers = intentionInfos.map {
|
||||
it.intention.constructors.single().call() to it.additionalChecker
|
||||
}
|
||||
val elementType = intentionsAndCheckers.map { it.first.elementType }.distinct().singleOrNull()
|
||||
?: error("$intentionInfos should have the same elementType")
|
||||
|
||||
return object : PsiElementVisitor() {
|
||||
override fun visitElement(element: PsiElement) {
|
||||
if (!elementType.isInstance(element) || element.textLength == 0) return
|
||||
@@ -77,21 +84,19 @@ abstract class IntentionBasedInspection<TElement : PsiElement>(
|
||||
var problemRange: TextRange? = null
|
||||
var fixes: SmartList<LocalQuickFix>? = null
|
||||
|
||||
for ((intention, additionalChecker) in intentions) {
|
||||
synchronized(intention) {
|
||||
val range = intention.applicabilityRange(targetElement)?.let { range ->
|
||||
val elementRange = targetElement.textRange
|
||||
assert(range in elementRange) { "Wrong applicabilityRange() result for $intention - should be within element's range" }
|
||||
range.shiftRight(-elementRange.startOffset)
|
||||
}
|
||||
for ((intention, additionalChecker) in intentionsAndCheckers) {
|
||||
val range = intention.applicabilityRange(targetElement)?.let { range ->
|
||||
val elementRange = targetElement.textRange
|
||||
assert(range in elementRange) { "Wrong applicabilityRange() result for $intention - should be within element's range" }
|
||||
range.shiftRight(-elementRange.startOffset)
|
||||
}
|
||||
|
||||
if (range != null && additionalChecker(targetElement, this@IntentionBasedInspection)) {
|
||||
problemRange = problemRange?.union(range) ?: range
|
||||
if (fixes == null) {
|
||||
fixes = SmartList<LocalQuickFix>()
|
||||
}
|
||||
fixes!!.add(createQuickFix(intention, additionalChecker, targetElement))
|
||||
if (range != null && additionalChecker(targetElement, this@IntentionBasedInspection)) {
|
||||
problemRange = problemRange?.union(range) ?: range
|
||||
if (fixes == null) {
|
||||
fixes = SmartList<LocalQuickFix>()
|
||||
}
|
||||
fixes!!.add(createQuickFix(intention, additionalChecker, targetElement))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-5
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containsInside
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
import java.util.*
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
abstract class SelfTargetingIntention<TElement : PsiElement>(
|
||||
val elementType: Class<TElement>,
|
||||
@@ -88,14 +89,14 @@ abstract class SelfTargetingIntention<TElement : PsiElement>(
|
||||
}
|
||||
|
||||
protected fun isIntentionBaseInspectionEnabled(project: Project, target: TElement): Boolean {
|
||||
val inspection = findInspection(javaClass) ?: return false
|
||||
val inspection = findInspection(this.javaClass.kotlin) ?: return false
|
||||
|
||||
val key = HighlightDisplayKey.find(inspection.shortName)
|
||||
if (!InspectionProjectProfileManager.getInstance(project).getInspectionProfile(target).isToolEnabled(key)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return inspection.intentions.single { it.intention.javaClass == javaClass }.additionalChecker(target, inspection)
|
||||
return inspection.intentionInfos.single { it.intention == this.javaClass.kotlin }.additionalChecker(target, inspection)
|
||||
}
|
||||
|
||||
final override fun invoke(project: Project, editor: Editor, file: PsiFile): Unit {
|
||||
@@ -109,9 +110,9 @@ abstract class SelfTargetingIntention<TElement : PsiElement>(
|
||||
override fun toString(): String = getText()
|
||||
|
||||
companion object {
|
||||
private val intentionBasedInspections = HashMap<Class<out SelfTargetingIntention<*>>, IntentionBasedInspection<*>?>()
|
||||
private val intentionBasedInspections = HashMap<KClass<out SelfTargetingIntention<*>>, IntentionBasedInspection<*>?>()
|
||||
|
||||
fun <TElement : PsiElement> findInspection(intentionClass: Class<out SelfTargetingIntention<TElement>>): IntentionBasedInspection<TElement>? {
|
||||
fun <TElement : PsiElement> findInspection(intentionClass: KClass<out SelfTargetingIntention<TElement>>): IntentionBasedInspection<TElement>? {
|
||||
if (intentionBasedInspections.containsKey(intentionClass)) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return intentionBasedInspections[intentionClass] as IntentionBasedInspection<TElement>?
|
||||
@@ -119,7 +120,7 @@ abstract class SelfTargetingIntention<TElement : PsiElement>(
|
||||
|
||||
for (extension in Extensions.getExtensions(LocalInspectionEP.LOCAL_INSPECTION)) {
|
||||
val inspection = extension.instance as? IntentionBasedInspection<*> ?: continue
|
||||
if (inspection.intentions.any { it.intention.javaClass == intentionClass }) {
|
||||
if (inspection.intentionInfos.any { it.intention == intentionClass }) {
|
||||
intentionBasedInspections[intentionClass] = inspection
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return inspection as IntentionBasedInspection<TElement>
|
||||
|
||||
Reference in New Issue
Block a user