Lazy diagnostics API in frontend

Relates to #KT-37702
This commit is contained in:
Vladimir Dolzhenko
2020-12-10 21:34:07 +01:00
committed by Space
parent 8f675fe757
commit 558338f997
17 changed files with 186 additions and 63 deletions
@@ -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();
}
@@ -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
@@ -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() {
@@ -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)
}
@@ -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()
@@ -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
@@ -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)
}
}
@@ -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) {
@@ -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
@@ -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)
}
}
@@ -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()
}