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 throwIfError() {
|
||||
when {
|
||||
this is InternalError -> throw IllegalStateException("failed to analyze: " + error, error)
|
||||
this is CompilationError -> throw CompilationErrorException()
|
||||
when (this) {
|
||||
is InternalError -> throw IllegalStateException("failed to analyze: $error", error)
|
||||
is CompilationError -> throw CompilationErrorException()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,15 @@ public interface DiagnosticSink {
|
||||
}
|
||||
};
|
||||
|
||||
interface DiagnosticsCallback {
|
||||
void callback(Diagnostic diagnostic);
|
||||
}
|
||||
|
||||
void report(@NotNull Diagnostic diagnostic);
|
||||
|
||||
default void setCallback(@NotNull DiagnosticsCallback callback) { }
|
||||
|
||||
default void resetCallback() { }
|
||||
|
||||
boolean wantsDiagnostics();
|
||||
}
|
||||
|
||||
+10
-10
@@ -48,16 +48,16 @@ sealed class RenderingContext {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun fromDiagnostic(d: Diagnostic): RenderingContext {
|
||||
val parameters = when (d) {
|
||||
is SimpleDiagnostic<*> -> listOf()
|
||||
is DiagnosticWithParameters1<*, *> -> listOf(d.a)
|
||||
is DiagnosticWithParameters2<*, *, *> -> listOf(d.a, d.b)
|
||||
is DiagnosticWithParameters3<*, *, *, *> -> listOf(d.a, d.b, d.c)
|
||||
is ParametrizedDiagnostic<*> -> error("Unexpected diagnostic: ${d::class.java}")
|
||||
else -> listOf()
|
||||
}
|
||||
return Impl(parameters)
|
||||
fun parameters(d: Diagnostic): List<Any> = when (d) {
|
||||
is SimpleDiagnostic<*> -> listOf()
|
||||
is DiagnosticWithParameters1<*, *> -> listOf(d.a)
|
||||
is DiagnosticWithParameters2<*, *, *> -> listOf(d.a, d.b)
|
||||
is DiagnosticWithParameters3<*, *, *, *> -> listOf(d.a, d.b, d.c)
|
||||
is ParametrizedDiagnostic<*> -> error("Unexpected diagnostic: ${d::class.java}")
|
||||
else -> listOf()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun fromDiagnostic(d: Diagnostic): RenderingContext = Impl(parameters(d))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ abstract class AbstractFilteringTrace(
|
||||
}
|
||||
|
||||
override fun report(diagnostic: Diagnostic) {
|
||||
diagnosticsCallback?.callback(diagnostic)
|
||||
|
||||
parentTrace.report(diagnostic)
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.resolve
|
||||
import com.google.common.collect.ImmutableMap
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.BindingContextSuppressCache
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||
@@ -156,6 +157,18 @@ open class DelegatingBindingTrace(
|
||||
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 toString(): String = name
|
||||
|
||||
@@ -16,9 +16,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.diagnostics
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.openapi.util.ModificationTracker
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink.DiagnosticsCallback
|
||||
import org.jetbrains.kotlin.diagnostics.GenericDiagnostics
|
||||
|
||||
interface Diagnostics : GenericDiagnostics<Diagnostic> {
|
||||
@@ -37,6 +38,9 @@ interface Diagnostics : GenericDiagnostics<Diagnostic> {
|
||||
|
||||
fun noSuppression(): Diagnostics
|
||||
|
||||
fun setCallback(callback: DiagnosticsCallback) {}
|
||||
fun resetCallback() {}
|
||||
|
||||
companion object {
|
||||
val EMPTY: Diagnostics = object : Diagnostics {
|
||||
override fun noSuppression(): Diagnostics = this
|
||||
|
||||
+11
@@ -23,6 +23,7 @@ import kotlin.collections.CollectionsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
@@ -73,6 +74,16 @@ public class DiagnosticsWithSuppression implements Diagnostics {
|
||||
throw new IllegalStateException("Trying to obtain modification tracker for readonly DiagnosticsWithSuppression.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCallback(@NotNull DiagnosticSink.DiagnosticsCallback callback) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetCallback() {
|
||||
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
@NotNull
|
||||
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.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.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.constants.ArrayValue
|
||||
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.
|
||||
*/
|
||||
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)
|
||||
if (suppressor.isSuppressed(suppressionKey, severity)) return true
|
||||
|
||||
@@ -140,6 +142,7 @@ abstract class KotlinSuppressCache {
|
||||
|
||||
private fun getSuppressingStrings(annotated: KtAnnotated): Set<String> {
|
||||
val builder = ImmutableSet.builder<String>()
|
||||
|
||||
for (annotationDescriptor in getSuppressionAnnotations(annotated)) {
|
||||
processAnnotation(builder, annotationDescriptor)
|
||||
}
|
||||
|
||||
+26
@@ -22,12 +22,16 @@ import com.intellij.psi.util.CachedValueProvider
|
||||
import com.intellij.util.CachedValueImpl
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
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(
|
||||
private val suppressCache: KotlinSuppressCache,
|
||||
private val delegateDiagnostics: Diagnostics,
|
||||
) : Diagnostics {
|
||||
private val diagnosticList = ArrayList<Diagnostic>()
|
||||
private var diagnosticsCallback: DiagnosticSink.DiagnosticsCallback? = null
|
||||
|
||||
//NOTE: CachedValuesManager is not used because it requires Project passed to this object
|
||||
private val cache = CachedValueImpl {
|
||||
@@ -43,16 +47,38 @@ class MutableDiagnosticsWithSuppression(
|
||||
override fun forElement(psiElement: PsiElement) = readonlyView().forElement(psiElement)
|
||||
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
|
||||
fun getOwnDiagnostics(): List<Diagnostic> {
|
||||
return diagnosticList
|
||||
}
|
||||
|
||||
fun report(diagnostic: Diagnostic) {
|
||||
onFlyDiagnosticsCallback(diagnostic)?.callback(diagnostic)
|
||||
|
||||
diagnosticList.add(diagnostic)
|
||||
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() {
|
||||
diagnosticList.clear()
|
||||
modificationTracker.incModificationCount()
|
||||
|
||||
-1
@@ -22,7 +22,6 @@ import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
|
||||
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
|
||||
import org.jetbrains.kotlin.resolve.TraceEntryFilter
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
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.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.idea.FrontendInternals
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
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]
|
||||
*/
|
||||
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())
|
||||
.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,
|
||||
|
||||
@@ -12,6 +12,8 @@ import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.analyzer.ResolverForProject
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
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.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
@@ -24,7 +26,7 @@ interface ResolutionFacade {
|
||||
fun analyze(element: KtElement, bodyResolveMode: BodyResolveMode = BodyResolveMode.FULL): 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
|
||||
|
||||
|
||||
+6
-2
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.container.tryGetService
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
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.project.ResolveElementCache
|
||||
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()
|
||||
|
||||
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.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
|
||||
import org.jetbrains.kotlin.frontend.di.createContainerForLazyBodyResolve
|
||||
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
||||
@@ -89,32 +90,50 @@ internal class PerFileAnalysisCache(val file: KtFile, componentProvider: Compone
|
||||
return null
|
||||
}
|
||||
|
||||
internal fun getAnalysisResults(element: KtElement): AnalysisResult {
|
||||
internal fun getAnalysisResults(element: KtElement, callback: DiagnosticSink.DiagnosticsCallback? = null): AnalysisResult {
|
||||
check(element)
|
||||
|
||||
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 {
|
||||
// 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
|
||||
// instead it looks up analysis for its parents - see lookUp(analyzableElement)
|
||||
|
||||
// step 2: return result if it is cached
|
||||
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
|
||||
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
|
||||
|
||||
return@guarded result
|
||||
}
|
||||
}
|
||||
|
||||
private fun getIncrementalAnalysisResult(): AnalysisResult? {
|
||||
private fun getIncrementalAnalysisResult(callback: DiagnosticSink.DiagnosticsCallback?): AnalysisResult? {
|
||||
updateFileResultFromCache()
|
||||
|
||||
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)
|
||||
}
|
||||
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()
|
||||
|
||||
val project = analyzableElement.project
|
||||
@@ -242,7 +267,8 @@ internal class PerFileAnalysisCache(val file: KtFile, componentProvider: Compone
|
||||
codeFragmentAnalyzer,
|
||||
bodyResolveCache,
|
||||
analyzableElement,
|
||||
bindingTrace
|
||||
bindingTrace,
|
||||
callback
|
||||
)
|
||||
} catch (e: ProcessCanceledException) {
|
||||
throw e
|
||||
@@ -412,12 +438,14 @@ private object KotlinResolveDataProvider {
|
||||
codeFragmentAnalyzer: CodeFragmentAnalyzer,
|
||||
bodyResolveCache: BodyResolveCache,
|
||||
analyzableElement: KtElement,
|
||||
bindingTrace: BindingTrace?
|
||||
bindingTrace: BindingTrace?,
|
||||
callback: DiagnosticSink.DiagnosticsCallback?
|
||||
): AnalysisResult {
|
||||
try {
|
||||
if (analyzableElement is KtCodeFragment) {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -427,34 +455,40 @@ private object KotlinResolveDataProvider {
|
||||
allowSliceRewrite = true
|
||||
)
|
||||
|
||||
val moduleInfo = analyzableElement.containingFile.getModuleInfo()
|
||||
val moduleInfo = analyzableElement.containingKtFile.getModuleInfo()
|
||||
|
||||
val targetPlatform = moduleInfo.platform
|
||||
|
||||
/*
|
||||
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)
|
||||
callback?.let { trace.setCallback(it) }
|
||||
|
||||
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>()
|
||||
try {
|
||||
/*
|
||||
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)
|
||||
|
||||
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)
|
||||
} 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.withProject
|
||||
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.IdeaModuleInfo
|
||||
import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListener
|
||||
@@ -143,10 +144,13 @@ internal class ProjectResolutionFacade(
|
||||
internal fun findModuleDescriptor(ideaModuleInfo: IdeaModuleInfo): ModuleDescriptor {
|
||||
return cachedResolverForProject.descriptorForModule(ideaModuleInfo)
|
||||
}
|
||||
|
||||
|
||||
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" }
|
||||
|
||||
val cache = analysisResultsSimpleLock.guarded {
|
||||
@@ -157,7 +161,7 @@ internal class ProjectResolutionFacade(
|
||||
val containingKtFile = it.containingKtFile
|
||||
val perFileCache = cache[containingKtFile]
|
||||
try {
|
||||
perFileCache.getAnalysisResults(it)
|
||||
perFileCache.getAnalysisResults(it, callback)
|
||||
} catch (e: Throwable) {
|
||||
if (e is ControlFlowException) {
|
||||
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.ModuleDescriptor
|
||||
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.getNullableModuleInfo
|
||||
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) }) {
|
||||
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.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.idea.FrontendInternals
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
@@ -72,7 +73,10 @@ class KotlinResolutionFacadeForRepl(
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun analyzeWithAllCompilerChecks(elements: Collection<KtElement>): AnalysisResult {
|
||||
override fun analyzeWithAllCompilerChecks(
|
||||
elements: Collection<KtElement>,
|
||||
callback: DiagnosticSink.DiagnosticsCallback?
|
||||
): AnalysisResult {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user