IntentionBasedInspection: Removing synchronizing on intention instances

Recreate intention instance in `buildVisitor`
Lock caused contention for UpSource
This commit is contained in:
Pavel V. Talanov
2016-08-18 15:42:55 +03:00
parent 742d7db953
commit a683c2b68d
32 changed files with 239 additions and 229 deletions
@@ -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))
}
}
@@ -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>