Extract AbstractKotlinSuppressCache to frontend.common
This commit is contained in:
@@ -7,6 +7,7 @@ dependencies {
|
||||
api(project(":compiler:config"))
|
||||
api(project(":compiler:container"))
|
||||
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
|
||||
compileOnly(intellijDep()) { includeJars("guava", rootProject = rootProject) }
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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.diagnostics
|
||||
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
|
||||
abstract class AbstractKotlinSuppressCache<Element> {
|
||||
// The cache is weak: we're OK with losing it
|
||||
protected val suppressors = ContainerUtil.createConcurrentWeakValueMap<Element, Suppressor<Element>>()
|
||||
|
||||
fun isSuppressed(element: Element, suppressionKey: String, severity: Severity) =
|
||||
isSuppressed(StringSuppressRequest(element, severity, suppressionKey.lowercase()))
|
||||
|
||||
protected open fun isSuppressed(request: SuppressRequest<Element>): Boolean {
|
||||
|
||||
val annotated = getParentAnnotatedElement(request.element, false) ?: return false
|
||||
|
||||
return isSuppressedByAnnotated(request.suppressKey, request.severity, annotated, 0)
|
||||
|
||||
}
|
||||
|
||||
protected abstract fun getParentAnnotatedElement(element: Element, strict: Boolean): Element?
|
||||
|
||||
/*
|
||||
The cache is optimized for the case where no warnings are suppressed (most frequent one)
|
||||
|
||||
trait Root {
|
||||
suppress("X")
|
||||
trait A {
|
||||
trait B {
|
||||
suppress("Y")
|
||||
trait C {
|
||||
fun foo() = warning
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Nothing is suppressed at foo, so we look above. While looking above we went up to the root (once) and propagated
|
||||
all the suppressors down, so now we have:
|
||||
|
||||
foo - suppress(Y) from C
|
||||
C - suppress(Y) from C
|
||||
B - suppress(X) from A
|
||||
A - suppress(X) from A
|
||||
Root - suppress() from Root
|
||||
|
||||
Next time we look up anything under foo, we try the Y-suppressor and then immediately the X-suppressor, then to the empty
|
||||
suppressor at the root. All the intermediate empty nodes are skipped, because every suppressor remembers its definition point.
|
||||
|
||||
This way we need no more lookups than the number of suppress() annotations from here to the root.
|
||||
*/
|
||||
protected open fun isSuppressedByAnnotated(
|
||||
suppressionKey: String,
|
||||
severity: Severity,
|
||||
annotated: Element,
|
||||
debugDepth: Int
|
||||
): Boolean {
|
||||
val suppressor = getOrCreateSuppressor(annotated)
|
||||
if (suppressor.isSuppressed(suppressionKey, severity)) return true
|
||||
|
||||
val annotatedAbove = getParentAnnotatedElement(suppressor.annotatedElement, true) ?: return false
|
||||
|
||||
val suppressed = isSuppressedByAnnotated(suppressionKey, severity, annotatedAbove, debugDepth + 1)
|
||||
val suppressorAbove = suppressors[annotatedAbove]
|
||||
if (suppressorAbove != null && suppressorAbove.dominates(suppressor)) {
|
||||
suppressors[annotated] = suppressorAbove
|
||||
}
|
||||
|
||||
return suppressed
|
||||
}
|
||||
|
||||
protected fun getOrCreateSuppressor(annotated: Element): Suppressor<Element> =
|
||||
suppressors.getOrPut(annotated) {
|
||||
val strings = getSuppressingStrings(annotated)
|
||||
when (strings.size) {
|
||||
0 -> EmptySuppressor(annotated)
|
||||
1 -> SingularSuppressor(annotated, strings.first())
|
||||
else -> MultiSuppressor(annotated, strings)
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun getSuppressingStrings(annotated: Element): Set<String>
|
||||
|
||||
companion object {
|
||||
private fun isSuppressedByStrings(key: String, strings: Set<String>, severity: Severity): Boolean =
|
||||
severity == Severity.WARNING && "warnings" in strings || key.lowercase() in strings
|
||||
}
|
||||
|
||||
protected abstract class Suppressor<Element>(val annotatedElement: Element) {
|
||||
abstract fun isSuppressed(suppressionKey: String, severity: Severity): Boolean
|
||||
|
||||
// true is \forall x. other.isSuppressed(x) -> this.isSuppressed(x)
|
||||
abstract fun dominates(other: Suppressor<Element>): Boolean
|
||||
}
|
||||
|
||||
private class EmptySuppressor<Element>(annotated: Element) : Suppressor<Element>(annotated) {
|
||||
override fun isSuppressed(suppressionKey: String, severity: Severity): Boolean = false
|
||||
override fun dominates(other: Suppressor<Element>): Boolean = other is EmptySuppressor
|
||||
}
|
||||
|
||||
private class SingularSuppressor<Element>(annotated: Element, private val string: String) : Suppressor<Element>(annotated) {
|
||||
override fun isSuppressed(suppressionKey: String, severity: Severity): Boolean {
|
||||
return isSuppressedByStrings(suppressionKey, ImmutableSet.of(string), severity)
|
||||
}
|
||||
|
||||
override fun dominates(other: Suppressor<Element>): Boolean {
|
||||
return other is EmptySuppressor || (other is SingularSuppressor && other.string == string)
|
||||
}
|
||||
}
|
||||
|
||||
private class MultiSuppressor<Element>(annotated: Element, private val strings: Set<String>) : Suppressor<Element>(annotated) {
|
||||
override fun isSuppressed(suppressionKey: String, severity: Severity): Boolean {
|
||||
return isSuppressedByStrings(suppressionKey, strings, severity)
|
||||
}
|
||||
|
||||
override fun dominates(other: Suppressor<Element>): Boolean {
|
||||
// it's too costly to check set inclusion
|
||||
return other is EmptySuppressor
|
||||
}
|
||||
}
|
||||
|
||||
protected interface SuppressRequest<Element> {
|
||||
val element: Element
|
||||
val severity: Severity
|
||||
val suppressKey: String
|
||||
}
|
||||
|
||||
private class StringSuppressRequest<Element>(
|
||||
override val element: Element,
|
||||
override val severity: Severity,
|
||||
override val suppressKey: String
|
||||
) : SuppressRequest<Element>
|
||||
}
|
||||
+1
-130
@@ -19,9 +19,9 @@ package org.jetbrains.kotlin.resolve.diagnostics
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.AbstractKotlinSuppressCache
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Severity
|
||||
import org.jetbrains.kotlin.psi.KtAnnotated
|
||||
@@ -41,135 +41,6 @@ interface DiagnosticSuppressor {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AbstractKotlinSuppressCache<Element> {
|
||||
// The cache is weak: we're OK with losing it
|
||||
protected val suppressors = ContainerUtil.createConcurrentWeakValueMap<Element, Suppressor<Element>>()
|
||||
|
||||
fun isSuppressed(element: Element, suppressionKey: String, severity: Severity) =
|
||||
isSuppressed(StringSuppressRequest(element, severity, suppressionKey.lowercase()))
|
||||
|
||||
protected open fun isSuppressed(request: SuppressRequest<Element>): Boolean {
|
||||
|
||||
val annotated = getParentAnnotatedElement(request.element, false) ?: return false
|
||||
|
||||
return isSuppressedByAnnotated(request.suppressKey, request.severity, annotated, 0)
|
||||
|
||||
}
|
||||
|
||||
protected abstract fun getParentAnnotatedElement(element: Element, strict: Boolean): Element?
|
||||
|
||||
/*
|
||||
The cache is optimized for the case where no warnings are suppressed (most frequent one)
|
||||
|
||||
trait Root {
|
||||
suppress("X")
|
||||
trait A {
|
||||
trait B {
|
||||
suppress("Y")
|
||||
trait C {
|
||||
fun foo() = warning
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Nothing is suppressed at foo, so we look above. While looking above we went up to the root (once) and propagated
|
||||
all the suppressors down, so now we have:
|
||||
|
||||
foo - suppress(Y) from C
|
||||
C - suppress(Y) from C
|
||||
B - suppress(X) from A
|
||||
A - suppress(X) from A
|
||||
Root - suppress() from Root
|
||||
|
||||
Next time we look up anything under foo, we try the Y-suppressor and then immediately the X-suppressor, then to the empty
|
||||
suppressor at the root. All the intermediate empty nodes are skipped, because every suppressor remembers its definition point.
|
||||
|
||||
This way we need no more lookups than the number of suppress() annotations from here to the root.
|
||||
*/
|
||||
protected open fun isSuppressedByAnnotated(
|
||||
suppressionKey: String,
|
||||
severity: Severity,
|
||||
annotated: Element,
|
||||
debugDepth: Int
|
||||
): Boolean {
|
||||
val suppressor = getOrCreateSuppressor(annotated)
|
||||
if (suppressor.isSuppressed(suppressionKey, severity)) return true
|
||||
|
||||
val annotatedAbove = getParentAnnotatedElement(suppressor.annotatedElement, true) ?: return false
|
||||
|
||||
val suppressed = isSuppressedByAnnotated(suppressionKey, severity, annotatedAbove, debugDepth + 1)
|
||||
val suppressorAbove = suppressors[annotatedAbove]
|
||||
if (suppressorAbove != null && suppressorAbove.dominates(suppressor)) {
|
||||
suppressors[annotated] = suppressorAbove
|
||||
}
|
||||
|
||||
return suppressed
|
||||
}
|
||||
|
||||
protected fun getOrCreateSuppressor(annotated: Element): Suppressor<Element> =
|
||||
suppressors.getOrPut(annotated) {
|
||||
val strings = getSuppressingStrings(annotated)
|
||||
when (strings.size) {
|
||||
0 -> EmptySuppressor(annotated)
|
||||
1 -> SingularSuppressor(annotated, strings.first())
|
||||
else -> MultiSuppressor(annotated, strings)
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun getSuppressingStrings(annotated: Element): Set<String>
|
||||
|
||||
companion object {
|
||||
private fun isSuppressedByStrings(key: String, strings: Set<String>, severity: Severity): Boolean =
|
||||
severity == Severity.WARNING && "warnings" in strings || key.lowercase() in strings
|
||||
}
|
||||
|
||||
protected abstract class Suppressor<Element>(val annotatedElement: Element) {
|
||||
abstract fun isSuppressed(suppressionKey: String, severity: Severity): Boolean
|
||||
|
||||
// true is \forall x. other.isSuppressed(x) -> this.isSuppressed(x)
|
||||
abstract fun dominates(other: Suppressor<Element>): Boolean
|
||||
}
|
||||
|
||||
private class EmptySuppressor<Element>(annotated: Element) : Suppressor<Element>(annotated) {
|
||||
override fun isSuppressed(suppressionKey: String, severity: Severity): Boolean = false
|
||||
override fun dominates(other: Suppressor<Element>): Boolean = other is EmptySuppressor
|
||||
}
|
||||
|
||||
private class SingularSuppressor<Element>(annotated: Element, private val string: String) : Suppressor<Element>(annotated) {
|
||||
override fun isSuppressed(suppressionKey: String, severity: Severity): Boolean {
|
||||
return isSuppressedByStrings(suppressionKey, ImmutableSet.of(string), severity)
|
||||
}
|
||||
|
||||
override fun dominates(other: Suppressor<Element>): Boolean {
|
||||
return other is EmptySuppressor || (other is SingularSuppressor && other.string == string)
|
||||
}
|
||||
}
|
||||
|
||||
private class MultiSuppressor<Element>(annotated: Element, private val strings: Set<String>) : Suppressor<Element>(annotated) {
|
||||
override fun isSuppressed(suppressionKey: String, severity: Severity): Boolean {
|
||||
return isSuppressedByStrings(suppressionKey, strings, severity)
|
||||
}
|
||||
|
||||
override fun dominates(other: Suppressor<Element>): Boolean {
|
||||
// it's too costly to check set inclusion
|
||||
return other is EmptySuppressor
|
||||
}
|
||||
}
|
||||
|
||||
protected interface SuppressRequest<Element> {
|
||||
val element: Element
|
||||
val severity: Severity
|
||||
val suppressKey: String
|
||||
}
|
||||
|
||||
private class StringSuppressRequest<Element>(
|
||||
override val element: Element,
|
||||
override val severity: Severity,
|
||||
override val suppressKey: String
|
||||
) : SuppressRequest<Element>
|
||||
}
|
||||
|
||||
abstract class KotlinSuppressCache : AbstractKotlinSuppressCache<PsiElement>() {
|
||||
|
||||
private val diagnosticSuppressors = ExtensionProvider.create(DiagnosticSuppressor.EP_NAME)
|
||||
|
||||
Reference in New Issue
Block a user