Force perform FULL analysis to avoid redundant analysis for the current open file
#KT-38687 Fixed
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.project
|
package org.jetbrains.kotlin.idea.project
|
||||||
|
|
||||||
|
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.roots.ProjectRootModificationTracker
|
import com.intellij.openapi.roots.ProjectRootModificationTracker
|
||||||
import com.intellij.psi.util.CachedValue
|
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.psi.util.CachedValuesManager
|
||||||
import com.intellij.util.containers.ContainerUtil
|
import com.intellij.util.containers.ContainerUtil
|
||||||
import com.intellij.util.containers.SLRUCache
|
import com.intellij.util.containers.SLRUCache
|
||||||
|
import org.jetbrains.annotations.TestOnly
|
||||||
import org.jetbrains.kotlin.cfg.ControlFlowInformationProvider
|
import org.jetbrains.kotlin.cfg.ControlFlowInformationProvider
|
||||||
import org.jetbrains.kotlin.container.get
|
import org.jetbrains.kotlin.container.get
|
||||||
import org.jetbrains.kotlin.context.SimpleGlobalContext
|
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.KotlinCodeBlockModificationListener
|
||||||
import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListenerCompat
|
import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListenerCompat
|
||||||
import org.jetbrains.kotlin.idea.caches.trackers.inBlockModificationCount
|
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.name.Name
|
||||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
@@ -151,6 +154,22 @@ class ResolveElementCache(
|
|||||||
assert(bodyResolveMode == BodyResolveMode.FULL)
|
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
|
// check if full additional resolve already performed and is up-to-date
|
||||||
val fullResolveMap = fullResolveCache.value
|
val fullResolveMap = fullResolveCache.value
|
||||||
val cachedFullResolve = fullResolveMap[resolveElement]
|
val cachedFullResolve = fullResolveMap[resolveElement]
|
||||||
@@ -807,5 +826,10 @@ class ResolveElementCache(
|
|||||||
|
|
||||||
override fun getTopDownAnalysisMode() = topDownAnalysisMode
|
override fun getTopDownAnalysisMode() = topDownAnalysisMode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@set:TestOnly
|
||||||
|
var forceFullAnalysisModeInTests: Boolean = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -26,11 +26,11 @@ import com.intellij.util.io.exists
|
|||||||
import org.jetbrains.kotlin.idea.configuration.getModulesWithKotlinFiles
|
import org.jetbrains.kotlin.idea.configuration.getModulesWithKotlinFiles
|
||||||
import org.jetbrains.kotlin.idea.perf.Stats.Companion.runAndMeasure
|
import org.jetbrains.kotlin.idea.perf.Stats.Companion.runAndMeasure
|
||||||
import org.jetbrains.kotlin.idea.perf.util.logMessage
|
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.project.getAndCacheLanguageLevelByDependencies
|
||||||
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import kotlin.test.assertNotNull
|
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
data class OpenProject(val projectPath: String, val projectName: String, val jdk: Sdk, val projectOpenAction: ProjectOpenAction)
|
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 projectManagerEx = ProjectManagerEx.getInstanceEx()
|
||||||
|
|
||||||
val project = loadProjectWithName(projectPath, projectName)
|
val project = loadProjectWithName(projectPath, projectName)
|
||||||
assertNotNull(project, "project $projectName at $projectPath is not loaded")
|
?: error("project $projectName at $projectPath is not loaded")
|
||||||
|
|
||||||
runWriteAction {
|
runWriteAction {
|
||||||
ProjectRootManager.getInstance(project).projectSdk = jdk
|
ProjectRootManager.getInstance(project).projectSdk = jdk
|
||||||
@@ -123,6 +123,7 @@ enum class ProjectOpenAction {
|
|||||||
|
|
||||||
open fun postOpenProject(project: Project, openProject: OpenProject) {
|
open fun postOpenProject(project: Project, openProject: OpenProject) {
|
||||||
ApplicationManager.getApplication().executeOnPooledThread {
|
ApplicationManager.getApplication().executeOnPooledThread {
|
||||||
|
ResolveElementCache.forceFullAnalysisModeInTests = true
|
||||||
DumbService.getInstance(project).waitForSmartMode()
|
DumbService.getInstance(project).waitForSmartMode()
|
||||||
|
|
||||||
for (module in getModulesWithKotlinFiles(project)) {
|
for (module in getModulesWithKotlinFiles(project)) {
|
||||||
|
|||||||
@@ -5,8 +5,12 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea
|
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.KotlinLightCodeInsightFixtureTestCase
|
||||||
import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor
|
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.idea.util.application.executeWriteCommand
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
|
||||||
@@ -43,6 +47,16 @@ class C(param1: String = "", param2: Int = 0) {
|
|||||||
val factory: KtPsiFactory
|
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) {
|
protected fun doTest(handler: Data.() -> Unit) {
|
||||||
val file = myFixture.configureByText("Test.kt", FILE_TEXT) as KtFile
|
val file = myFixture.configureByText("Test.kt", FILE_TEXT) as KtFile
|
||||||
val data = extractData(file)
|
val data = extractData(file)
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.idea
|
|||||||
|
|
||||||
import com.intellij.psi.PsiDocumentManager
|
import com.intellij.psi.PsiDocumentManager
|
||||||
import junit.framework.TestCase
|
import junit.framework.TestCase
|
||||||
import org.intellij.lang.annotations.Language
|
|
||||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithAllCompilerChecks
|
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.caches.resolve.unsafeResolveToDescriptor
|
||||||
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
|
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
|
||||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
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.idea.util.application.executeWriteCommand
|
||||||
import org.jetbrains.kotlin.name.SpecialNames
|
import org.jetbrains.kotlin.name.SpecialNames
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
@@ -291,12 +291,12 @@ class ResolveElementCacheTest : AbstractResolveElementCacheTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun testIncompleteFileAnnotationList() {
|
fun testIncompleteFileAnnotationList() {
|
||||||
val file = myFixture.configureByText(
|
val file = configureWithKotlin(
|
||||||
"Test.kt", """
|
"""
|
||||||
@file
|
@file
|
||||||
import some.hello
|
import some.hello
|
||||||
"""
|
"""
|
||||||
) as KtFile
|
)
|
||||||
|
|
||||||
val fileAnnotationList = file.fileAnnotationList!!
|
val fileAnnotationList = file.fileAnnotationList!!
|
||||||
fileAnnotationList.analyze(BodyResolveMode.PARTIAL)
|
fileAnnotationList.analyze(BodyResolveMode.PARTIAL)
|
||||||
@@ -336,16 +336,12 @@ class ResolveElementCacheTest : AbstractResolveElementCacheTest() {
|
|||||||
assertEmpty(context.diagnostics.all())
|
assertEmpty(context.diagnostics.all())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun configureWithKotlin(@Language("kotlin") text: String): KtFile {
|
|
||||||
return myFixture.configureByText("Test.kt", text.trimIndent()) as KtFile
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testPrimaryConstructorParameterFullAnalysis() {
|
fun testPrimaryConstructorParameterFullAnalysis() {
|
||||||
myFixture.configureByText(
|
configureWithKotlin(
|
||||||
"Test.kt", """
|
"""
|
||||||
class My(param: Int = <caret>0)
|
class My(param: Int = <caret>0)
|
||||||
"""
|
"""
|
||||||
) as KtFile
|
)
|
||||||
|
|
||||||
val defaultValue = myFixture.elementByOffset.getParentOfType<KtExpression>(true)!!
|
val defaultValue = myFixture.elementByOffset.getParentOfType<KtExpression>(true)!!
|
||||||
// Kept to preserve correct behaviour of analyzeFully() on class internal elements
|
// Kept to preserve correct behaviour of analyzeFully() on class internal elements
|
||||||
@@ -355,11 +351,11 @@ class ResolveElementCacheTest : AbstractResolveElementCacheTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun testPrimaryConstructorAnnotationFullAnalysis() {
|
fun testPrimaryConstructorAnnotationFullAnalysis() {
|
||||||
myFixture.configureByText(
|
configureWithKotlin(
|
||||||
"Test.kt", """
|
"""
|
||||||
class My @Deprecated("<caret>xyz") protected constructor(param: Int)
|
class My @Deprecated("<caret>xyz") protected constructor(param: Int)
|
||||||
"""
|
"""
|
||||||
) as KtFile
|
)
|
||||||
|
|
||||||
val annotationArguments = myFixture.elementByOffset.getParentOfType<KtValueArgumentList>(true)!!
|
val annotationArguments = myFixture.elementByOffset.getParentOfType<KtValueArgumentList>(true)!!
|
||||||
|
|
||||||
@@ -368,14 +364,14 @@ class ResolveElementCacheTest : AbstractResolveElementCacheTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun testFunctionParameterAnnotation() {
|
fun testFunctionParameterAnnotation() {
|
||||||
val file = myFixture.configureByText(
|
val file = configureWithKotlin(
|
||||||
"Test.kt", """
|
"""
|
||||||
annotation class Ann
|
annotation class Ann
|
||||||
fun foo(@<caret>Ann p: Int) {
|
fun foo(@<caret>Ann p: Int) {
|
||||||
bar()
|
bar()
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
) as KtFile
|
)
|
||||||
|
|
||||||
val function = (file.declarations[1]) as KtFunction
|
val function = (file.declarations[1]) as KtFunction
|
||||||
val typeRef = myFixture.elementByOffset.getParentOfType<KtTypeReference>(true)!!
|
val typeRef = myFixture.elementByOffset.getParentOfType<KtTypeReference>(true)!!
|
||||||
@@ -391,12 +387,12 @@ class ResolveElementCacheTest : AbstractResolveElementCacheTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun testPrimaryConstructorParameterAnnotation() {
|
fun testPrimaryConstructorParameterAnnotation() {
|
||||||
myFixture.configureByText(
|
configureWithKotlin(
|
||||||
"Test.kt", """
|
"""
|
||||||
annotation class Ann
|
annotation class Ann
|
||||||
class X(@set:<caret>Ann var p: Int)
|
class X(@set:<caret>Ann var p: Int)
|
||||||
"""
|
"""
|
||||||
) as KtFile
|
)
|
||||||
|
|
||||||
val typeRef = myFixture.elementByOffset.getParentOfType<KtTypeReference>(true)!!
|
val typeRef = myFixture.elementByOffset.getParentOfType<KtTypeReference>(true)!!
|
||||||
|
|
||||||
@@ -408,8 +404,8 @@ class ResolveElementCacheTest : AbstractResolveElementCacheTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun testSecondaryConstructorParameterAnnotation() {
|
fun testSecondaryConstructorParameterAnnotation() {
|
||||||
val file = myFixture.configureByText(
|
val file = configureWithKotlin(
|
||||||
"Test.kt", """
|
"""
|
||||||
annotation class Ann
|
annotation class Ann
|
||||||
class X {
|
class X {
|
||||||
constructor(@<caret>Ann p: Int) {
|
constructor(@<caret>Ann p: Int) {
|
||||||
@@ -417,7 +413,7 @@ class ResolveElementCacheTest : AbstractResolveElementCacheTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
) as KtFile
|
)
|
||||||
|
|
||||||
val constructor = ((file.declarations[1]) as KtClass).secondaryConstructors[0]
|
val constructor = ((file.declarations[1]) as KtClass).secondaryConstructors[0]
|
||||||
val typeRef = myFixture.elementByOffset.getParentOfType<KtTypeReference>(true)!!
|
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() {
|
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 nameRef = file.findDescendantOfType<KtNameReferenceExpression>()!!
|
||||||
val bindingContext = nameRef.analyze(BodyResolveMode.PARTIAL)
|
val bindingContext = nameRef.analyze(BodyResolveMode.PARTIAL)
|
||||||
assert(bindingContext[BindingContext.REFERENCE_TARGET, nameRef]?.fqNameSafe?.asString() == "kotlin.Int")
|
assert(bindingContext[BindingContext.REFERENCE_TARGET, nameRef]?.fqNameSafe?.asString() == "kotlin.Int")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun testResolveDefaultValueInPrimaryConstructor() {
|
fun testResolveDefaultValueInPrimaryConstructor() {
|
||||||
myFixture.configureByText(
|
configureWithKotlin(
|
||||||
"Test.kt", """
|
"""
|
||||||
class ClassA<N> (
|
class ClassA<N> (
|
||||||
messenger: ClassB<N> = object : ClassB<N> {
|
messenger: ClassB<N> = object : ClassB<N> {
|
||||||
override fun methodOne<caret>(param: List<N>) {
|
override fun methodOne<caret>(param: List<N>) {
|
||||||
@@ -496,7 +504,7 @@ class ResolveElementCacheTest : AbstractResolveElementCacheTest() {
|
|||||||
fun methodOne(param: List<N>)
|
fun methodOne(param: List<N>)
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
) as KtFile
|
)
|
||||||
|
|
||||||
val methodOne = myFixture.elementByOffset.getParentOfType<KtFunction>(true)!!
|
val methodOne = myFixture.elementByOffset.getParentOfType<KtFunction>(true)!!
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user