Allow to check suppression by string keys

This commit is contained in:
Nikolay Krasko
2015-11-30 17:54:59 +03:00
committed by Nikolay Krasko
parent 20379028e8
commit b61b4e1c90
3 changed files with 64 additions and 30 deletions
@@ -30,6 +30,11 @@ public abstract class DiagnosticFactory<D extends Diagnostic> {
this.severity = severity; this.severity = severity;
} }
protected DiagnosticFactory(@NotNull String name, @NotNull Severity severity) {
this.name = name;
this.severity = severity;
}
/*package*/ void setName(@NotNull String name) { /*package*/ void setName(@NotNull String name) {
this.name = name; this.name = name;
} }
@@ -30,13 +30,13 @@ import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
public class DiagnosticsWithSuppression implements Diagnostics { public class DiagnosticsWithSuppression implements Diagnostics {
private final SuppressionManager suppressionManager; private final BindingContextSuppressionManager suppressionManager;
private final Collection<Diagnostic> diagnostics; private final Collection<Diagnostic> diagnostics;
private final DiagnosticsElementsCache elementsCache; private final DiagnosticsElementsCache elementsCache;
public DiagnosticsWithSuppression(@NotNull BindingContext context, @NotNull Collection<Diagnostic> diagnostics) { public DiagnosticsWithSuppression(@NotNull BindingContext context, @NotNull Collection<Diagnostic> diagnostics) {
this.diagnostics = diagnostics; this.diagnostics = diagnostics;
this.suppressionManager = new SuppressionManager(context); this.suppressionManager = new BindingContextSuppressionManager(context);
this.elementsCache = new DiagnosticsElementsCache(this, suppressionManager.getFilter()); this.elementsCache = new DiagnosticsElementsCache(this, suppressionManager.getFilter());
} }
@@ -19,10 +19,13 @@ package org.jetbrains.kotlin.resolve.diagnostics
import com.google.common.collect.ImmutableSet import com.google.common.collect.ImmutableSet
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import com.intellij.util.containers.ConcurrentWeakValueHashMap import com.intellij.util.containers.ConcurrentWeakValueHashMap
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.psi.KtAnnotated import org.jetbrains.kotlin.psi.KtAnnotated
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
@@ -49,7 +52,7 @@ interface DiagnosticSuppressor {
} }
} }
class SuppressionManager(val context: BindingContext) { abstract class SuppressionManager {
private val LOG = Logger.getInstance(DiagnosticsWithSuppression::class.java) private val LOG = Logger.getInstance(DiagnosticsWithSuppression::class.java)
private val ADDITIONAL_SUPPRESS_STRING_PROVIDERS = ExtensionProvider.create(SuppressStringProvider.EP_NAME) private val ADDITIONAL_SUPPRESS_STRING_PROVIDERS = ExtensionProvider.create(SuppressStringProvider.EP_NAME)
@@ -58,27 +61,33 @@ class SuppressionManager(val context: BindingContext) {
// The cache is weak: we're OK with losing it // The cache is weak: we're OK with losing it
private val suppressors = ConcurrentWeakValueHashMap<KtAnnotated, Suppressor>() private val suppressors = ConcurrentWeakValueHashMap<KtAnnotated, Suppressor>()
public val filter: (Diagnostic) -> Boolean = { diagnostic: Diagnostic -> !isSuppressed(diagnostic) } val filter: (Diagnostic) -> Boolean = { diagnostic: Diagnostic -> !isSuppressed(diagnostic) }
private fun isSuppressed(diagnostic: Diagnostic): Boolean { fun isSuppressed(psiElement: PsiElement, suppressionKey: String, severity: Severity) =
val element = diagnostic.psiElement isSuppressed(StringSuppressRequest(psiElement, severity, suppressionKey.toLowerCase()))
fun isSuppressed(diagnostic: Diagnostic): Boolean = isSuppressed(DiagnosticSuppressRequest(diagnostic))
private fun isSuppressed(request: SuppressRequest): Boolean {
// If diagnostics are reported in a synthetic file generated by KtPsiFactory (dummy.kt), // If diagnostics are reported in a synthetic file generated by KtPsiFactory (dummy.kt),
// there's no point to present such diagnostics to the user, because the user didn't write this code // there's no point to present such diagnostics to the user, because the user didn't write this code
val file = element.containingFile val file = request.element.containingFile
if (file is KtFile) { if (file is KtFile) {
if (file.doNotAnalyze != null) return true if (file.doNotAnalyze != null) return true
} }
for (suppressor in DIAGNOSTIC_SUPPRESSORS.get()) { if (request is DiagnosticSuppressRequest) {
if (suppressor.isSuppressed(diagnostic)) return true for (suppressor in DIAGNOSTIC_SUPPRESSORS.get()) {
if (suppressor.isSuppressed(request.diagnostic)) return true
}
} }
val annotated = KtStubbedPsiUtil.getPsiOrStubParent(element, KtAnnotated::class.java, false) ?: return false val annotated = KtStubbedPsiUtil.getPsiOrStubParent(request.element, KtAnnotated::class.java, false) ?: return false
return isSuppressedByAnnotated(diagnostic, annotated, 0) return isSuppressedByAnnotated(request.suppressKey, request.severity, annotated, 0)
} }
/* /*
The cache is optimized for the case where no warnings are suppressed (most frequent one) The cache is optimized for the case where no warnings are suppressed (most frequent one)
@@ -108,7 +117,7 @@ class SuppressionManager(val context: BindingContext) {
This way we need no more lookups than the number of suppress() annotations from here to the root. This way we need no more lookups than the number of suppress() annotations from here to the root.
*/ */
private fun isSuppressedByAnnotated(diagnostic: Diagnostic, annotated: KtAnnotated, debugDepth: Int): Boolean { protected fun isSuppressedByAnnotated(suppressionKey: String, severity: Severity, annotated: KtAnnotated, debugDepth: Int): Boolean {
if (LOG.isDebugEnabled) { if (LOG.isDebugEnabled) {
LOG.debug("Annotated: ", annotated.name) LOG.debug("Annotated: ", annotated.name)
LOG.debug("Depth: ", debugDepth) LOG.debug("Depth: ", debugDepth)
@@ -116,11 +125,11 @@ class SuppressionManager(val context: BindingContext) {
} }
val suppressor = getOrCreateSuppressor(annotated) val suppressor = getOrCreateSuppressor(annotated)
if (suppressor.isSuppressed(diagnostic)) return true if (suppressor.isSuppressed(suppressionKey, severity)) return true
val annotatedAbove = KtStubbedPsiUtil.getPsiOrStubParent(suppressor.annotatedElement, KtAnnotated::class.java, true) ?: return false val annotatedAbove = KtStubbedPsiUtil.getPsiOrStubParent(suppressor.annotatedElement, KtAnnotated::class.java, true) ?: return false
val suppressed = isSuppressedByAnnotated(diagnostic, annotatedAbove, debugDepth + 1) val suppressed = isSuppressedByAnnotated(suppressionKey, severity, annotatedAbove, debugDepth + 1)
val suppressorAbove = suppressors[annotatedAbove] val suppressorAbove = suppressors[annotatedAbove]
if (suppressorAbove != null && suppressorAbove.dominates(suppressor)) { if (suppressorAbove != null && suppressorAbove.dominates(suppressor)) {
suppressors.put(annotated, suppressorAbove) suppressors.put(annotated, suppressorAbove)
@@ -147,28 +156,18 @@ class SuppressionManager(val context: BindingContext) {
return suppressor return suppressor
} }
abstract fun getSuppressionAnnotations(annotated: KtAnnotated): List<AnnotationDescriptor>
private fun getSuppressingStrings(annotated: KtAnnotated): Set<String> { private fun getSuppressingStrings(annotated: KtAnnotated): Set<String> {
val builder = ImmutableSet.builder<String>() val builder = ImmutableSet.builder<String>()
for (annotationDescriptor in getSuppressionAnnotations(annotated)) {
val descriptor = context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, annotated) processAnnotation(builder, annotationDescriptor)
if (descriptor != null) {
for (annotationDescriptor in descriptor.annotations) {
processAnnotation(builder, annotationDescriptor)
}
}
else {
for (annotationEntry in annotated.annotationEntries) {
val annotationDescriptor = context.get(BindingContext.ANNOTATION, annotationEntry)
processAnnotation(builder, annotationDescriptor)
}
} }
return builder.build() return builder.build()
} }
private fun processAnnotation(builder: ImmutableSet.Builder<String>, annotationDescriptor: AnnotationDescriptor?) { private fun processAnnotation(builder: ImmutableSet.Builder<String>, annotationDescriptor: AnnotationDescriptor) {
if (annotationDescriptor == null) return
for (suppressStringProvider in ADDITIONAL_SUPPRESS_STRING_PROVIDERS.get()) { for (suppressStringProvider in ADDITIONAL_SUPPRESS_STRING_PROVIDERS.get()) {
builder.addAll(suppressStringProvider[annotationDescriptor]) builder.addAll(suppressStringProvider[annotationDescriptor])
} }
@@ -241,4 +240,34 @@ class SuppressionManager(val context: BindingContext) {
return other is EmptySuppressor return other is EmptySuppressor
} }
} }
private interface SuppressRequest {
val element: PsiElement
val severity: Severity
val suppressKey: String
}
private class StringSuppressRequest(
override val element: PsiElement,
override val severity: Severity,
override val suppressKey: String) : SuppressRequest
private class DiagnosticSuppressRequest(val diagnostic: Diagnostic) : SuppressRequest {
override val element: PsiElement get() = diagnostic.psiElement
override val severity: Severity get() = diagnostic.severity
override val suppressKey: String get() = getDiagnosticSuppressKey(diagnostic)
}
}
class BindingContextSuppressionManager(val context: BindingContext) : SuppressionManager() {
override fun getSuppressionAnnotations(annotated: KtAnnotated): List<AnnotationDescriptor> {
val descriptor = context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, annotated)
if (descriptor != null) {
return descriptor.annotations.toList()
}
else {
return annotated.annotationEntries.map { context.get(BindingContext.ANNOTATION, it) }.filterNotNull()
}
}
} }