Fix a problem where a sequence of out-of-block completions could lead to an inconsistent resolve session provided for a synthetic copy of a file

This commit is contained in:
Pavel V. Talanov
2015-06-03 14:19:58 +03:00
parent 7b63dd987c
commit f335e9f6b2
5 changed files with 66 additions and 5 deletions
@@ -103,7 +103,7 @@ public class KotlinCodeBlockModificationListener(modificationTracker: PsiModific
return false
}
private fun isInsideCodeBlock(element: PsiElement?): Boolean {
public fun isInsideCodeBlock(element: PsiElement?): Boolean {
if (element is PsiFileSystemItem) return false
if (element == null || element.getParent() == null) return true
@@ -104,6 +104,10 @@ public class KotlinCacheService(val project: Project) {
// we assume that all files come from the same module
val targetPlatform = files.map { TargetPlatformDetector.getPlatform(it) }.toSet().single()
val syntheticFileModule = files.map { it.getModuleInfo() }.toSet().single()
val dependenciesForSyntheticFileCache = listOf(
PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT,
KotlinOutOfBlockCompletionModificationTracker.getInstance(project)
)
return when {
syntheticFileModule is ModuleSourceInfo -> {
val dependentModules = syntheticFileModule.getDependentModules()
@@ -114,7 +118,7 @@ public class KotlinCacheService(val project: Project) {
syntheticFiles = files,
reuseDataFromCache = getGlobalCache(targetPlatform),
moduleFilter = { it in dependentModules },
dependencies = listOf(PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT)
dependencies = dependenciesForSyntheticFileCache
)
)
}
@@ -127,7 +131,7 @@ public class KotlinCacheService(val project: Project) {
syntheticFiles = files,
reuseDataFromCache = getGlobalLibrariesCache(targetPlatform),
moduleFilter = { it == syntheticFileModule },
dependencies = listOf(PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT)
dependencies = dependenciesForSyntheticFileCache
)
)
}
@@ -143,7 +147,7 @@ public class KotlinCacheService(val project: Project) {
targetPlatform,
syntheticFiles = files,
moduleFilter = { true },
dependencies = listOf(PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT)
dependencies = dependenciesForSyntheticFileCache
)
)
}
@@ -0,0 +1,48 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.SimpleModificationTracker
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.asJava.KotlinCodeBlockModificationListener
// NOTE: Sadly we must track out of block completion and drop sessions for synthetic files after the completion happens.
// Synthetic file for completion can be modified without sending tree changed events and sequence of completions can lead to inconsistent
// resolve session being cached for such a file otherwise.
// This code is not tested. See KT-6216 for an example.
public class KotlinOutOfBlockCompletionModificationTracker() : SimpleModificationTracker() {
companion object {
public fun getInstance(project: Project): KotlinOutOfBlockCompletionModificationTracker
= ServiceManager.getService(project, javaClass<KotlinOutOfBlockCompletionModificationTracker>())!!
}
}
public fun performCompletionWithOutOfBlockTracking(completionPosition: PsiElement, body: () -> Unit) {
if (KotlinCodeBlockModificationListener.isInsideCodeBlock(completionPosition)) {
body()
return
}
try {
body()
}
finally {
KotlinOutOfBlockCompletionModificationTracker.getInstance(completionPosition.getProject()).incModificationCount()
}
}
@@ -34,11 +34,11 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.performCompletionWithOutOfBlockTracking
import org.jetbrains.kotlin.idea.completion.smart.SmartCompletion
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.bindingContextUtil.getReferenceTargets
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
@@ -197,6 +197,12 @@ public class KotlinCompletionContributor : CompletionContributor() {
val position = parameters.getPosition()
if (position.getContainingFile() !is JetFile) return
performCompletionWithOutOfBlockTracking(position) {
doComplete(parameters, position, result)
}
}
private fun doComplete(parameters: CompletionParameters, position: PsiElement, result: CompletionResultSet) {
if (position.getNonStrictParentOfType<PsiComment>() != null) {
// don't stop here, allow other contributors to run
return
+3
View File
@@ -219,6 +219,9 @@
<projectService serviceInterface="org.jetbrains.kotlin.idea.caches.resolve.LibraryModificationTracker"
serviceImplementation="org.jetbrains.kotlin.idea.caches.resolve.LibraryModificationTracker"/>
<projectService serviceInterface="org.jetbrains.kotlin.idea.caches.resolve.KotlinOutOfBlockCompletionModificationTracker"
serviceImplementation="org.jetbrains.kotlin.idea.caches.resolve.KotlinOutOfBlockCompletionModificationTracker"/>
<projectService serviceInterface="org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade"
serviceImplementation="org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade"/>