Binding trace filtering: do not calculate diagnostics if no one is going to query them

This commit is contained in:
Dmitry Jemerov
2016-09-26 12:48:46 +02:00
parent b365e547c4
commit d8b0c7aaec
24 changed files with 230 additions and 89 deletions
@@ -46,6 +46,7 @@ import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtScript import org.jetbrains.kotlin.psi.KtScript
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.BindingTraceFilter
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import org.jetbrains.kotlin.resolve.jvm.JvmClassName import org.jetbrains.kotlin.resolve.jvm.JvmClassName
@@ -67,7 +68,8 @@ class GenerationState @JvmOverloads constructor(
// partial compilation. Module chunks are treated as a single module. // partial compilation. Module chunks are treated as a single module.
// TODO: get rid of it with the proper module infrastructure // TODO: get rid of it with the proper module infrastructure
val outDirectory: File? = null, val outDirectory: File? = null,
private val onIndependentPartCompilationEnd: GenerationStateEventCallback = GenerationStateEventCallback.DO_NOTHING private val onIndependentPartCompilationEnd: GenerationStateEventCallback = GenerationStateEventCallback.DO_NOTHING,
wantsDiagnostics: Boolean = true
) { ) {
abstract class GenerateClassFilter { abstract class GenerateClassFilter {
abstract fun shouldAnnotateClass(processingClassOrObject: KtClassOrObject): Boolean abstract fun shouldAnnotateClass(processingClassOrObject: KtClassOrObject): Boolean
@@ -129,7 +131,8 @@ class GenerationState @JvmOverloads constructor(
val moduleName: String = moduleName ?: JvmCodegenUtil.getModuleName(module) val moduleName: String = moduleName ?: JvmCodegenUtil.getModuleName(module)
val classBuilderMode: ClassBuilderMode = builderFactory.classBuilderMode val classBuilderMode: ClassBuilderMode = builderFactory.classBuilderMode
val bindingTrace: BindingTrace = DelegatingBindingTrace(bindingContext, "trace in GenerationState") val bindingTrace: BindingTrace = DelegatingBindingTrace(bindingContext, "trace in GenerationState",
filter = if (wantsDiagnostics) BindingTraceFilter.ACCEPT_ALL else BindingTraceFilter.NO_DIAGNOSTICS)
val bindingContext: BindingContext = bindingTrace.bindingContext val bindingContext: BindingContext = bindingTrace.bindingContext
val typeMapper: KotlinTypeMapper = KotlinTypeMapper( val typeMapper: KotlinTypeMapper = KotlinTypeMapper(
this.bindingContext, classBuilderMode, fileClassesProvider, incrementalCacheForThisTarget, this.bindingContext, classBuilderMode, fileClassesProvider, incrementalCacheForThisTarget,
@@ -86,13 +86,17 @@ class ControlFlowInformationProvider private constructor(
markUninitializedVariables() markUninitializedVariables()
markUnusedVariables() if (trace.wantsDiagnostics()) {
markUnusedVariables()
}
markStatements() markStatements()
markUnusedExpressions() markUnusedExpressions()
checkIfExpressions() if (trace.wantsDiagnostics()) {
checkIfExpressions()
}
checkWhenExpressions() checkWhenExpressions()
@@ -82,6 +82,11 @@ public class PseudocodeUtil {
@Override @Override
public void report(@NotNull Diagnostic diagnostic) { public void report(@NotNull Diagnostic diagnostic) {
} }
@Override
public boolean wantsDiagnostics() {
return false;
}
}; };
return new ControlFlowProcessor(mockTrace).generatePseudocode(declaration); return new ControlFlowProcessor(mockTrace).generatePseudocode(declaration);
} }
@@ -30,6 +30,11 @@ public interface DiagnosticSink {
@Override @Override
public void report(@NotNull Diagnostic diagnostic) { public void report(@NotNull Diagnostic diagnostic) {
} }
@Override
public boolean wantsDiagnostics() {
return false;
}
}; };
DiagnosticSink THROW_EXCEPTION = new DiagnosticSink() { DiagnosticSink THROW_EXCEPTION = new DiagnosticSink() {
@@ -42,7 +47,13 @@ public interface DiagnosticSink {
throw new IllegalStateException(diagnostic.getFactory().getName() + ": " + diagnosticText + " " + DiagnosticUtils.atLocation(psiFile, textRanges.get(0))); throw new IllegalStateException(diagnostic.getFactory().getName() + ": " + diagnosticText + " " + DiagnosticUtils.atLocation(psiFile, textRanges.get(0)));
} }
} }
@Override
public boolean wantsDiagnostics() {
return true;
}
}; };
void report(@NotNull Diagnostic diagnostic); void report(@NotNull Diagnostic diagnostic);
boolean wantsDiagnostics();
} }
@@ -37,14 +37,15 @@ public class BindingTraceContext implements BindingTrace {
/* package */ final static boolean TRACK_WITH_STACK_TRACES = true; /* package */ final static boolean TRACK_WITH_STACK_TRACES = true;
private final MutableSlicedMap map; private final MutableSlicedMap map;
private final MutableDiagnosticsWithSuppression mutableDiagnostics; @Nullable private final MutableDiagnosticsWithSuppression mutableDiagnostics;
@NotNull private final BindingTraceFilter filter;
private final BindingContext bindingContext = new BindingContext() { private final BindingContext bindingContext = new BindingContext() {
@NotNull @NotNull
@Override @Override
public Diagnostics getDiagnostics() { public Diagnostics getDiagnostics() {
return mutableDiagnostics; return mutableDiagnostics != null ? mutableDiagnostics : Diagnostics.Companion.getEMPTY();
} }
@Override @Override
@@ -78,28 +79,45 @@ public class BindingTraceContext implements BindingTrace {
}; };
public BindingTraceContext() { public BindingTraceContext() {
this(BindingTraceFilter.Companion.getACCEPT_ALL());
}
public BindingTraceContext(BindingTraceFilter filter) {
//noinspection ConstantConditions //noinspection ConstantConditions
this(TRACK_REWRITES ? new TrackingSlicedMap(TRACK_WITH_STACK_TRACES) : SlicedMapImpl.create()); this(TRACK_REWRITES ? new TrackingSlicedMap(TRACK_WITH_STACK_TRACES) : SlicedMapImpl.create(), filter);
} }
private BindingTraceContext(@NotNull MutableSlicedMap map) { private BindingTraceContext(@NotNull MutableSlicedMap map, BindingTraceFilter filter) {
this.map = map; this.map = map;
this.mutableDiagnostics = new MutableDiagnosticsWithSuppression(bindingContext, Diagnostics.Companion.getEMPTY()); this.mutableDiagnostics = !filter.getIgnoreDiagnostics()
? new MutableDiagnosticsWithSuppression(bindingContext, Diagnostics.Companion.getEMPTY())
: null;
this.filter = filter;
} }
@TestOnly @TestOnly
public static BindingTraceContext createTraceableBindingTrace() { public static BindingTraceContext createTraceableBindingTrace() {
return new BindingTraceContext(new TrackingSlicedMap(TRACK_WITH_STACK_TRACES)); return new BindingTraceContext(new TrackingSlicedMap(TRACK_WITH_STACK_TRACES), BindingTraceFilter.Companion.getACCEPT_ALL());
} }
@Override @Override
public void report(@NotNull Diagnostic diagnostic) { public void report(@NotNull Diagnostic diagnostic) {
if (mutableDiagnostics == null) {
return;
}
mutableDiagnostics.report(diagnostic); mutableDiagnostics.report(diagnostic);
} }
public void clearDiagnostics() { public void clearDiagnostics() {
mutableDiagnostics.clear(); if (mutableDiagnostics != null) {
mutableDiagnostics.clear();
}
}
@Override
public boolean wantsDiagnostics() {
return mutableDiagnostics != null;
} }
@NotNull @NotNull
@@ -0,0 +1,31 @@
/*
* Copyright 2010-2016 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
class BindingTraceFilter(val ignoreDiagnostics: Boolean) {
companion object {
val ACCEPT_ALL = BindingTraceFilter(false)
val NO_DIAGNOSTICS = BindingTraceFilter(true)
}
fun includesEverythingIn(otherFilter: BindingTraceFilter): Boolean {
if (ignoreDiagnostics && !otherFilter.ignoreDiagnostics) {
return false
}
return true
}
}
@@ -28,12 +28,13 @@ import org.jetbrains.kotlin.util.slicedMap.*
open class DelegatingBindingTrace(private val parentContext: BindingContext, open class DelegatingBindingTrace(private val parentContext: BindingContext,
private val name: String, private val name: String,
withParentDiagnostics: Boolean = true) : BindingTrace { withParentDiagnostics: Boolean = true,
private val filter: BindingTraceFilter = BindingTraceFilter.ACCEPT_ALL) : BindingTrace {
private val map = if (BindingTraceContext.TRACK_REWRITES) TrackingSlicedMap(BindingTraceContext.TRACK_WITH_STACK_TRACES) else SlicedMapImpl.create() private val map = if (BindingTraceContext.TRACK_REWRITES) TrackingSlicedMap(BindingTraceContext.TRACK_WITH_STACK_TRACES) else SlicedMapImpl.create()
private val mutableDiagnostics: MutableDiagnosticsWithSuppression private val mutableDiagnostics: MutableDiagnosticsWithSuppression?
private inner class MyBindingContext : BindingContext { private inner class MyBindingContext : BindingContext {
override fun getDiagnostics(): Diagnostics = mutableDiagnostics override fun getDiagnostics(): Diagnostics = mutableDiagnostics ?: Diagnostics.EMPTY
override fun <K, V> get(slice: ReadOnlySlice<K, V>, key: K): V? { override fun <K, V> get(slice: ReadOnlySlice<K, V>, key: K): V? {
return this@DelegatingBindingTrace.get(slice, key) return this@DelegatingBindingTrace.get(slice, key)
@@ -60,7 +61,9 @@ open class DelegatingBindingTrace(private val parentContext: BindingContext,
private val bindingContext = MyBindingContext() private val bindingContext = MyBindingContext()
init { init {
this.mutableDiagnostics = if (withParentDiagnostics) this.mutableDiagnostics = if (filter.ignoreDiagnostics)
null
else if (withParentDiagnostics)
MutableDiagnosticsWithSuppression(bindingContext, parentContext.diagnostics) MutableDiagnosticsWithSuppression(bindingContext, parentContext.diagnostics)
else else
MutableDiagnosticsWithSuppression(bindingContext) MutableDiagnosticsWithSuppression(bindingContext)
@@ -68,7 +71,11 @@ open class DelegatingBindingTrace(private val parentContext: BindingContext,
constructor(parentContext: BindingContext, constructor(parentContext: BindingContext,
debugName: String, debugName: String,
resolutionSubjectForMessage: Any?) : this(parentContext, AnalyzingUtils.formDebugNameForBindingTrace(debugName, resolutionSubjectForMessage)) { resolutionSubjectForMessage: Any?,
filter: BindingTraceFilter = BindingTraceFilter.ACCEPT_ALL)
: this(parentContext,
AnalyzingUtils.formDebugNameForBindingTrace(debugName, resolutionSubjectForMessage),
filter = filter) {
} }
override fun getBindingContext(): BindingContext = bindingContext override fun getBindingContext(): BindingContext = bindingContext
@@ -130,12 +137,17 @@ open class DelegatingBindingTrace(private val parentContext: BindingContext,
fun clear() { fun clear() {
map.clear() map.clear()
mutableDiagnostics.clear() mutableDiagnostics?.clear()
} }
override fun report(diagnostic: Diagnostic) { override fun report(diagnostic: Diagnostic) {
if (mutableDiagnostics == null) {
return
}
mutableDiagnostics.report(diagnostic) mutableDiagnostics.report(diagnostic)
} }
override fun wantsDiagnostics(): Boolean = mutableDiagnostics != null
override fun toString(): String = name override fun toString(): String = name
} }
@@ -91,5 +91,9 @@ public class ObservableBindingTrace implements BindingTrace {
handlers = handlers.plus(slice, handler); handlers = handlers.plus(slice, handler);
return this; return this;
} }
@Override
public boolean wantsDiagnostics() {
return originalTrace.wantsDiagnostics();
}
} }
@@ -23,7 +23,12 @@ public class TemporaryBindingTrace extends DelegatingBindingTrace {
@NotNull @NotNull
public static TemporaryBindingTrace create(@NotNull BindingTrace trace, String debugName) { public static TemporaryBindingTrace create(@NotNull BindingTrace trace, String debugName) {
return new TemporaryBindingTrace(trace, debugName); return create(trace, debugName, BindingTraceFilter.Companion.getACCEPT_ALL());
}
@NotNull
public static TemporaryBindingTrace create(@NotNull BindingTrace trace, String debugName, BindingTraceFilter filter) {
return new TemporaryBindingTrace(trace, debugName, filter);
} }
@NotNull @NotNull
@@ -33,8 +38,8 @@ public class TemporaryBindingTrace extends DelegatingBindingTrace {
protected final BindingTrace trace; protected final BindingTrace trace;
protected TemporaryBindingTrace(@NotNull BindingTrace trace, String debugName) { protected TemporaryBindingTrace(@NotNull BindingTrace trace, String debugName, BindingTraceFilter filter) {
super(trace.getBindingContext(), debugName, true); super(trace.getBindingContext(), debugName, true, filter);
this.trace = trace; this.trace = trace;
} }
@@ -47,4 +52,9 @@ public class TemporaryBindingTrace extends DelegatingBindingTrace {
addOwnDataTo(trace, filter, commitDiagnostics); addOwnDataTo(trace, filter, commitDiagnostics);
clear(); clear();
} }
@Override
public boolean wantsDiagnostics() {
return trace.wantsDiagnostics();
}
} }
@@ -94,9 +94,11 @@ class CallCompleter(
if (calleeExpression != null && !calleeExpression.isFakeElement) calleeExpression if (calleeExpression != null && !calleeExpression.isFakeElement) calleeExpression
else resolvedCall.call.callElement else resolvedCall.call.callElement
val callCheckerContext = CallCheckerContext(context, languageVersionSettings) if (context.trace.wantsDiagnostics()) {
for (callChecker in callCheckers) { val callCheckerContext = CallCheckerContext(context, languageVersionSettings)
callChecker.check(resolvedCall, reportOn, callCheckerContext) for (callChecker in callCheckers) {
callChecker.check(resolvedCall, reportOn, callCheckerContext)
}
} }
resolveHandleResultCallForCoroutineLambdaExpressions(context, resolvedCall) resolveHandleResultCallForCoroutineLambdaExpressions(context, resolvedCall)
@@ -563,7 +563,7 @@ public class CallResolver {
if (CallResolverUtilKt.isInvokeCallOnVariable(call)) return; if (CallResolverUtilKt.isInvokeCallOnVariable(call)) return;
DelegatingBindingTrace deltasTraceToCacheResolve = new DelegatingBindingTrace( DelegatingBindingTrace deltasTraceToCacheResolve = new DelegatingBindingTrace(
BindingContext.EMPTY, "delta trace for caching resolve of", context.call); BindingContext.EMPTY, "delta trace for caching resolve of", context.call, BindingTraceFilter.Companion.getACCEPT_ALL());
traceToResolveCall.addOwnDataTo(deltasTraceToCacheResolve); traceToResolveCall.addOwnDataTo(deltasTraceToCacheResolve);
context.resolutionResultsCache.record(call, results, context, tracing, deltasTraceToCacheResolve); context.resolutionResultsCache.record(call, results, context, tracing, deltasTraceToCacheResolve);
@@ -37,7 +37,7 @@ class MutableDiagnosticsWithSuppression @JvmOverloads constructor(
CachedValueProvider.Result(DiagnosticsWithSuppression(bindingContext, allDiagnostics), modificationTracker) CachedValueProvider.Result(DiagnosticsWithSuppression(bindingContext, allDiagnostics), modificationTracker)
}) })
private fun readonlyView() = cache.value!! private fun readonlyView(): DiagnosticsWithSuppression = cache.value!!
override val modificationTracker = CompositeModificationTracker(delegateDiagnostics.modificationTracker) override val modificationTracker = CompositeModificationTracker(delegateDiagnostics.modificationTracker)
@@ -46,7 +46,9 @@ class MutableDiagnosticsWithSuppression @JvmOverloads constructor(
override fun noSuppression() = readonlyView().noSuppression() override fun noSuppression() = readonlyView().noSuppression()
//essential that this list is readonly //essential that this list is readonly
fun getOwnDiagnostics(): List<Diagnostic> = diagnosticList fun getOwnDiagnostics(): List<Diagnostic> {
return diagnosticList
}
fun report(diagnostic: Diagnostic) { fun report(diagnostic: Diagnostic) {
diagnosticList.add(diagnostic) diagnosticList.add(diagnostic)
@@ -56,7 +56,7 @@ class FileScopeFactory(
fun createScopesForFile(file: KtFile, existingImports: ImportingScope? = null): FileScopes { fun createScopesForFile(file: KtFile, existingImports: ImportingScope? = null): FileScopes {
val debugName = "LazyFileScope for file " + file.name val debugName = "LazyFileScope for file " + file.name
val tempTrace = TemporaryBindingTrace.create(bindingTrace, "Transient trace for default imports lazy resolve") val tempTrace = TemporaryBindingTrace.create(bindingTrace, "Transient trace for default imports lazy resolve", false)
infix fun <T> Collection<T>.concat(other: Collection<T>?) = infix fun <T> Collection<T>.concat(other: Collection<T>?) =
if (other == null || other.isEmpty()) this else this + other if (other == null || other.isEmpty()) this else this + other
@@ -81,5 +81,7 @@ class LockBasedLazyResolveStorageManager(private val storageManager: StorageMana
override fun report(diagnostic: Diagnostic) { override fun report(diagnostic: Diagnostic) {
storageManager.compute { trace.report(diagnostic) } storageManager.compute { trace.report(diagnostic) }
} }
override fun wantsDiagnostics() = trace.wantsDiagnostics()
} }
} }
@@ -609,9 +609,11 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
trace.record(RESOLVED_CALL, call, resolvedCall); trace.record(RESOLVED_CALL, call, resolvedCall);
trace.record(CALL, expression, call); trace.record(CALL, expression, call);
CallCheckerContext callCheckerContext = new CallCheckerContext(context, components.languageVersionSettings); if (context.trace.wantsDiagnostics()) {
for (CallChecker checker : components.callCheckers) { CallCheckerContext callCheckerContext = new CallCheckerContext(context, components.languageVersionSettings);
checker.check(resolvedCall, expression, callCheckerContext); for (CallChecker checker : components.callCheckers) {
checker.check(resolvedCall, expression, callCheckerContext);
}
} }
} }
@@ -910,7 +912,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
if (KtTokens.AUGMENTED_ASSIGNMENTS.contains(operationType) if (KtTokens.AUGMENTED_ASSIGNMENTS.contains(operationType)
|| operationType == KtTokens.PLUSPLUS || operationType == KtTokens.MINUSMINUS) { || operationType == KtTokens.PLUSPLUS || operationType == KtTokens.MINUSMINUS) {
ResolvedCall<?> resolvedCall = ignoreReportsTrace.get(INDEXED_LVALUE_SET, expression); ResolvedCall<?> resolvedCall = ignoreReportsTrace.get(INDEXED_LVALUE_SET, expression);
if (resolvedCall != null) { if (resolvedCall != null && trace.wantsDiagnostics()) {
// Call must be validated with the actual, not temporary trace in order to report operator diagnostic // Call must be validated with the actual, not temporary trace in order to report operator diagnostic
// Only unary assignment expressions (++, --) and +=/... must be checked, normal assignments have the proper trace // Only unary assignment expressions (++, --) and +=/... must be checked, normal assignments have the proper trace
CallCheckerContext callCheckerContext = new CallCheckerContext(context, trace, components.languageVersionSettings); CallCheckerContext callCheckerContext = new CallCheckerContext(context, trace, components.languageVersionSettings);
@@ -979,9 +981,11 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
); );
resolvedCall.markCallAsCompleted(); resolvedCall.markCallAsCompleted();
CallCheckerContext callCheckerContext = new CallCheckerContext(context, components.languageVersionSettings); if (context.trace.wantsDiagnostics()) {
for (CallChecker checker : components.callCheckers) { CallCheckerContext callCheckerContext = new CallCheckerContext(context, components.languageVersionSettings);
checker.check(resolvedCall, expression, callCheckerContext); for (CallChecker checker : components.callCheckers) {
checker.check(resolvedCall, expression, callCheckerContext);
}
} }
} }
@@ -99,7 +99,8 @@ abstract class LightClassDataProvider<T : WithFileStubAndExtraDiagnostics>(
context.bindingContext, context.bindingContext,
files.toMutableList(), files.toMutableList(),
CompilerConfiguration.EMPTY, CompilerConfiguration.EMPTY,
generateClassFilter generateClassFilter,
wantsDiagnostics = false
) )
state.beforeCompile() state.beforeCompile()
@@ -196,6 +196,11 @@ public class KotlinTestUtils {
throw new IllegalStateException("Unresolved: " + diagnostic.getPsiElement().getText()); throw new IllegalStateException("Unresolved: " + diagnostic.getPsiElement().getText());
} }
} }
@Override
public boolean wantsDiagnostics() {
return false;
}
}; };
public static BindingTrace DUMMY_EXCEPTION_ON_ERROR_TRACE = new BindingTrace() { public static BindingTrace DUMMY_EXCEPTION_ON_ERROR_TRACE = new BindingTrace() {
@@ -276,6 +281,11 @@ public class KotlinTestUtils {
throw new IllegalStateException(DefaultErrorMessages.render(diagnostic)); throw new IllegalStateException(DefaultErrorMessages.render(diagnostic));
} }
} }
@Override
public boolean wantsDiagnostics() {
return true;
}
}; };
// We suspect sequences of eight consecutive hexadecimal digits to be a package part hash code // We suspect sequences of eight consecutive hexadecimal digits to be a package part hash code
@@ -16,8 +16,21 @@
package org.jetbrains.kotlin.resolve.lazy package org.jetbrains.kotlin.resolve.lazy
enum class BodyResolveMode { import org.jetbrains.kotlin.resolve.BindingTraceFilter
FULL,
PARTIAL, open class BodyResolveMode {
PARTIAL_FOR_COMPLETION open val bindingTraceFilter: BindingTraceFilter = BindingTraceFilter.ACCEPT_ALL
companion object {
@JvmField val FULL = BodyResolveMode()
@JvmField val PARTIAL = object : BodyResolveMode() {
override val bindingTraceFilter: BindingTraceFilter
get() = BindingTraceFilter.NO_DIAGNOSTICS
}
@JvmField val PARTIAL_WITH_DIAGNOSTICS = BodyResolveMode()
@JvmField val PARTIAL_FOR_COMPLETION = BodyResolveMode()
}
} }
@@ -83,10 +83,11 @@ class ResolveElementCache(
}, },
false) false)
private class CachedPartialResolve(val bindingContext: BindingContext, file: KtFile) { private class CachedPartialResolve(val bindingContext: BindingContext, file: KtFile, val filter: BindingTraceFilter) {
private val modificationStamp: Long? = modificationStamp(file) private val modificationStamp: Long? = modificationStamp(file)
fun isUpToDate(file: KtFile) = modificationStamp == modificationStamp(file) fun isUpToDate(file: KtFile, newFilter: BindingTraceFilter) =
modificationStamp == modificationStamp(file) && filter.includesEverythingIn(newFilter)
private fun modificationStamp(file: KtFile): Long? { private fun modificationStamp(file: KtFile): Long? {
return if (!file.isPhysical) // for non-physical file we don't get MODIFICATION_COUNT increased and must reset data on any modification of the file return if (!file.isPhysical) // for non-physical file we don't get MODIFICATION_COUNT increased and must reset data on any modification of the file
@@ -117,7 +118,7 @@ class ResolveElementCache(
= getElementsAdditionalResolve(function, null, BodyResolveMode.FULL) = getElementsAdditionalResolve(function, null, BodyResolveMode.FULL)
fun resolvePrimaryConstructorParametersDefaultValues(ktClass: KtClass): BindingContext { fun resolvePrimaryConstructorParametersDefaultValues(ktClass: KtClass): BindingContext {
return constructorAdditionalResolve(resolveSession, ktClass, ktClass.getContainingKtFile()).bindingContext return constructorAdditionalResolve(resolveSession, ktClass, ktClass.getContainingKtFile(), BindingTraceFilter.NO_DIAGNOSTICS).bindingContext
} }
@Deprecated("Use getElementsAdditionalResolve") @Deprecated("Use getElementsAdditionalResolve")
@@ -149,7 +150,16 @@ class ResolveElementCache(
return bindingContext return bindingContext
} }
BodyResolveMode.PARTIAL -> { BodyResolveMode.PARTIAL_FOR_COMPLETION -> {
if (resolveElement !is KtDeclaration) {
return getElementsAdditionalResolve(resolveElement, null, BodyResolveMode.FULL)
}
// not cached
return performElementAdditionalResolve(resolveElement, contextElements, bodyResolveMode).first
}
else -> {
if (resolveElement !is KtDeclaration) { if (resolveElement !is KtDeclaration) {
return getElementsAdditionalResolve(resolveElement, null, BodyResolveMode.FULL) return getElementsAdditionalResolve(resolveElement, null, BodyResolveMode.FULL)
} }
@@ -158,18 +168,18 @@ class ResolveElementCache(
val statementsToResolve = contextElements!!.map { PartialBodyResolveFilter.findStatementToResolve(it, resolveElement) }.distinct() val statementsToResolve = contextElements!!.map { PartialBodyResolveFilter.findStatementToResolve(it, resolveElement) }.distinct()
val partialResolveMap = partialBodyResolveCache.value val partialResolveMap = partialBodyResolveCache.value
val cachedResults = statementsToResolve.map { partialResolveMap[it ?: resolveElement] } val cachedResults = statementsToResolve.map { partialResolveMap[it ?: resolveElement] }
if (cachedResults.all { it != null && it.isUpToDate(file) }) { // partial resolve is already cached for these statements if (cachedResults.all { it != null && it.isUpToDate(file, bodyResolveMode.bindingTraceFilter) }) { // partial resolve is already cached for these statements
return CompositeBindingContext.create(cachedResults.map { it!!.bindingContext }.distinct()) return CompositeBindingContext.create(cachedResults.map { it!!.bindingContext }.distinct())
} }
val (bindingContext, statementFilter) = performElementAdditionalResolve(resolveElement, contextElements, BodyResolveMode.PARTIAL) val (bindingContext, statementFilter) = performElementAdditionalResolve(resolveElement, contextElements, bodyResolveMode)
if (statementFilter == StatementFilter.NONE) { // partial resolve is not supported for the given declaration - full resolve performed instead if (statementFilter == StatementFilter.NONE) { // partial resolve is not supported for the given declaration - full resolve performed instead
fullResolveMap[resolveElement] = CachedFullResolve(bindingContext, resolveElement) fullResolveMap[resolveElement] = CachedFullResolve(bindingContext, resolveElement)
return bindingContext return bindingContext
} }
val resolveToCache = CachedPartialResolve(bindingContext, file) val resolveToCache = CachedPartialResolve(bindingContext, file, bodyResolveMode.bindingTraceFilter)
for (statement in (statementFilter as PartialBodyResolveFilter).allStatementsToResolve) { for (statement in (statementFilter as PartialBodyResolveFilter).allStatementsToResolve) {
if (!partialResolveMap.containsKey(statement) && bindingContext[BindingContext.PROCESSED, statement] == true) { if (!partialResolveMap.containsKey(statement) && bindingContext[BindingContext.PROCESSED, statement] == true) {
@@ -182,15 +192,6 @@ class ResolveElementCache(
return bindingContext return bindingContext
} }
BodyResolveMode.PARTIAL_FOR_COMPLETION -> {
if (resolveElement !is KtDeclaration) {
return getElementsAdditionalResolve(resolveElement, null, BodyResolveMode.FULL)
}
// not cached
return performElementAdditionalResolve(resolveElement, contextElements, bodyResolveMode).first
}
} }
} }
@@ -311,17 +312,17 @@ class ResolveElementCache(
} }
val trace: BindingTrace = when (resolveElement) { val trace: BindingTrace = when (resolveElement) {
is KtNamedFunction -> functionAdditionalResolve(resolveSession, resolveElement, file, createStatementFilter()) is KtNamedFunction -> functionAdditionalResolve(resolveSession, resolveElement, file, createStatementFilter(), bodyResolveMode.bindingTraceFilter)
is KtAnonymousInitializer -> initializerAdditionalResolve(resolveSession, resolveElement, file, createStatementFilter()) is KtAnonymousInitializer -> initializerAdditionalResolve(resolveSession, resolveElement, file, createStatementFilter(), bodyResolveMode.bindingTraceFilter)
is KtSecondaryConstructor -> secondaryConstructorAdditionalResolve(resolveSession, resolveElement, file, createStatementFilter()) is KtSecondaryConstructor -> secondaryConstructorAdditionalResolve(resolveSession, resolveElement, file, createStatementFilter(), bodyResolveMode.bindingTraceFilter)
is KtProperty -> propertyAdditionalResolve(resolveSession, resolveElement, file, createStatementFilter()) is KtProperty -> propertyAdditionalResolve(resolveSession, resolveElement, file, createStatementFilter(), bodyResolveMode.bindingTraceFilter)
is KtSuperTypeList -> delegationSpecifierAdditionalResolve(resolveSession, resolveElement, resolveElement.getParent() as KtClassOrObject, file) is KtSuperTypeList -> delegationSpecifierAdditionalResolve(resolveSession, resolveElement, resolveElement.getParent() as KtClassOrObject, file, bodyResolveMode.bindingTraceFilter)
is KtInitializerList -> delegationSpecifierAdditionalResolve(resolveSession, resolveElement, resolveElement.getParent() as KtEnumEntry, file) is KtInitializerList -> delegationSpecifierAdditionalResolve(resolveSession, resolveElement, resolveElement.getParent() as KtEnumEntry, file, bodyResolveMode.bindingTraceFilter)
is KtImportList -> { is KtImportList -> {
val resolver = resolveSession.fileScopeProvider.getImportResolver(resolveElement.getContainingKtFile()) val resolver = resolveSession.fileScopeProvider.getImportResolver(resolveElement.getContainingKtFile())
@@ -341,9 +342,9 @@ class ResolveElementCache(
is KtAnnotationEntry -> annotationAdditionalResolve(resolveSession, resolveElement) is KtAnnotationEntry -> annotationAdditionalResolve(resolveSession, resolveElement)
is KtClass -> constructorAdditionalResolve(resolveSession, resolveElement, file) is KtClass -> constructorAdditionalResolve(resolveSession, resolveElement, file, bodyResolveMode.bindingTraceFilter)
is KtTypeAlias -> typealiasAdditionalResolve(resolveSession, resolveElement) is KtTypeAlias -> typealiasAdditionalResolve(resolveSession, resolveElement, bodyResolveMode.bindingTraceFilter)
is KtTypeParameter -> typeParameterAdditionalResolve(resolveSession, resolveElement) is KtTypeParameter -> typeParameterAdditionalResolve(resolveSession, resolveElement)
@@ -353,7 +354,7 @@ class ResolveElementCache(
else -> { else -> {
if (resolveElement.getParentOfType<KtPackageDirective>(true) != null) { if (resolveElement.getParentOfType<KtPackageDirective>(true) != null) {
packageRefAdditionalResolve(resolveSession, resolveElement) packageRefAdditionalResolve(resolveSession, resolveElement, bodyResolveMode.bindingTraceFilter)
} }
else { else {
error("Invalid type of the topmost parent: $resolveElement\n${resolveElement.getElementTextWithContext()}") error("Invalid type of the topmost parent: $resolveElement\n${resolveElement.getElementTextWithContext()}")
@@ -368,8 +369,9 @@ class ResolveElementCache(
return Pair(trace.bindingContext, statementFilterUsed) return Pair(trace.bindingContext, statementFilterUsed)
} }
private fun packageRefAdditionalResolve(resolveSession: ResolveSession, ktElement: KtElement): BindingTrace { private fun packageRefAdditionalResolve(resolveSession: ResolveSession, ktElement: KtElement,
val trace = createDelegatingTrace(ktElement) bindingTraceFilter: BindingTraceFilter): BindingTrace {
val trace = createDelegatingTrace(ktElement, bindingTraceFilter)
if (ktElement is KtSimpleNameExpression) { if (ktElement is KtSimpleNameExpression) {
val header = ktElement.getParentOfType<KtPackageDirective>(true)!! val header = ktElement.getParentOfType<KtPackageDirective>(true)!!
@@ -398,7 +400,7 @@ class ResolveElementCache(
} }
private fun codeFragmentAdditionalResolve(codeFragment: KtCodeFragment, bodyResolveMode: BodyResolveMode): BindingTrace { private fun codeFragmentAdditionalResolve(codeFragment: KtCodeFragment, bodyResolveMode: BodyResolveMode): BindingTrace {
val trace = createDelegatingTrace(codeFragment) val trace = createDelegatingTrace(codeFragment, bodyResolveMode.bindingTraceFilter)
val contextResolveMode = if (bodyResolveMode == BodyResolveMode.PARTIAL) val contextResolveMode = if (bodyResolveMode == BodyResolveMode.PARTIAL)
BodyResolveMode.PARTIAL_FOR_COMPLETION BodyResolveMode.PARTIAL_FOR_COMPLETION
@@ -456,8 +458,9 @@ class ResolveElementCache(
} }
private fun delegationSpecifierAdditionalResolve(resolveSession: ResolveSession, ktElement: KtElement, private fun delegationSpecifierAdditionalResolve(resolveSession: ResolveSession, ktElement: KtElement,
classOrObject: KtClassOrObject, file: KtFile): BindingTrace { classOrObject: KtClassOrObject, file: KtFile,
val trace = createDelegatingTrace(ktElement) bindingTraceFilter: BindingTraceFilter): BindingTrace {
val trace = createDelegatingTrace(ktElement, bindingTraceFilter)
val descriptor = resolveSession.resolveToDescriptor(classOrObject) as LazyClassDescriptor val descriptor = resolveSession.resolveToDescriptor(classOrObject) as LazyClassDescriptor
// Activate resolving of supertypes // Activate resolving of supertypes
@@ -475,8 +478,10 @@ class ResolveElementCache(
} }
private fun propertyAdditionalResolve(resolveSession: ResolveSession, property: KtProperty, private fun propertyAdditionalResolve(resolveSession: ResolveSession, property: KtProperty,
file: KtFile, statementFilter: StatementFilter): BindingTrace { file: KtFile,
val trace = createDelegatingTrace(property) statementFilter: StatementFilter,
bindingTraceFilter: BindingTraceFilter): BindingTrace {
val trace = createDelegatingTrace(property, bindingTraceFilter)
val bodyResolver = createBodyResolver(resolveSession, trace, file, statementFilter) val bodyResolver = createBodyResolver(resolveSession, trace, file, statementFilter)
val descriptor = resolveSession.resolveToDescriptor(property) as PropertyDescriptor val descriptor = resolveSession.resolveToDescriptor(property) as PropertyDescriptor
@@ -501,8 +506,9 @@ class ResolveElementCache(
} }
private fun functionAdditionalResolve(resolveSession: ResolveSession, namedFunction: KtNamedFunction, file: KtFile, private fun functionAdditionalResolve(resolveSession: ResolveSession, namedFunction: KtNamedFunction, file: KtFile,
statementFilter: StatementFilter): BindingTrace { statementFilter: StatementFilter,
val trace = createDelegatingTrace(namedFunction) bindingTraceFilter: BindingTraceFilter): BindingTrace {
val trace = createDelegatingTrace(namedFunction, bindingTraceFilter)
val scope = resolveSession.declarationScopeProvider.getResolutionScopeForDeclaration(namedFunction) val scope = resolveSession.declarationScopeProvider.getResolutionScopeForDeclaration(namedFunction)
val functionDescriptor = resolveSession.resolveToDescriptor(namedFunction) as FunctionDescriptor val functionDescriptor = resolveSession.resolveToDescriptor(namedFunction) as FunctionDescriptor
@@ -517,8 +523,9 @@ class ResolveElementCache(
} }
private fun secondaryConstructorAdditionalResolve(resolveSession: ResolveSession, constructor: KtSecondaryConstructor, private fun secondaryConstructorAdditionalResolve(resolveSession: ResolveSession, constructor: KtSecondaryConstructor,
file: KtFile, statementFilter: StatementFilter): BindingTrace { file: KtFile, statementFilter: StatementFilter,
val trace = createDelegatingTrace(constructor) bindingTraceFilter: BindingTraceFilter): BindingTrace {
val trace = createDelegatingTrace(constructor, bindingTraceFilter)
val scope = resolveSession.declarationScopeProvider.getResolutionScopeForDeclaration(constructor) val scope = resolveSession.declarationScopeProvider.getResolutionScopeForDeclaration(constructor)
val constructorDescriptor = resolveSession.resolveToDescriptor(constructor) as ClassConstructorDescriptor val constructorDescriptor = resolveSession.resolveToDescriptor(constructor) as ClassConstructorDescriptor
@@ -532,8 +539,8 @@ class ResolveElementCache(
return trace return trace
} }
private fun constructorAdditionalResolve(resolveSession: ResolveSession, klass: KtClass, file: KtFile): BindingTrace { private fun constructorAdditionalResolve(resolveSession: ResolveSession, klass: KtClass, file: KtFile, filter : BindingTraceFilter): BindingTrace {
val trace = createDelegatingTrace(klass) val trace = createDelegatingTrace(klass, filter)
val scope = resolveSession.declarationScopeProvider.getResolutionScopeForDeclaration(klass) val scope = resolveSession.declarationScopeProvider.getResolutionScopeForDeclaration(klass)
val classDescriptor = resolveSession.resolveToDescriptor(klass) as ClassDescriptor val classDescriptor = resolveSession.resolveToDescriptor(klass) as ClassDescriptor
@@ -547,8 +554,9 @@ class ResolveElementCache(
return trace return trace
} }
private fun typealiasAdditionalResolve(resolveSession: ResolveSession, typeAlias: KtTypeAlias): BindingTrace { private fun typealiasAdditionalResolve(resolveSession: ResolveSession, typeAlias: KtTypeAlias,
val trace = createDelegatingTrace(typeAlias) bindingTraceFilter: BindingTraceFilter): BindingTrace {
val trace = createDelegatingTrace(typeAlias, bindingTraceFilter)
val typeAliasDescriptor = resolveSession.resolveToDescriptor(typeAlias) val typeAliasDescriptor = resolveSession.resolveToDescriptor(typeAlias)
ForceResolveUtil.forceResolveAllContents(typeAliasDescriptor) ForceResolveUtil.forceResolveAllContents(typeAliasDescriptor)
forceResolveAnnotationsInside(typeAlias) forceResolveAnnotationsInside(typeAlias)
@@ -556,8 +564,9 @@ class ResolveElementCache(
} }
private fun initializerAdditionalResolve(resolveSession: ResolveSession, anonymousInitializer: KtAnonymousInitializer, private fun initializerAdditionalResolve(resolveSession: ResolveSession, anonymousInitializer: KtAnonymousInitializer,
file: KtFile, statementFilter: StatementFilter): BindingTrace { file: KtFile, statementFilter: StatementFilter,
val trace = createDelegatingTrace(anonymousInitializer) bindingTraceFilter: BindingTraceFilter): BindingTrace {
val trace = createDelegatingTrace(anonymousInitializer, bindingTraceFilter)
val classOrObjectDescriptor = resolveSession.resolveToDescriptor(anonymousInitializer.containingDeclaration) as LazyClassDescriptor val classOrObjectDescriptor = resolveSession.resolveToDescriptor(anonymousInitializer.containingDeclaration) as LazyClassDescriptor
@@ -595,9 +604,9 @@ class ResolveElementCache(
} }
// All additional resolve should be done to separate trace // All additional resolve should be done to separate trace
private fun createDelegatingTrace(resolveElement: KtElement): BindingTrace { private fun createDelegatingTrace(resolveElement: KtElement, filter: BindingTraceFilter): BindingTrace {
return resolveSession.storageManager.createSafeTrace( return resolveSession.storageManager.createSafeTrace(
DelegatingBindingTrace(resolveSession.bindingContext, "trace to resolve element", resolveElement)) DelegatingBindingTrace(resolveSession.bindingContext, "trace to resolve element", resolveElement, filter))
} }
private class BodyResolveContextForLazy( private class BodyResolveContextForLazy(
@@ -70,7 +70,7 @@ abstract class AbstractKtReference<T : KtElement>(element: T)
override fun isSoft(): Boolean = false override fun isSoft(): Boolean = false
private fun resolveToPsiElements(): Collection<PsiElement> { private fun resolveToPsiElements(): Collection<PsiElement> {
val bindingContext = expression.analyze(BodyResolveMode.PARTIAL) val bindingContext = expression.analyze(BodyResolveMode.PARTIAL_WITH_DIAGNOSTICS)
return resolveToPsiElements(bindingContext, getTargetDescriptors(bindingContext)) return resolveToPsiElements(bindingContext, getTargetDescriptors(bindingContext))
} }
@@ -76,7 +76,7 @@ abstract class KotlinIntentionActionFactoryWithDelegate<E : KtElement, D : Any>
if (!diagnosticElement.isValid || !element.isValid) return@factory null if (!diagnosticElement.isValid || !element.isValid) return@factory null
val currentDiagnostic = val currentDiagnostic =
element.analyze(BodyResolveMode.PARTIAL) element.analyze(BodyResolveMode.PARTIAL_WITH_DIAGNOSTICS)
.diagnostics .diagnostics
.forElement(diagnosticElement) .forElement(diagnosticElement)
.firstOrNull { DefaultErrorMessages.render(it) == diagnosticMessage } ?: return@factory null .firstOrNull { DefaultErrorMessages.render(it) == diagnosticMessage } ?: return@factory null
@@ -111,7 +111,7 @@ class CreateTypeParameterFromUsageFix(
} }
} }
is KtCallElement -> { is KtCallElement -> {
if (it.analyze(BodyResolveMode.PARTIAL).diagnostics.forElement(it.calleeExpression!!).any { if (it.analyze(BodyResolveMode.PARTIAL_WITH_DIAGNOSTICS).diagnostics.forElement(it.calleeExpression!!).any {
it.factory in Errors.TYPE_INFERENCE_ERRORS it.factory in Errors.TYPE_INFERENCE_ERRORS
}) { }) {
callsToExplicateArguments += it callsToExplicateArguments += it
@@ -303,7 +303,7 @@ class KotlinInlineValHandler : InlineActionHandler() {
resolutionFacade: ResolutionFacade resolutionFacade: ResolutionFacade
): Boolean { ): Boolean {
val functionLiteral = lambdaExpression.functionLiteral val functionLiteral = lambdaExpression.functionLiteral
val context = resolutionFacade.analyze(lambdaExpression, BodyResolveMode.PARTIAL) val context = resolutionFacade.analyze(lambdaExpression, BodyResolveMode.PARTIAL_WITH_DIAGNOSTICS)
return context.diagnostics.any { diagnostic -> return context.diagnostics.any { diagnostic ->
val factory = diagnostic.factory val factory = diagnostic.factory
val element = diagnostic.psiElement val element = diagnostic.psiElement
@@ -784,7 +784,7 @@ fun getQualifiedTypeArgumentList(initializer: KtExpression): KtTypeArgumentList?
} }
fun addTypeArgumentsIfNeeded(expression: KtExpression, typeArgumentList: KtTypeArgumentList) { fun addTypeArgumentsIfNeeded(expression: KtExpression, typeArgumentList: KtTypeArgumentList) {
val context = expression.analyze(BodyResolveMode.PARTIAL) val context = expression.analyze(BodyResolveMode.PARTIAL_WITH_DIAGNOSTICS)
val call = expression.getCallWithAssert(context) val call = expression.getCallWithAssert(context)
val callElement = call.callElement as? KtCallExpression ?: return val callElement = call.callElement as? KtCallExpression ?: return
if (call.typeArgumentList != null) return if (call.typeArgumentList != null) return