Refactoring: extract separate class that can store suppressions
This commit is contained in:
committed by
Nikolay Krasko
parent
e562b73eff
commit
20379028e8
+8
-264
@@ -16,73 +16,28 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.diagnostics;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.ModificationTracker;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.util.containers.ConcurrentWeakValueHashMap;
|
||||
import com.intellij.util.containers.FilteringIterator;
|
||||
import kotlin.CollectionsKt;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
||||
import org.jetbrains.kotlin.diagnostics.Severity;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.constants.ArrayValue;
|
||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
|
||||
import org.jetbrains.kotlin.resolve.constants.StringValue;
|
||||
import org.jetbrains.kotlin.util.ExtensionProvider;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class DiagnosticsWithSuppression implements Diagnostics {
|
||||
|
||||
public interface SuppressStringProvider {
|
||||
ExtensionPointName<SuppressStringProvider> EP_NAME = ExtensionPointName.create("org.jetbrains.kotlin.suppressStringProvider");
|
||||
|
||||
@NotNull
|
||||
List<String> get(@NotNull AnnotationDescriptor annotationDescriptor);
|
||||
}
|
||||
|
||||
public interface DiagnosticSuppressor {
|
||||
ExtensionPointName<DiagnosticSuppressor> EP_NAME = ExtensionPointName.create("org.jetbrains.kotlin.diagnosticSuppressor");
|
||||
|
||||
boolean isSuppressed(@NotNull Diagnostic diagnostic);
|
||||
}
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(DiagnosticsWithSuppression.class);
|
||||
|
||||
private static final ExtensionProvider<SuppressStringProvider> ADDITIONAL_SUPPRESS_STRING_PROVIDERS
|
||||
= ExtensionProvider.create(SuppressStringProvider.EP_NAME);
|
||||
private static final ExtensionProvider<DiagnosticSuppressor> DIAGNOSTIC_SUPPRESSORS
|
||||
= ExtensionProvider.create(DiagnosticSuppressor.EP_NAME);
|
||||
|
||||
private final BindingContext context;
|
||||
private final SuppressionManager suppressionManager;
|
||||
private final Collection<Diagnostic> diagnostics;
|
||||
|
||||
// The cache is weak: we're OK with losing it
|
||||
private final Map<KtAnnotated, Suppressor> suppressors = new ConcurrentWeakValueHashMap<KtAnnotated, Suppressor>();
|
||||
|
||||
private final Function1<Diagnostic, Boolean> filter = new Function1<Diagnostic, Boolean>() {
|
||||
@Override
|
||||
public Boolean invoke(Diagnostic diagnostic) {
|
||||
return !isSuppressed(diagnostic);
|
||||
}
|
||||
};
|
||||
|
||||
private final DiagnosticsElementsCache elementsCache = new DiagnosticsElementsCache(this, filter);
|
||||
private final DiagnosticsElementsCache elementsCache;
|
||||
|
||||
public DiagnosticsWithSuppression(@NotNull BindingContext context, @NotNull Collection<Diagnostic> diagnostics) {
|
||||
this.context = context;
|
||||
this.diagnostics = diagnostics;
|
||||
this.suppressionManager = new SuppressionManager(context);
|
||||
this.elementsCache = new DiagnosticsElementsCache(this, suppressionManager.getFilter());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -97,7 +52,7 @@ public class DiagnosticsWithSuppression implements Diagnostics {
|
||||
return new FilteringIterator<Diagnostic, Diagnostic>(diagnostics.iterator(), new Condition<Diagnostic>() {
|
||||
@Override
|
||||
public boolean value(Diagnostic diagnostic) {
|
||||
return filter.invoke(diagnostic);
|
||||
return suppressionManager.getFilter().invoke(diagnostic);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -105,7 +60,7 @@ public class DiagnosticsWithSuppression implements Diagnostics {
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<Diagnostic> all() {
|
||||
return CollectionsKt.filter(diagnostics, filter);
|
||||
return CollectionsKt.filter(diagnostics, suppressionManager.getFilter());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -119,223 +74,12 @@ public class DiagnosticsWithSuppression implements Diagnostics {
|
||||
return all().isEmpty();
|
||||
}
|
||||
|
||||
private boolean isSuppressed(@NotNull Diagnostic diagnostic) {
|
||||
PsiElement element = diagnostic.getPsiElement();
|
||||
|
||||
// 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
|
||||
PsiFile file = element.getContainingFile();
|
||||
if (file instanceof KtFile) {
|
||||
if (KtPsiFactoryKt.getDoNotAnalyze((KtFile) file) != null) return true;
|
||||
}
|
||||
|
||||
for (DiagnosticSuppressor suppressor : DIAGNOSTIC_SUPPRESSORS.get()) {
|
||||
if (suppressor.isSuppressed(diagnostic)) return true;
|
||||
}
|
||||
|
||||
KtAnnotated annotated = KtStubbedPsiUtil.getPsiOrStubParent(element, KtAnnotated.class, false);
|
||||
if (annotated == null) return false;
|
||||
|
||||
return isSuppressedByAnnotated(diagnostic, annotated, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
private boolean isSuppressedByAnnotated(@NotNull Diagnostic diagnostic, @NotNull KtAnnotated annotated, int debugDepth) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Annotated: ", annotated.getName());
|
||||
LOG.debug("Depth: ", debugDepth);
|
||||
LOG.debug("Cache size: ", suppressors.size(), "\n");
|
||||
}
|
||||
|
||||
Suppressor suppressor = getOrCreateSuppressor(annotated);
|
||||
if (suppressor.isSuppressed(diagnostic)) return true;
|
||||
|
||||
KtAnnotated annotatedAbove = KtStubbedPsiUtil.getPsiOrStubParent(suppressor.getAnnotatedElement(), KtAnnotated.class, true);
|
||||
if (annotatedAbove == null) return false;
|
||||
|
||||
boolean suppressed = isSuppressedByAnnotated(diagnostic, annotatedAbove, debugDepth + 1);
|
||||
Suppressor suppressorAbove = suppressors.get(annotatedAbove);
|
||||
if (suppressorAbove != null && suppressorAbove.dominates(suppressor)) {
|
||||
suppressors.put(annotated, suppressorAbove);
|
||||
}
|
||||
|
||||
return suppressed;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Suppressor getOrCreateSuppressor(@NotNull KtAnnotated annotated) {
|
||||
Suppressor suppressor = suppressors.get(annotated);
|
||||
if (suppressor == null) {
|
||||
Set<String> strings = getSuppressingStrings(annotated);
|
||||
if (strings.isEmpty()) {
|
||||
suppressor = new EmptySuppressor(annotated);
|
||||
}
|
||||
else if (strings.size() == 1) {
|
||||
suppressor = new SingularSuppressor(annotated, strings.iterator().next());
|
||||
}
|
||||
else {
|
||||
suppressor = new MultiSuppressor(annotated, strings);
|
||||
}
|
||||
suppressors.put(annotated, suppressor);
|
||||
}
|
||||
return suppressor;
|
||||
}
|
||||
|
||||
private Set<String> getSuppressingStrings(@NotNull KtAnnotated annotated) {
|
||||
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
|
||||
|
||||
DeclarationDescriptor descriptor = context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, annotated);
|
||||
|
||||
if (descriptor != null) {
|
||||
for (AnnotationDescriptor annotationDescriptor : descriptor.getAnnotations()) {
|
||||
processAnnotation(builder, annotationDescriptor);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (KtAnnotationEntry annotationEntry : annotated.getAnnotationEntries()) {
|
||||
AnnotationDescriptor annotationDescriptor = context.get(BindingContext.ANNOTATION, annotationEntry);
|
||||
processAnnotation(builder, annotationDescriptor);
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private void processAnnotation(ImmutableSet.Builder<String> builder, AnnotationDescriptor annotationDescriptor) {
|
||||
if (annotationDescriptor == null) return;
|
||||
|
||||
for (SuppressStringProvider suppressStringProvider : ADDITIONAL_SUPPRESS_STRING_PROVIDERS.get()) {
|
||||
builder.addAll(suppressStringProvider.get(annotationDescriptor));
|
||||
}
|
||||
|
||||
if (!KotlinBuiltIns.isSuppressAnnotation(annotationDescriptor)) return;
|
||||
|
||||
// We only add strings and skip other values to facilitate recovery in presence of erroneous code
|
||||
for (ConstantValue<?> arrayValue : annotationDescriptor.getAllValueArguments().values()) {
|
||||
if ((arrayValue instanceof ArrayValue)) {
|
||||
for (ConstantValue<?> value : ((ArrayValue) arrayValue).getValue()) {
|
||||
if (value instanceof StringValue) {
|
||||
builder.add(String.valueOf(((StringValue) value).getValue()).toLowerCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isSuppressedByStrings(@NotNull Diagnostic diagnostic, @NotNull Set<String> strings) {
|
||||
if (strings.contains("warnings") && diagnostic.getSeverity() == Severity.WARNING) return true;
|
||||
|
||||
return strings.contains(diagnostic.getFactory().getName().toLowerCase());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ModificationTracker getModificationTracker() {
|
||||
throw new IllegalStateException("Trying to obtain modification tracker for readonly DiagnosticsWithSuppression.");
|
||||
}
|
||||
|
||||
private static abstract class Suppressor {
|
||||
private final KtAnnotated annotated;
|
||||
|
||||
protected Suppressor(@NotNull KtAnnotated annotated) {
|
||||
this.annotated = annotated;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public KtAnnotated getAnnotatedElement() {
|
||||
return annotated;
|
||||
}
|
||||
|
||||
public abstract boolean isSuppressed(@NotNull Diagnostic diagnostic);
|
||||
|
||||
// true is \forall x. other.isSuppressed(x) -> this.isSuppressed(x)
|
||||
public abstract boolean dominates(@NotNull Suppressor other);
|
||||
}
|
||||
|
||||
private static class EmptySuppressor extends Suppressor {
|
||||
|
||||
private EmptySuppressor(@NotNull KtAnnotated annotated) {
|
||||
super(annotated);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuppressed(@NotNull Diagnostic diagnostic) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dominates(@NotNull Suppressor other) {
|
||||
return other instanceof EmptySuppressor;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SingularSuppressor extends Suppressor {
|
||||
private final String string;
|
||||
|
||||
private SingularSuppressor(@NotNull KtAnnotated annotated, @NotNull String string) {
|
||||
super(annotated);
|
||||
this.string = string;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuppressed(@NotNull Diagnostic diagnostic) {
|
||||
return isSuppressedByStrings(diagnostic, ImmutableSet.of(string));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dominates(@NotNull Suppressor other) {
|
||||
return other instanceof EmptySuppressor
|
||||
|| (other instanceof SingularSuppressor && ((SingularSuppressor) other).string.equals(string));
|
||||
}
|
||||
}
|
||||
|
||||
private static class MultiSuppressor extends Suppressor {
|
||||
private final Set<String> strings;
|
||||
|
||||
private MultiSuppressor(@NotNull KtAnnotated annotated, @NotNull Set<String> strings) {
|
||||
super(annotated);
|
||||
this.strings = strings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuppressed(@NotNull Diagnostic diagnostic) {
|
||||
return isSuppressedByStrings(diagnostic, strings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dominates(@NotNull Suppressor other) {
|
||||
// it's too costly to check set inclusion
|
||||
return other instanceof EmptySuppressor;
|
||||
}
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
@NotNull
|
||||
public Collection<Diagnostic> getDiagnostics() {
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ public val PROPERTY_NOT_INITIALIZED_ERRORS: List<DiagnosticFactory0<KtProperty>>
|
||||
public abstract class SuppressDiagnosticsByAnnotations(
|
||||
diagnosticsToSuppress: List<DiagnosticFactory<out Diagnostic>>,
|
||||
vararg annotationsFqName: FqName
|
||||
) : DiagnosticsWithSuppression.SuppressStringProvider {
|
||||
) : SuppressStringProvider {
|
||||
|
||||
private val annotationsFqName = annotationsFqName
|
||||
private val stringsToSuppress = diagnosticsToSuppress.map { it.getName().toLowerCase() }
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.diagnostics
|
||||
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
import com.intellij.util.containers.ConcurrentWeakValueHashMap
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Severity
|
||||
import org.jetbrains.kotlin.psi.KtAnnotated
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtStubbedPsiUtil
|
||||
import org.jetbrains.kotlin.psi.doNotAnalyze
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.constants.ArrayValue
|
||||
import org.jetbrains.kotlin.resolve.constants.StringValue
|
||||
import org.jetbrains.kotlin.util.ExtensionProvider
|
||||
|
||||
interface SuppressStringProvider {
|
||||
operator fun get(annotationDescriptor: AnnotationDescriptor): List<String>
|
||||
|
||||
companion object {
|
||||
val EP_NAME = ExtensionPointName.create<SuppressStringProvider>("org.jetbrains.kotlin.suppressStringProvider")
|
||||
}
|
||||
}
|
||||
|
||||
interface DiagnosticSuppressor {
|
||||
fun isSuppressed(diagnostic: Diagnostic): Boolean
|
||||
|
||||
companion object {
|
||||
val EP_NAME = ExtensionPointName.create<DiagnosticSuppressor>("org.jetbrains.kotlin.diagnosticSuppressor")
|
||||
}
|
||||
}
|
||||
|
||||
class SuppressionManager(val context: BindingContext) {
|
||||
private val LOG = Logger.getInstance(DiagnosticsWithSuppression::class.java)
|
||||
|
||||
private val ADDITIONAL_SUPPRESS_STRING_PROVIDERS = ExtensionProvider.create(SuppressStringProvider.EP_NAME)
|
||||
private val DIAGNOSTIC_SUPPRESSORS = ExtensionProvider.create(DiagnosticSuppressor.EP_NAME)
|
||||
|
||||
// The cache is weak: we're OK with losing it
|
||||
private val suppressors = ConcurrentWeakValueHashMap<KtAnnotated, Suppressor>()
|
||||
|
||||
public val filter: (Diagnostic) -> Boolean = { diagnostic: Diagnostic -> !isSuppressed(diagnostic) }
|
||||
|
||||
private fun isSuppressed(diagnostic: Diagnostic): Boolean {
|
||||
val element = diagnostic.psiElement
|
||||
|
||||
// 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
|
||||
val file = element.containingFile
|
||||
if (file is KtFile) {
|
||||
if (file.doNotAnalyze != null) return true
|
||||
}
|
||||
|
||||
for (suppressor in DIAGNOSTIC_SUPPRESSORS.get()) {
|
||||
if (suppressor.isSuppressed(diagnostic)) return true
|
||||
}
|
||||
|
||||
val annotated = KtStubbedPsiUtil.getPsiOrStubParent(element, KtAnnotated::class.java, false) ?: return false
|
||||
|
||||
return isSuppressedByAnnotated(diagnostic, annotated, 0)
|
||||
}
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
private fun isSuppressedByAnnotated(diagnostic: Diagnostic, annotated: KtAnnotated, debugDepth: Int): Boolean {
|
||||
if (LOG.isDebugEnabled) {
|
||||
LOG.debug("Annotated: ", annotated.name)
|
||||
LOG.debug("Depth: ", debugDepth)
|
||||
LOG.debug("Cache size: ", suppressors.size, "\n")
|
||||
}
|
||||
|
||||
val suppressor = getOrCreateSuppressor(annotated)
|
||||
if (suppressor.isSuppressed(diagnostic)) return true
|
||||
|
||||
val annotatedAbove = KtStubbedPsiUtil.getPsiOrStubParent(suppressor.annotatedElement, KtAnnotated::class.java, true) ?: return false
|
||||
|
||||
val suppressed = isSuppressedByAnnotated(diagnostic, annotatedAbove, debugDepth + 1)
|
||||
val suppressorAbove = suppressors[annotatedAbove]
|
||||
if (suppressorAbove != null && suppressorAbove.dominates(suppressor)) {
|
||||
suppressors.put(annotated, suppressorAbove)
|
||||
}
|
||||
|
||||
return suppressed
|
||||
}
|
||||
|
||||
private fun getOrCreateSuppressor(annotated: KtAnnotated): Suppressor {
|
||||
var suppressor: Suppressor? = suppressors[annotated]
|
||||
if (suppressor == null) {
|
||||
val strings = getSuppressingStrings(annotated)
|
||||
if (strings.isEmpty()) {
|
||||
suppressor = EmptySuppressor(annotated)
|
||||
}
|
||||
else if (strings.size == 1) {
|
||||
suppressor = SingularSuppressor(annotated, strings.iterator().next())
|
||||
}
|
||||
else {
|
||||
suppressor = MultiSuppressor(annotated, strings)
|
||||
}
|
||||
suppressors.put(annotated, suppressor)
|
||||
}
|
||||
return suppressor
|
||||
}
|
||||
|
||||
private fun getSuppressingStrings(annotated: KtAnnotated): Set<String> {
|
||||
val builder = ImmutableSet.builder<String>()
|
||||
|
||||
val descriptor = context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, annotated)
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
private fun processAnnotation(builder: ImmutableSet.Builder<String>, annotationDescriptor: AnnotationDescriptor?) {
|
||||
if (annotationDescriptor == null) return
|
||||
|
||||
for (suppressStringProvider in ADDITIONAL_SUPPRESS_STRING_PROVIDERS.get()) {
|
||||
builder.addAll(suppressStringProvider[annotationDescriptor])
|
||||
}
|
||||
|
||||
if (!KotlinBuiltIns.isSuppressAnnotation(annotationDescriptor)) return
|
||||
|
||||
// We only add strings and skip other values to facilitate recovery in presence of erroneous code
|
||||
for (arrayValue in annotationDescriptor.allValueArguments.values) {
|
||||
if ((arrayValue is ArrayValue)) {
|
||||
for (value in arrayValue.value) {
|
||||
if (value is StringValue) {
|
||||
builder.add(value.value.toString().toLowerCase())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getDiagnosticSuppressKey(diagnostic: Diagnostic): String {
|
||||
return diagnostic.factory.name.toLowerCase()
|
||||
}
|
||||
|
||||
fun isSuppressedByStrings(key: String, strings: Set<String>, severity: Severity): Boolean {
|
||||
if (strings.contains("warnings") && severity == Severity.WARNING) return true
|
||||
|
||||
return strings.contains(key)
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class Suppressor protected constructor(val annotatedElement: KtAnnotated) {
|
||||
abstract fun isSuppressed(diagnostic: Diagnostic): Boolean
|
||||
abstract fun isSuppressed(suppressionKey: String, severity: Severity): Boolean
|
||||
|
||||
// true is \forall x. other.isSuppressed(x) -> this.isSuppressed(x)
|
||||
abstract fun dominates(other: Suppressor): Boolean
|
||||
}
|
||||
|
||||
private class EmptySuppressor(annotated: KtAnnotated) : Suppressor(annotated) {
|
||||
override fun isSuppressed(diagnostic: Diagnostic): Boolean = false
|
||||
override fun isSuppressed(suppressionKey: String, severity: Severity): Boolean = false
|
||||
override fun dominates(other: Suppressor): Boolean = other is EmptySuppressor
|
||||
}
|
||||
|
||||
private class SingularSuppressor(annotated: KtAnnotated, private val string: String) : Suppressor(annotated) {
|
||||
override fun isSuppressed(diagnostic: Diagnostic): Boolean {
|
||||
return isSuppressed(getDiagnosticSuppressKey(diagnostic), diagnostic.severity)
|
||||
}
|
||||
|
||||
override fun isSuppressed(suppressionKey: String, severity: Severity): Boolean {
|
||||
return isSuppressedByStrings(suppressionKey, ImmutableSet.of(string), severity)
|
||||
}
|
||||
|
||||
override fun dominates(other: Suppressor): Boolean {
|
||||
return other is EmptySuppressor || (other is SingularSuppressor && other.string == string)
|
||||
}
|
||||
}
|
||||
|
||||
private class MultiSuppressor(annotated: KtAnnotated, private val strings: Set<String>) : Suppressor(annotated) {
|
||||
override fun isSuppressed(diagnostic: Diagnostic): Boolean {
|
||||
return isSuppressed(getDiagnosticSuppressKey(diagnostic), diagnostic.severity)
|
||||
}
|
||||
|
||||
override fun isSuppressed(suppressionKey: String, severity: Severity): Boolean {
|
||||
return isSuppressedByStrings(suppressionKey, strings, severity)
|
||||
}
|
||||
|
||||
override fun dominates(other: Suppressor): Boolean {
|
||||
// it's too costly to check set inclusion
|
||||
return other is EmptySuppressor
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,9 @@
|
||||
<extensionPoint name="defaultErrorMessages"
|
||||
interface="org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages$Extension"/>
|
||||
<extensionPoint name="suppressStringProvider"
|
||||
interface="org.jetbrains.kotlin.resolve.diagnostics.DiagnosticsWithSuppression$SuppressStringProvider"/>
|
||||
interface="org.jetbrains.kotlin.resolve.diagnostics.SuppressStringProvider"/>
|
||||
<extensionPoint name="diagnosticSuppressor"
|
||||
interface="org.jetbrains.kotlin.resolve.diagnostics.DiagnosticsWithSuppression$DiagnosticSuppressor"/>
|
||||
interface="org.jetbrains.kotlin.resolve.diagnostics.DiagnosticSuppressor"/>
|
||||
<extensionPoint name="externalDeclarationsProvider"
|
||||
interface="org.jetbrains.kotlin.extensions.ExternalDeclarationsProvider"
|
||||
area="IDEA_PROJECT"/>
|
||||
|
||||
@@ -20,9 +20,9 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.DiagnosticsWithSuppression
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.DiagnosticSuppressor
|
||||
|
||||
public class DiagnosticSuppressorForDebugger : DiagnosticsWithSuppression.DiagnosticSuppressor {
|
||||
public class DiagnosticSuppressorForDebugger : DiagnosticSuppressor {
|
||||
override fun isSuppressed(diagnostic: Diagnostic): Boolean {
|
||||
val element = diagnostic.getPsiElement()
|
||||
val containingFile = element.getContainingFile()
|
||||
|
||||
@@ -16,20 +16,20 @@
|
||||
|
||||
package org.jetbrains.kotlin.js.analyze
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.js.PredefinedAnnotation.*
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Severity
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.js.config.LibrarySourcesConfig
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters1
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.diagnostics.Severity
|
||||
import org.jetbrains.kotlin.js.PredefinedAnnotation.*
|
||||
import org.jetbrains.kotlin.js.config.LibrarySourcesConfig
|
||||
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.DiagnosticsWithSuppression
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.SuppressDiagnosticsByAnnotations
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.DiagnosticSuppressor
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.FUNCTION_NO_BODY_ERRORS
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.PROPERTY_NOT_INITIALIZED_ERRORS
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.SuppressDiagnosticsByAnnotations
|
||||
|
||||
private val NATIVE_ANNOTATIONS = arrayOf(NATIVE.fqName, NATIVE_INVOKE.fqName, NATIVE_GETTER.fqName, NATIVE_SETTER.fqName)
|
||||
|
||||
@@ -37,7 +37,7 @@ public class SuppressUnusedParameterForJsNative : SuppressDiagnosticsByAnnotatio
|
||||
|
||||
public class SuppressNoBodyErrorsForNativeDeclarations : SuppressDiagnosticsByAnnotations(FUNCTION_NO_BODY_ERRORS + PROPERTY_NOT_INITIALIZED_ERRORS, *NATIVE_ANNOTATIONS)
|
||||
|
||||
public class SuppressUninitializedErrorsForNativeDeclarations : DiagnosticsWithSuppression.DiagnosticSuppressor {
|
||||
public class SuppressUninitializedErrorsForNativeDeclarations : DiagnosticSuppressor {
|
||||
override fun isSuppressed(diagnostic: Diagnostic): Boolean {
|
||||
if (diagnostic.getFactory() != Errors.UNINITIALIZED_VARIABLE) return false
|
||||
|
||||
@@ -50,7 +50,7 @@ public class SuppressUninitializedErrorsForNativeDeclarations : DiagnosticsWithS
|
||||
}
|
||||
}
|
||||
|
||||
public class SuppressWarningsFromExternalModules : DiagnosticsWithSuppression.DiagnosticSuppressor {
|
||||
public class SuppressWarningsFromExternalModules : DiagnosticSuppressor {
|
||||
override fun isSuppressed(diagnostic: Diagnostic): Boolean {
|
||||
val file = diagnostic.getPsiFile()
|
||||
return diagnostic.getSeverity() == Severity.WARNING &&
|
||||
|
||||
Reference in New Issue
Block a user