Introduce MutableDiagnosticsWithSuppression and use it in BindingTrace implementations

Exposes the fact that Diagnostics is in fact a mutable entity with readonly API
Update readonly view when diagnostics are modified
Allows to avoid recreating DiagnosticsWithSuppression every time getDiagnostics() is called in DelegatingBindingTrace
  which led to severe performance issues for large files with lots of diagnostics
This commit is contained in:
Pavel V. Talanov
2014-10-09 15:13:37 +04:00
parent d7c06f0527
commit 19ec9f9eda
6 changed files with 84 additions and 19 deletions
@@ -17,31 +17,27 @@
package org.jetbrains.jet.lang.resolve;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.TestOnly;
import org.jetbrains.jet.lang.diagnostics.Diagnostic;
import org.jetbrains.jet.util.slicedmap.*;
import java.util.Collection;
import java.util.List;
public class BindingTraceContext implements BindingTrace {
private final List<Diagnostic> diagnosticList = Lists.newArrayList();
private final Diagnostics diagnostics;
// These flags are used for debugging of "Rewrite at slice..." exceptions
/* package */ final static boolean TRACK_REWRITES = false;
/* package */ final static boolean TRACK_WITH_STACK_TRACES = true;
private final MutableSlicedMap map;
private final MutableDiagnosticsWithSuppression mutableDiagnostics;
private final BindingContext bindingContext = new BindingContext() {
@NotNull
@Override
public Diagnostics getDiagnostics() {
return diagnostics;
return mutableDiagnostics;
}
@Override
@@ -71,7 +67,7 @@ public class BindingTraceContext implements BindingTrace {
private BindingTraceContext(@NotNull MutableSlicedMap map) {
this.map = map;
this.diagnostics = new DiagnosticsWithSuppression(getBindingContext(), diagnosticList);
this.mutableDiagnostics = new MutableDiagnosticsWithSuppression(bindingContext, Diagnostics.EMPTY);
}
@TestOnly
@@ -81,11 +77,11 @@ public class BindingTraceContext implements BindingTrace {
@Override
public void report(@NotNull Diagnostic diagnostic) {
diagnosticList.add(diagnostic);
mutableDiagnostics.report(diagnostic);
}
public void clearDiagnostics() {
diagnosticList.clear();
mutableDiagnostics.clear();
}
@NotNull
@@ -25,7 +25,6 @@ import org.jetbrains.annotations.TestOnly;
import org.jetbrains.jet.lang.diagnostics.Diagnostic;
import org.jetbrains.jet.util.slicedmap.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -35,16 +34,14 @@ public class DelegatingBindingTrace implements BindingTrace {
private final MutableSlicedMap map = BindingTraceContext.TRACK_REWRITES ? new TrackingSlicedMap(BindingTraceContext.TRACK_WITH_STACK_TRACES) : SlicedMapImpl.create();
private final BindingContext parentContext;
private final List<Diagnostic> diagnostics = Lists.newArrayList();
private final String name;
private final MutableDiagnosticsWithSuppression mutableDiagnostics;
private final BindingContext bindingContext = new BindingContext() {
@NotNull
@Override
public Diagnostics getDiagnostics() {
ArrayList<Diagnostic> mergedDiagnostics = new ArrayList<Diagnostic>(diagnostics);
mergedDiagnostics.addAll(parentContext.getDiagnostics().noSuppression().all());
return new DiagnosticsWithSuppression(this, mergedDiagnostics);
return mutableDiagnostics;
}
@Override
@@ -73,6 +70,7 @@ public class DelegatingBindingTrace implements BindingTrace {
public DelegatingBindingTrace(BindingContext parentContext, String debugName) {
this.parentContext = parentContext;
this.name = debugName;
this.mutableDiagnostics = new MutableDiagnosticsWithSuppression(bindingContext, parentContext.getDiagnostics());
}
public DelegatingBindingTrace(BindingContext parentContext, String debugName, @Nullable Object resolutionSubjectForMessage) {
@@ -147,7 +145,7 @@ public class DelegatingBindingTrace implements BindingTrace {
if (!commitDiagnostics) return;
for (Diagnostic diagnostic : diagnostics) {
for (Diagnostic diagnostic : mutableDiagnostics.getOwnDiagnostics()) {
if (filter == null || filter.accept(null, diagnostic.getPsiElement())) {
trace.report(diagnostic);
}
@@ -156,12 +154,12 @@ public class DelegatingBindingTrace implements BindingTrace {
public void clear() {
map.clear();
diagnostics.clear();
mutableDiagnostics.clear();
}
@Override
public void report(@NotNull Diagnostic diagnostic) {
diagnostics.add(diagnostic);
mutableDiagnostics.report(diagnostic);
}
@Override
@@ -18,8 +18,13 @@ package org.jetbrains.jet.lang.resolve
import com.intellij.psi.PsiElement
import org.jetbrains.jet.lang.diagnostics.Diagnostic
import com.intellij.openapi.util.ModificationTracker
public trait Diagnostics : Iterable<Diagnostic> {
//should not be called on readonly views
public val modificationTracker: ModificationTracker
get() = throw IllegalStateException("Trying to obtain modification tracker for Diagnostics object of class $javaClass")
public fun all(): Collection<Diagnostic>
public fun forElement(psiElement: PsiElement): Collection<Diagnostic>
@@ -31,9 +36,9 @@ public trait Diagnostics : Iterable<Diagnostic> {
override fun iterator() = all().iterator()
class object {
public val EMPTY: Diagnostics = object : Diagnostics {
override fun noSuppression(): Diagnostics = this
override val modificationTracker: ModificationTracker = ModificationTracker.NEVER_CHANGED
override fun all() = listOf<Diagnostic>()
override fun forElement(psiElement: PsiElement) = listOf<Diagnostic>()
}
@@ -19,6 +19,7 @@ package org.jetbrains.jet.lang.resolve;
import com.google.common.collect.ImmutableSet;
import com.intellij.openapi.diagnostic.Logger;
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;
@@ -217,6 +218,12 @@ public class DiagnosticsWithSuppression implements Diagnostics {
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 JetAnnotated annotated;
@@ -0,0 +1,59 @@
/*
* Copyright 2010-2014 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.jet.lang.resolve
import org.jetbrains.jet.lang.diagnostics.Diagnostic
import java.util.ArrayList
import com.intellij.openapi.util.CompositeModificationTracker
import com.intellij.util.CachedValueImpl
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.PsiElement
public class MutableDiagnosticsWithSuppression(
private val bindingContext: BindingContext,
private val delegateDiagnostics: Diagnostics = Diagnostics.EMPTY
) : Diagnostics {
private val diagnosticList = ArrayList<Diagnostic>()
//NOTE: CachedValuesManager is not used because it requires Project passed to this object
private val cache = CachedValueImpl(CachedValueProvider {
val allDiagnostics = delegateDiagnostics.noSuppression().all() + diagnosticList
CachedValueProvider.Result(DiagnosticsWithSuppression(bindingContext, allDiagnostics), modificationTracker)
})
private fun readonlyView() = cache.getValue()!!
public override val modificationTracker = CompositeModificationTracker(delegateDiagnostics.modificationTracker)
override fun all(): Collection<Diagnostic> = readonlyView().all()
override fun forElement(psiElement: PsiElement) = readonlyView().forElement(psiElement)
override fun noSuppression() = readonlyView().noSuppression()
//essential that this list is readonly
fun getOwnDiagnostics(): List<Diagnostic> = diagnosticList
fun report(diagnostic: Diagnostic) {
diagnosticList.add(diagnostic)
modificationTracker.incModificationCount()
}
fun clear() {
diagnosticList.clear()
modificationTracker.incModificationCount()
}
}
@@ -124,7 +124,7 @@ public class JetTestUtils {
@NotNull
@Override
public Diagnostics getDiagnostics() {
throw new UnsupportedOperationException(); // TODO
return Diagnostics.EMPTY;
}
@Override