Force perform FULL analysis to avoid redundant analysis for the current open file

#KT-38687 Fixed
This commit is contained in:
Vladimir Dolzhenko
2020-07-01 08:22:58 +00:00
parent 1a82c94979
commit 853503d75f
4 changed files with 76 additions and 29 deletions
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.idea.project
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectRootModificationTracker
import com.intellij.psi.util.CachedValue
@@ -12,6 +13,7 @@ import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.util.containers.ContainerUtil
import com.intellij.util.containers.SLRUCache
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.cfg.ControlFlowInformationProvider
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.context.SimpleGlobalContext
@@ -25,6 +27,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.util.analyzeControlFlow
import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListener
import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListenerCompat
import org.jetbrains.kotlin.idea.caches.trackers.inBlockModificationCount
import org.jetbrains.kotlin.idea.util.application.isUnitTestMode
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.psi.*
@@ -151,6 +154,22 @@ class ResolveElementCache(
assert(bodyResolveMode == BodyResolveMode.FULL)
}
// KT-38687: There are lots of editor specific items like inlays, line markers, injected items etc
// those require analysis: it is called with BodyResolveMode.PARTIAL or BodyResolveMode.PARTIAL_WITH_CFA almost simultaneously.
// In the same time Kotlin Annotator (e.g. KotlinPsiChecker) requires BodyResolveMode.FULL analysis.
//
// While results of FULL could be reused by any of PARTIAL or PARTIAL_WITH_CFA analysis,
// neither result of PARTIAL nor result of PARTIAL_WITH_CFA analyses could be reused by FULL analysis.
//
// Force perform FULL analysis to avoid redundant analysis for the current selected files.
if (bodyResolveMode != BodyResolveMode.FULL && (!isUnitTestMode() || forceFullAnalysisModeInTests)) {
val virtualFile = resolveElement.containingFile.virtualFile
// applicable for real (physical) files only
if (virtualFile != null && FileEditorManager.getInstance(resolveElement.project).selectedFiles.any { it == virtualFile }) {
return getElementsAdditionalResolve(resolveElement, contextElements, BodyResolveMode.FULL)
}
}
// check if full additional resolve already performed and is up-to-date
val fullResolveMap = fullResolveCache.value
val cachedFullResolve = fullResolveMap[resolveElement]
@@ -807,5 +826,10 @@ class ResolveElementCache(
override fun getTopDownAnalysisMode() = topDownAnalysisMode
}
companion object {
@set:TestOnly
var forceFullAnalysisModeInTests: Boolean = false
}
}
@@ -26,11 +26,11 @@ import com.intellij.util.io.exists
import org.jetbrains.kotlin.idea.configuration.getModulesWithKotlinFiles
import org.jetbrains.kotlin.idea.perf.Stats.Companion.runAndMeasure
import org.jetbrains.kotlin.idea.perf.util.logMessage
import org.jetbrains.kotlin.idea.project.ResolveElementCache
import org.jetbrains.kotlin.idea.project.getAndCacheLanguageLevelByDependencies
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
import java.io.File
import java.nio.file.Paths
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
data class OpenProject(val projectPath: String, val projectName: String, val jdk: Sdk, val projectOpenAction: ProjectOpenAction)
@@ -64,7 +64,7 @@ enum class ProjectOpenAction {
val projectManagerEx = ProjectManagerEx.getInstanceEx()
val project = loadProjectWithName(projectPath, projectName)
assertNotNull(project, "project $projectName at $projectPath is not loaded")
?: error("project $projectName at $projectPath is not loaded")
runWriteAction {
ProjectRootManager.getInstance(project).projectSdk = jdk
@@ -123,6 +123,7 @@ enum class ProjectOpenAction {
open fun postOpenProject(project: Project, openProject: OpenProject) {
ApplicationManager.getApplication().executeOnPooledThread {
ResolveElementCache.forceFullAnalysisModeInTests = true
DumbService.getInstance(project).waitForSmartMode()
for (module in getModulesWithKotlinFiles(project)) {
@@ -5,8 +5,12 @@
package org.jetbrains.kotlin.idea
import com.intellij.util.ThrowableRunnable
import org.intellij.lang.annotations.Language
import org.jetbrains.kotlin.idea.project.ResolveElementCache
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor
import org.jetbrains.kotlin.idea.test.runAll
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.psi.*
@@ -43,6 +47,16 @@ class C(param1: String = "", param2: Int = 0) {
val factory: KtPsiFactory
)
override fun tearDown() {
runAll(
ThrowableRunnable { ResolveElementCache.forceFullAnalysisModeInTests = false },
ThrowableRunnable { super.tearDown() },
)
}
protected fun configureWithKotlin(@Language("kotlin") text: String): KtFile =
myFixture.configureByText("Test.kt", text.trimIndent()) as KtFile
protected fun doTest(handler: Data.() -> Unit) {
val file = myFixture.configureByText("Test.kt", FILE_TEXT) as KtFile
val data = extractData(file)
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.idea
import com.intellij.psi.PsiDocumentManager
import junit.framework.TestCase
import org.intellij.lang.annotations.Language
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithAllCompilerChecks
@@ -15,6 +14,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.project.ResolveElementCache
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.psi.*
@@ -291,12 +291,12 @@ class ResolveElementCacheTest : AbstractResolveElementCacheTest() {
}
fun testIncompleteFileAnnotationList() {
val file = myFixture.configureByText(
"Test.kt", """
val file = configureWithKotlin(
"""
@file
import some.hello
"""
) as KtFile
)
val fileAnnotationList = file.fileAnnotationList!!
fileAnnotationList.analyze(BodyResolveMode.PARTIAL)
@@ -336,16 +336,12 @@ class ResolveElementCacheTest : AbstractResolveElementCacheTest() {
assertEmpty(context.diagnostics.all())
}
private fun configureWithKotlin(@Language("kotlin") text: String): KtFile {
return myFixture.configureByText("Test.kt", text.trimIndent()) as KtFile
}
fun testPrimaryConstructorParameterFullAnalysis() {
myFixture.configureByText(
"Test.kt", """
configureWithKotlin(
"""
class My(param: Int = <caret>0)
"""
) as KtFile
)
val defaultValue = myFixture.elementByOffset.getParentOfType<KtExpression>(true)!!
// Kept to preserve correct behaviour of analyzeFully() on class internal elements
@@ -355,11 +351,11 @@ class ResolveElementCacheTest : AbstractResolveElementCacheTest() {
}
fun testPrimaryConstructorAnnotationFullAnalysis() {
myFixture.configureByText(
"Test.kt", """
configureWithKotlin(
"""
class My @Deprecated("<caret>xyz") protected constructor(param: Int)
"""
) as KtFile
)
val annotationArguments = myFixture.elementByOffset.getParentOfType<KtValueArgumentList>(true)!!
@@ -368,14 +364,14 @@ class ResolveElementCacheTest : AbstractResolveElementCacheTest() {
}
fun testFunctionParameterAnnotation() {
val file = myFixture.configureByText(
"Test.kt", """
val file = configureWithKotlin(
"""
annotation class Ann
fun foo(@<caret>Ann p: Int) {
bar()
}
"""
) as KtFile
)
val function = (file.declarations[1]) as KtFunction
val typeRef = myFixture.elementByOffset.getParentOfType<KtTypeReference>(true)!!
@@ -391,12 +387,12 @@ class ResolveElementCacheTest : AbstractResolveElementCacheTest() {
}
fun testPrimaryConstructorParameterAnnotation() {
myFixture.configureByText(
"Test.kt", """
configureWithKotlin(
"""
annotation class Ann
class X(@set:<caret>Ann var p: Int)
"""
) as KtFile
)
val typeRef = myFixture.elementByOffset.getParentOfType<KtTypeReference>(true)!!
@@ -408,8 +404,8 @@ class ResolveElementCacheTest : AbstractResolveElementCacheTest() {
}
fun testSecondaryConstructorParameterAnnotation() {
val file = myFixture.configureByText(
"Test.kt", """
val file = configureWithKotlin(
"""
annotation class Ann
class X {
constructor(@<caret>Ann p: Int) {
@@ -417,7 +413,7 @@ class ResolveElementCacheTest : AbstractResolveElementCacheTest() {
}
}
"""
) as KtFile
)
val constructor = ((file.declarations[1]) as KtClass).secondaryConstructors[0]
val typeRef = myFixture.elementByOffset.getParentOfType<KtTypeReference>(true)!!
@@ -475,16 +471,28 @@ class ResolveElementCacheTest : AbstractResolveElementCacheTest() {
}
}
fun testPartialResolveIsAFullResolveForOpenedFile() {
ResolveElementCache.forceFullAnalysisModeInTests = true
doTest {
val function = members[0] as KtFunction
val bindingContextPartial = function.analyze(BodyResolveMode.PARTIAL)
val bindingContextFull = function.analyze(BodyResolveMode.FULL)
assert(bindingContextPartial === bindingContextFull) {
"Partial resolve is forced to FULL resolve for a file currently opened in editor"
}
}
}
fun testKT14376() {
val file = myFixture.configureByText("Test.kt", "object Obj(val x: Int)") as KtFile
val file = configureWithKotlin("object Obj(val x: Int)")
val nameRef = file.findDescendantOfType<KtNameReferenceExpression>()!!
val bindingContext = nameRef.analyze(BodyResolveMode.PARTIAL)
assert(bindingContext[BindingContext.REFERENCE_TARGET, nameRef]?.fqNameSafe?.asString() == "kotlin.Int")
}
fun testResolveDefaultValueInPrimaryConstructor() {
myFixture.configureByText(
"Test.kt", """
configureWithKotlin(
"""
class ClassA<N> (
messenger: ClassB<N> = object : ClassB<N> {
override fun methodOne<caret>(param: List<N>) {
@@ -496,7 +504,7 @@ class ResolveElementCacheTest : AbstractResolveElementCacheTest() {
fun methodOne(param: List<N>)
}
"""
) as KtFile
)
val methodOne = myFixture.elementByOffset.getParentOfType<KtFunction>(true)!!