Lazy diagnostics API in frontend
Relates to #KT-37702
This commit is contained in:
committed by
Space
parent
8f675fe757
commit
558338f997
@@ -52,9 +52,9 @@ open class AnalysisResult protected constructor(
|
|||||||
fun isError(): Boolean = this is InternalError || this is CompilationError
|
fun isError(): Boolean = this is InternalError || this is CompilationError
|
||||||
|
|
||||||
fun throwIfError() {
|
fun throwIfError() {
|
||||||
when {
|
when (this) {
|
||||||
this is InternalError -> throw IllegalStateException("failed to analyze: " + error, error)
|
is InternalError -> throw IllegalStateException("failed to analyze: $error", error)
|
||||||
this is CompilationError -> throw CompilationErrorException()
|
is CompilationError -> throw CompilationErrorException()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,15 @@ public interface DiagnosticSink {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface DiagnosticsCallback {
|
||||||
|
void callback(Diagnostic diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
void report(@NotNull Diagnostic diagnostic);
|
void report(@NotNull Diagnostic diagnostic);
|
||||||
|
|
||||||
|
default void setCallback(@NotNull DiagnosticsCallback callback) { }
|
||||||
|
|
||||||
|
default void resetCallback() { }
|
||||||
|
|
||||||
boolean wantsDiagnostics();
|
boolean wantsDiagnostics();
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-10
@@ -48,16 +48,16 @@ sealed class RenderingContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun fromDiagnostic(d: Diagnostic): RenderingContext {
|
fun parameters(d: Diagnostic): List<Any> = when (d) {
|
||||||
val parameters = when (d) {
|
is SimpleDiagnostic<*> -> listOf()
|
||||||
is SimpleDiagnostic<*> -> listOf()
|
is DiagnosticWithParameters1<*, *> -> listOf(d.a)
|
||||||
is DiagnosticWithParameters1<*, *> -> listOf(d.a)
|
is DiagnosticWithParameters2<*, *, *> -> listOf(d.a, d.b)
|
||||||
is DiagnosticWithParameters2<*, *, *> -> listOf(d.a, d.b)
|
is DiagnosticWithParameters3<*, *, *, *> -> listOf(d.a, d.b, d.c)
|
||||||
is DiagnosticWithParameters3<*, *, *, *> -> listOf(d.a, d.b, d.c)
|
is ParametrizedDiagnostic<*> -> error("Unexpected diagnostic: ${d::class.java}")
|
||||||
is ParametrizedDiagnostic<*> -> error("Unexpected diagnostic: ${d::class.java}")
|
else -> listOf()
|
||||||
else -> listOf()
|
|
||||||
}
|
|
||||||
return Impl(parameters)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun fromDiagnostic(d: Diagnostic): RenderingContext = Impl(parameters(d))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ abstract class AbstractFilteringTrace(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun report(diagnostic: Diagnostic) {
|
override fun report(diagnostic: Diagnostic) {
|
||||||
|
diagnosticsCallback?.callback(diagnostic)
|
||||||
|
|
||||||
parentTrace.report(diagnostic)
|
parentTrace.report(diagnostic)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.resolve
|
|||||||
import com.google.common.collect.ImmutableMap
|
import com.google.common.collect.ImmutableMap
|
||||||
import org.jetbrains.annotations.TestOnly
|
import org.jetbrains.annotations.TestOnly
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
import org.jetbrains.kotlin.resolve.diagnostics.BindingContextSuppressCache
|
import org.jetbrains.kotlin.resolve.diagnostics.BindingContextSuppressCache
|
||||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||||
@@ -156,6 +157,18 @@ open class DelegatingBindingTrace(
|
|||||||
mutableDiagnostics.report(diagnostic)
|
mutableDiagnostics.report(diagnostic)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected var diagnosticsCallback: DiagnosticSink.DiagnosticsCallback? = null
|
||||||
|
|
||||||
|
override fun setCallback(callback: DiagnosticSink.DiagnosticsCallback) {
|
||||||
|
diagnosticsCallback = callback
|
||||||
|
mutableDiagnostics?.setCallback(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resetCallback() {
|
||||||
|
diagnosticsCallback = null
|
||||||
|
mutableDiagnostics?.resetCallback()
|
||||||
|
}
|
||||||
|
|
||||||
override fun wantsDiagnostics(): Boolean = mutableDiagnostics != null
|
override fun wantsDiagnostics(): Boolean = mutableDiagnostics != null
|
||||||
|
|
||||||
override fun toString(): String = name
|
override fun toString(): String = name
|
||||||
|
|||||||
@@ -16,9 +16,10 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.resolve.diagnostics
|
package org.jetbrains.kotlin.resolve.diagnostics
|
||||||
|
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import com.intellij.openapi.util.ModificationTracker
|
import com.intellij.openapi.util.ModificationTracker
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticSink.DiagnosticsCallback
|
||||||
import org.jetbrains.kotlin.diagnostics.GenericDiagnostics
|
import org.jetbrains.kotlin.diagnostics.GenericDiagnostics
|
||||||
|
|
||||||
interface Diagnostics : GenericDiagnostics<Diagnostic> {
|
interface Diagnostics : GenericDiagnostics<Diagnostic> {
|
||||||
@@ -37,6 +38,9 @@ interface Diagnostics : GenericDiagnostics<Diagnostic> {
|
|||||||
|
|
||||||
fun noSuppression(): Diagnostics
|
fun noSuppression(): Diagnostics
|
||||||
|
|
||||||
|
fun setCallback(callback: DiagnosticsCallback) {}
|
||||||
|
fun resetCallback() {}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val EMPTY: Diagnostics = object : Diagnostics {
|
val EMPTY: Diagnostics = object : Diagnostics {
|
||||||
override fun noSuppression(): Diagnostics = this
|
override fun noSuppression(): Diagnostics = this
|
||||||
|
|||||||
+11
@@ -23,6 +23,7 @@ import kotlin.collections.CollectionsKt;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.TestOnly;
|
import org.jetbrains.annotations.TestOnly;
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticSink;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@@ -73,6 +74,16 @@ public class DiagnosticsWithSuppression implements Diagnostics {
|
|||||||
throw new IllegalStateException("Trying to obtain modification tracker for readonly DiagnosticsWithSuppression.");
|
throw new IllegalStateException("Trying to obtain modification tracker for readonly DiagnosticsWithSuppression.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCallback(@NotNull DiagnosticSink.DiagnosticsCallback callback) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resetCallback() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@TestOnly
|
@TestOnly
|
||||||
@NotNull
|
@NotNull
|
||||||
public Collection<Diagnostic> getDiagnostics() {
|
public Collection<Diagnostic> getDiagnostics() {
|
||||||
|
|||||||
+8
-5
@@ -24,10 +24,7 @@ import org.jetbrains.kotlin.builtins.StandardNames
|
|||||||
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.Severity
|
import org.jetbrains.kotlin.diagnostics.Severity
|
||||||
import org.jetbrains.kotlin.psi.KtAnnotated
|
import org.jetbrains.kotlin.psi.*
|
||||||
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.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.constants.ArrayValue
|
import org.jetbrains.kotlin.resolve.constants.ArrayValue
|
||||||
import org.jetbrains.kotlin.resolve.constants.StringValue
|
import org.jetbrains.kotlin.resolve.constants.StringValue
|
||||||
@@ -111,7 +108,12 @@ abstract class KotlinSuppressCache {
|
|||||||
|
|
||||||
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(suppressionKey: String, severity: Severity, annotated: KtAnnotated, debugDepth: Int): Boolean {
|
private fun isSuppressedByAnnotated(
|
||||||
|
suppressionKey: String,
|
||||||
|
severity: Severity,
|
||||||
|
annotated: KtAnnotated,
|
||||||
|
debugDepth: Int
|
||||||
|
): Boolean {
|
||||||
val suppressor = getOrCreateSuppressor(annotated)
|
val suppressor = getOrCreateSuppressor(annotated)
|
||||||
if (suppressor.isSuppressed(suppressionKey, severity)) return true
|
if (suppressor.isSuppressed(suppressionKey, severity)) return true
|
||||||
|
|
||||||
@@ -140,6 +142,7 @@ abstract class KotlinSuppressCache {
|
|||||||
|
|
||||||
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)) {
|
for (annotationDescriptor in getSuppressionAnnotations(annotated)) {
|
||||||
processAnnotation(builder, annotationDescriptor)
|
processAnnotation(builder, annotationDescriptor)
|
||||||
}
|
}
|
||||||
|
|||||||
+26
@@ -22,12 +22,16 @@ import com.intellij.psi.util.CachedValueProvider
|
|||||||
import com.intellij.util.CachedValueImpl
|
import com.intellij.util.CachedValueImpl
|
||||||
import org.jetbrains.annotations.TestOnly
|
import org.jetbrains.annotations.TestOnly
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||||
|
import org.jetbrains.kotlin.psi.KtAnnotationEntry
|
||||||
|
import org.jetbrains.kotlin.psi.KtStubbedPsiUtil
|
||||||
|
|
||||||
class MutableDiagnosticsWithSuppression(
|
class MutableDiagnosticsWithSuppression(
|
||||||
private val suppressCache: KotlinSuppressCache,
|
private val suppressCache: KotlinSuppressCache,
|
||||||
private val delegateDiagnostics: Diagnostics,
|
private val delegateDiagnostics: Diagnostics,
|
||||||
) : Diagnostics {
|
) : Diagnostics {
|
||||||
private val diagnosticList = ArrayList<Diagnostic>()
|
private val diagnosticList = ArrayList<Diagnostic>()
|
||||||
|
private var diagnosticsCallback: DiagnosticSink.DiagnosticsCallback? = null
|
||||||
|
|
||||||
//NOTE: CachedValuesManager is not used because it requires Project passed to this object
|
//NOTE: CachedValuesManager is not used because it requires Project passed to this object
|
||||||
private val cache = CachedValueImpl {
|
private val cache = CachedValueImpl {
|
||||||
@@ -43,16 +47,38 @@ class MutableDiagnosticsWithSuppression(
|
|||||||
override fun forElement(psiElement: PsiElement) = readonlyView().forElement(psiElement)
|
override fun forElement(psiElement: PsiElement) = readonlyView().forElement(psiElement)
|
||||||
override fun noSuppression() = readonlyView().noSuppression()
|
override fun noSuppression() = readonlyView().noSuppression()
|
||||||
|
|
||||||
|
override fun setCallback(callback: DiagnosticSink.DiagnosticsCallback) {
|
||||||
|
assert(diagnosticsCallback == null) { "diagnostic callback has been already registered" }
|
||||||
|
diagnosticsCallback = callback
|
||||||
|
delegateDiagnostics.setCallback(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resetCallback() {
|
||||||
|
diagnosticsCallback = null
|
||||||
|
delegateDiagnostics.resetCallback()
|
||||||
|
}
|
||||||
|
|
||||||
//essential that this list is readonly
|
//essential that this list is readonly
|
||||||
fun getOwnDiagnostics(): List<Diagnostic> {
|
fun getOwnDiagnostics(): List<Diagnostic> {
|
||||||
return diagnosticList
|
return diagnosticList
|
||||||
}
|
}
|
||||||
|
|
||||||
fun report(diagnostic: Diagnostic) {
|
fun report(diagnostic: Diagnostic) {
|
||||||
|
onFlyDiagnosticsCallback(diagnostic)?.callback(diagnostic)
|
||||||
|
|
||||||
diagnosticList.add(diagnostic)
|
diagnosticList.add(diagnostic)
|
||||||
modificationTracker.incModificationCount()
|
modificationTracker.incModificationCount()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun onFlyDiagnosticsCallback(diagnostic: Diagnostic): DiagnosticSink.DiagnosticsCallback? =
|
||||||
|
diagnosticsCallback.takeIf {
|
||||||
|
diagnosticsCallback != null &&
|
||||||
|
// Due to a potential recursion in filter.invoke (via LazyAnnotations) do not try to report
|
||||||
|
// diagnostic on fly if it happened in annotations
|
||||||
|
KtStubbedPsiUtil.getPsiOrStubParent(diagnostic.psiElement, KtAnnotationEntry::class.java, false) == null &&
|
||||||
|
suppressCache.filter.invoke(diagnostic)
|
||||||
|
}
|
||||||
|
|
||||||
fun clear() {
|
fun clear() {
|
||||||
diagnosticList.clear()
|
diagnosticList.clear()
|
||||||
modificationTracker.incModificationCount()
|
modificationTracker.incModificationCount()
|
||||||
|
|||||||
-1
@@ -22,7 +22,6 @@ import org.jetbrains.kotlin.resolve.BindingContext
|
|||||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||||
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
|
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
|
||||||
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
|
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
|
||||||
import org.jetbrains.kotlin.resolve.TraceEntryFilter
|
|
||||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||||
import com.intellij.util.containers.ContainerUtil
|
import com.intellij.util.containers.ContainerUtil
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
|||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||||
import org.jetbrains.kotlin.idea.FrontendInternals
|
import org.jetbrains.kotlin.idea.FrontendInternals
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
@@ -167,8 +168,11 @@ inline fun <reified T> T.analyzeWithContent(): BindingContext where T : KtDeclar
|
|||||||
* @ref [org.jetbrains.kotlin.idea.caches.resolve.PerFileAnalysisCache]
|
* @ref [org.jetbrains.kotlin.idea.caches.resolve.PerFileAnalysisCache]
|
||||||
*/
|
*/
|
||||||
fun KtFile.analyzeWithAllCompilerChecks(vararg extraFiles: KtFile): AnalysisResult =
|
fun KtFile.analyzeWithAllCompilerChecks(vararg extraFiles: KtFile): AnalysisResult =
|
||||||
|
this.analyzeWithAllCompilerChecks(null, *extraFiles)
|
||||||
|
|
||||||
|
fun KtFile.analyzeWithAllCompilerChecks(callback: ((Diagnostic) -> Unit)?, vararg extraFiles: KtFile): AnalysisResult =
|
||||||
KotlinCacheService.getInstance(project).getResolutionFacade(listOf(this) + extraFiles.toList())
|
KotlinCacheService.getInstance(project).getResolutionFacade(listOf(this) + extraFiles.toList())
|
||||||
.analyzeWithAllCompilerChecks(listOf(this))
|
.analyzeWithAllCompilerChecks(listOf(this), callback)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function is expected to produce the same result as compiler for the given element and its children (including diagnostics,
|
* This function is expected to produce the same result as compiler for the given element and its children (including diagnostics,
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import org.jetbrains.kotlin.analyzer.ModuleInfo
|
|||||||
import org.jetbrains.kotlin.analyzer.ResolverForProject
|
import org.jetbrains.kotlin.analyzer.ResolverForProject
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||||
import org.jetbrains.kotlin.idea.FrontendInternals
|
import org.jetbrains.kotlin.idea.FrontendInternals
|
||||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
@@ -24,7 +26,7 @@ interface ResolutionFacade {
|
|||||||
fun analyze(element: KtElement, bodyResolveMode: BodyResolveMode = BodyResolveMode.FULL): BindingContext
|
fun analyze(element: KtElement, bodyResolveMode: BodyResolveMode = BodyResolveMode.FULL): BindingContext
|
||||||
fun analyze(elements: Collection<KtElement>, bodyResolveMode: BodyResolveMode): BindingContext
|
fun analyze(elements: Collection<KtElement>, bodyResolveMode: BodyResolveMode): BindingContext
|
||||||
|
|
||||||
fun analyzeWithAllCompilerChecks(elements: Collection<KtElement>): AnalysisResult
|
fun analyzeWithAllCompilerChecks(elements: Collection<KtElement>, callback: DiagnosticSink.DiagnosticsCallback? = null): AnalysisResult
|
||||||
|
|
||||||
fun resolveToDescriptor(declaration: KtDeclaration, bodyResolveMode: BodyResolveMode = BodyResolveMode.FULL): DeclarationDescriptor
|
fun resolveToDescriptor(declaration: KtDeclaration, bodyResolveMode: BodyResolveMode = BodyResolveMode.FULL): DeclarationDescriptor
|
||||||
|
|
||||||
|
|||||||
+6
-2
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.container.tryGetService
|
|||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
import org.jetbrains.kotlin.idea.FrontendInternals
|
import org.jetbrains.kotlin.idea.FrontendInternals
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||||
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||||
import org.jetbrains.kotlin.idea.project.ResolveElementCache
|
import org.jetbrains.kotlin.idea.project.ResolveElementCache
|
||||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||||
@@ -75,11 +76,14 @@ internal class ModuleResolutionFacadeImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun analyzeWithAllCompilerChecks(elements: Collection<KtElement>): AnalysisResult {
|
override fun analyzeWithAllCompilerChecks(
|
||||||
|
elements: Collection<KtElement>,
|
||||||
|
callback: DiagnosticSink.DiagnosticsCallback?
|
||||||
|
): AnalysisResult {
|
||||||
ResolveInDispatchThreadManager.assertNoResolveInDispatchThread()
|
ResolveInDispatchThreadManager.assertNoResolveInDispatchThread()
|
||||||
|
|
||||||
return runWithCancellationCheck {
|
return runWithCancellationCheck {
|
||||||
projectFacade.getAnalysisResultsForElements(elements)
|
projectFacade.getAnalysisResultsForElements(elements, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+67
-33
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.context.withModule
|
|||||||
import org.jetbrains.kotlin.context.withProject
|
import org.jetbrains.kotlin.context.withProject
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
|
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
|
||||||
import org.jetbrains.kotlin.frontend.di.createContainerForLazyBodyResolve
|
import org.jetbrains.kotlin.frontend.di.createContainerForLazyBodyResolve
|
||||||
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
||||||
@@ -89,32 +90,50 @@ internal class PerFileAnalysisCache(val file: KtFile, componentProvider: Compone
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun getAnalysisResults(element: KtElement): AnalysisResult {
|
internal fun getAnalysisResults(element: KtElement, callback: DiagnosticSink.DiagnosticsCallback? = null): AnalysisResult {
|
||||||
check(element)
|
check(element)
|
||||||
|
|
||||||
val analyzableParent = KotlinResolveDataProvider.findAnalyzableParent(element) ?: return AnalysisResult.EMPTY
|
val analyzableParent = KotlinResolveDataProvider.findAnalyzableParent(element) ?: return AnalysisResult.EMPTY
|
||||||
|
|
||||||
|
fun handleResult(result: AnalysisResult, callback: DiagnosticSink.DiagnosticsCallback?): AnalysisResult {
|
||||||
|
callback?.let { result.bindingContext.diagnostics.forEach(it::callback) }
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
return guardLock.guarded {
|
return guardLock.guarded {
|
||||||
// step 1: perform incremental analysis IF it is applicable
|
// step 1: perform incremental analysis IF it is applicable
|
||||||
getIncrementalAnalysisResult()?.let { return@guarded it }
|
getIncrementalAnalysisResult(callback)?.let {
|
||||||
|
return@guarded handleResult(it, callback)
|
||||||
|
}
|
||||||
|
|
||||||
// cache does not contain AnalysisResult per each kt/psi element
|
// cache does not contain AnalysisResult per each kt/psi element
|
||||||
// instead it looks up analysis for its parents - see lookUp(analyzableElement)
|
// instead it looks up analysis for its parents - see lookUp(analyzableElement)
|
||||||
|
|
||||||
// step 2: return result if it is cached
|
// step 2: return result if it is cached
|
||||||
lookUp(analyzableParent)?.let {
|
lookUp(analyzableParent)?.let {
|
||||||
return@guarded it
|
return@guarded handleResult(it, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val localDiagnostics = mutableSetOf<Diagnostic>()
|
||||||
|
val localCallback = if (callback != null) { d: Diagnostic ->
|
||||||
|
localDiagnostics.add(d)
|
||||||
|
callback.callback(d)
|
||||||
|
} else null
|
||||||
|
|
||||||
// step 3: perform analyze of analyzableParent as nothing has been cached yet
|
// step 3: perform analyze of analyzableParent as nothing has been cached yet
|
||||||
val result = analyze(analyzableParent)
|
val result = analyze(analyzableParent, null, localCallback)
|
||||||
|
|
||||||
|
// some of diagnostics could be not handled with a callback - send out the rest
|
||||||
|
callback?.let { c ->
|
||||||
|
result.bindingContext.diagnostics.filterNot { it in localDiagnostics }.forEach(c::callback)
|
||||||
|
}
|
||||||
cache[analyzableParent] = result
|
cache[analyzableParent] = result
|
||||||
|
|
||||||
return@guarded result
|
return@guarded result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getIncrementalAnalysisResult(): AnalysisResult? {
|
private fun getIncrementalAnalysisResult(callback: DiagnosticSink.DiagnosticsCallback?): AnalysisResult? {
|
||||||
updateFileResultFromCache()
|
updateFileResultFromCache()
|
||||||
|
|
||||||
val inBlockModifications = file.inBlockModifications
|
val inBlockModifications = file.inBlockModifications
|
||||||
@@ -151,7 +170,9 @@ internal class PerFileAnalysisCache(val file: KtFile, componentProvider: Compone
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val newResult = analyze(inBlockModification, trace)
|
callback?.let { trace.parentDiagnosticsApartElement.forEach(it::callback) }
|
||||||
|
|
||||||
|
val newResult = analyze(inBlockModification, trace, callback)
|
||||||
analysisResult = wrapResult(result, newResult, trace)
|
analysisResult = wrapResult(result, newResult, trace)
|
||||||
}
|
}
|
||||||
file.clearInBlockModifications()
|
file.clearInBlockModifications()
|
||||||
@@ -224,7 +245,11 @@ internal class PerFileAnalysisCache(val file: KtFile, componentProvider: Compone
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun analyze(analyzableElement: KtElement, bindingTrace: BindingTrace? = null): AnalysisResult {
|
private fun analyze(
|
||||||
|
analyzableElement: KtElement,
|
||||||
|
bindingTrace: BindingTrace?,
|
||||||
|
callback: DiagnosticSink.DiagnosticsCallback?
|
||||||
|
): AnalysisResult {
|
||||||
ProgressIndicatorProvider.checkCanceled()
|
ProgressIndicatorProvider.checkCanceled()
|
||||||
|
|
||||||
val project = analyzableElement.project
|
val project = analyzableElement.project
|
||||||
@@ -242,7 +267,8 @@ internal class PerFileAnalysisCache(val file: KtFile, componentProvider: Compone
|
|||||||
codeFragmentAnalyzer,
|
codeFragmentAnalyzer,
|
||||||
bodyResolveCache,
|
bodyResolveCache,
|
||||||
analyzableElement,
|
analyzableElement,
|
||||||
bindingTrace
|
bindingTrace,
|
||||||
|
callback
|
||||||
)
|
)
|
||||||
} catch (e: ProcessCanceledException) {
|
} catch (e: ProcessCanceledException) {
|
||||||
throw e
|
throw e
|
||||||
@@ -412,12 +438,14 @@ private object KotlinResolveDataProvider {
|
|||||||
codeFragmentAnalyzer: CodeFragmentAnalyzer,
|
codeFragmentAnalyzer: CodeFragmentAnalyzer,
|
||||||
bodyResolveCache: BodyResolveCache,
|
bodyResolveCache: BodyResolveCache,
|
||||||
analyzableElement: KtElement,
|
analyzableElement: KtElement,
|
||||||
bindingTrace: BindingTrace?
|
bindingTrace: BindingTrace?,
|
||||||
|
callback: DiagnosticSink.DiagnosticsCallback?
|
||||||
): AnalysisResult {
|
): AnalysisResult {
|
||||||
try {
|
try {
|
||||||
if (analyzableElement is KtCodeFragment) {
|
if (analyzableElement is KtCodeFragment) {
|
||||||
val bodyResolveMode = BodyResolveMode.PARTIAL_FOR_COMPLETION
|
val bodyResolveMode = BodyResolveMode.PARTIAL_FOR_COMPLETION
|
||||||
val bindingContext = codeFragmentAnalyzer.analyzeCodeFragment(analyzableElement, bodyResolveMode).bindingContext
|
val trace: BindingTrace = codeFragmentAnalyzer.analyzeCodeFragment(analyzableElement, bodyResolveMode)
|
||||||
|
val bindingContext = trace.bindingContext
|
||||||
return AnalysisResult.success(bindingContext, moduleDescriptor)
|
return AnalysisResult.success(bindingContext, moduleDescriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,34 +455,40 @@ private object KotlinResolveDataProvider {
|
|||||||
allowSliceRewrite = true
|
allowSliceRewrite = true
|
||||||
)
|
)
|
||||||
|
|
||||||
val moduleInfo = analyzableElement.containingFile.getModuleInfo()
|
val moduleInfo = analyzableElement.containingKtFile.getModuleInfo()
|
||||||
|
|
||||||
val targetPlatform = moduleInfo.platform
|
val targetPlatform = moduleInfo.platform
|
||||||
|
|
||||||
/*
|
callback?.let { trace.setCallback(it) }
|
||||||
Note that currently we *have* to re-create LazyTopDownAnalyzer with custom trace in order to disallow resolution of
|
|
||||||
bodies in top-level trace (trace from DI-container).
|
|
||||||
Resolving bodies in top-level trace may lead to memory leaks and incorrect resolution, because top-level
|
|
||||||
trace isn't invalidated on in-block modifications (while body resolution surely does)
|
|
||||||
|
|
||||||
Also note that for function bodies, we'll create DelegatingBindingTrace in ResolveElementCache anyways
|
try {
|
||||||
(see 'functionAdditionalResolve'). However, this trace is still needed, because we have other
|
/*
|
||||||
codepaths for other KtDeclarationWithBodies (like property accessors/secondary constructors/class initializers)
|
Note that currently we *have* to re-create LazyTopDownAnalyzer with custom trace in order to disallow resolution of
|
||||||
*/
|
bodies in top-level trace (trace from DI-container).
|
||||||
val lazyTopDownAnalyzer = createContainerForLazyBodyResolve(
|
Resolving bodies in top-level trace may lead to memory leaks and incorrect resolution, because top-level
|
||||||
//TODO: should get ModuleContext
|
trace isn't invalidated on in-block modifications (while body resolution surely does)
|
||||||
globalContext.withProject(project).withModule(moduleDescriptor),
|
|
||||||
resolveSession,
|
|
||||||
trace,
|
|
||||||
targetPlatform,
|
|
||||||
bodyResolveCache,
|
|
||||||
targetPlatform.findAnalyzerServices(project),
|
|
||||||
analyzableElement.languageVersionSettings,
|
|
||||||
IdeaModuleStructureOracle(),
|
|
||||||
IdeMainFunctionDetectorFactory()
|
|
||||||
).get<LazyTopDownAnalyzer>()
|
|
||||||
|
|
||||||
lazyTopDownAnalyzer.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, listOf(analyzableElement))
|
Also note that for function bodies, we'll create DelegatingBindingTrace in ResolveElementCache anyways
|
||||||
|
(see 'functionAdditionalResolve'). However, this trace is still needed, because we have other
|
||||||
|
codepaths for other KtDeclarationWithBodies (like property accessors/secondary constructors/class initializers)
|
||||||
|
*/
|
||||||
|
val lazyTopDownAnalyzer = createContainerForLazyBodyResolve(
|
||||||
|
//TODO: should get ModuleContext
|
||||||
|
globalContext.withProject(project).withModule(moduleDescriptor),
|
||||||
|
resolveSession,
|
||||||
|
trace,
|
||||||
|
targetPlatform,
|
||||||
|
bodyResolveCache,
|
||||||
|
targetPlatform.findAnalyzerServices(project),
|
||||||
|
analyzableElement.languageVersionSettings,
|
||||||
|
IdeaModuleStructureOracle(),
|
||||||
|
IdeMainFunctionDetectorFactory()
|
||||||
|
).get<LazyTopDownAnalyzer>()
|
||||||
|
|
||||||
|
lazyTopDownAnalyzer.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, listOf(analyzableElement))
|
||||||
|
} finally {
|
||||||
|
trace.resetCallback()
|
||||||
|
}
|
||||||
|
|
||||||
return AnalysisResult.success(trace.bindingContext, moduleDescriptor)
|
return AnalysisResult.success(trace.bindingContext, moduleDescriptor)
|
||||||
} catch (e: ProcessCanceledException) {
|
} catch (e: ProcessCanceledException) {
|
||||||
|
|||||||
+7
-3
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.analyzer.*
|
|||||||
import org.jetbrains.kotlin.context.GlobalContextImpl
|
import org.jetbrains.kotlin.context.GlobalContextImpl
|
||||||
import org.jetbrains.kotlin.context.withProject
|
import org.jetbrains.kotlin.context.withProject
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||||
import org.jetbrains.kotlin.idea.caches.project.*
|
import org.jetbrains.kotlin.idea.caches.project.*
|
||||||
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||||
import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListener
|
import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListener
|
||||||
@@ -143,10 +144,13 @@ internal class ProjectResolutionFacade(
|
|||||||
internal fun findModuleDescriptor(ideaModuleInfo: IdeaModuleInfo): ModuleDescriptor {
|
internal fun findModuleDescriptor(ideaModuleInfo: IdeaModuleInfo): ModuleDescriptor {
|
||||||
return cachedResolverForProject.descriptorForModule(ideaModuleInfo)
|
return cachedResolverForProject.descriptorForModule(ideaModuleInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun getResolverForProject(): ResolverForProject<IdeaModuleInfo> = cachedResolverForProject
|
internal fun getResolverForProject(): ResolverForProject<IdeaModuleInfo> = cachedResolverForProject
|
||||||
|
|
||||||
internal fun getAnalysisResultsForElements(elements: Collection<KtElement>): AnalysisResult {
|
internal fun getAnalysisResultsForElements(
|
||||||
|
elements: Collection<KtElement>,
|
||||||
|
callback: DiagnosticSink.DiagnosticsCallback? = null
|
||||||
|
): AnalysisResult {
|
||||||
assert(elements.isNotEmpty()) { "elements collection should not be empty" }
|
assert(elements.isNotEmpty()) { "elements collection should not be empty" }
|
||||||
|
|
||||||
val cache = analysisResultsSimpleLock.guarded {
|
val cache = analysisResultsSimpleLock.guarded {
|
||||||
@@ -157,7 +161,7 @@ internal class ProjectResolutionFacade(
|
|||||||
val containingKtFile = it.containingKtFile
|
val containingKtFile = it.containingKtFile
|
||||||
val perFileCache = cache[containingKtFile]
|
val perFileCache = cache[containingKtFile]
|
||||||
try {
|
try {
|
||||||
perFileCache.getAnalysisResults(it)
|
perFileCache.getAnalysisResults(it, callback)
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
if (e is ControlFlowException) {
|
if (e is ControlFlowException) {
|
||||||
throw e
|
throw e
|
||||||
|
|||||||
+6
-2
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.analyzer.ResolverForProject
|
|||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
import org.jetbrains.kotlin.idea.FrontendInternals
|
import org.jetbrains.kotlin.idea.FrontendInternals
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||||
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||||
import org.jetbrains.kotlin.idea.caches.project.getNullableModuleInfo
|
import org.jetbrains.kotlin.idea.caches.project.getNullableModuleInfo
|
||||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||||
@@ -52,9 +53,12 @@ private class ResolutionFacadeWithDebugInfo(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun analyzeWithAllCompilerChecks(elements: Collection<KtElement>): AnalysisResult {
|
override fun analyzeWithAllCompilerChecks(
|
||||||
|
elements: Collection<KtElement>,
|
||||||
|
callback: DiagnosticSink.DiagnosticsCallback?
|
||||||
|
): AnalysisResult {
|
||||||
return wrapExceptions({ ResolvingWhat(elements) }) {
|
return wrapExceptions({ ResolvingWhat(elements) }) {
|
||||||
delegate.analyzeWithAllCompilerChecks(elements)
|
delegate.analyzeWithAllCompilerChecks(elements, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.container.ComponentProvider
|
|||||||
import org.jetbrains.kotlin.container.getService
|
import org.jetbrains.kotlin.container.getService
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||||
import org.jetbrains.kotlin.idea.FrontendInternals
|
import org.jetbrains.kotlin.idea.FrontendInternals
|
||||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||||
@@ -72,7 +73,10 @@ class KotlinResolutionFacadeForRepl(
|
|||||||
throw UnsupportedOperationException()
|
throw UnsupportedOperationException()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun analyzeWithAllCompilerChecks(elements: Collection<KtElement>): AnalysisResult {
|
override fun analyzeWithAllCompilerChecks(
|
||||||
|
elements: Collection<KtElement>,
|
||||||
|
callback: DiagnosticSink.DiagnosticsCallback?
|
||||||
|
): AnalysisResult {
|
||||||
throw UnsupportedOperationException()
|
throw UnsupportedOperationException()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user