ResolveElementCache works correctly for non-physical files
This commit is contained in:
@@ -28,6 +28,8 @@ import org.jetbrains.kotlin.psi.findDocComment.FindDocCommentPackage;
|
|||||||
import org.jetbrains.kotlin.psi.stubs.KotlinClassOrObjectStub;
|
import org.jetbrains.kotlin.psi.stubs.KotlinClassOrObjectStub;
|
||||||
|
|
||||||
abstract class JetDeclarationStub<T extends StubElement<?>> extends JetModifierListOwnerStub<T> implements JetDeclaration {
|
abstract class JetDeclarationStub<T extends StubElement<?>> extends JetModifierListOwnerStub<T> implements JetDeclaration {
|
||||||
|
private long modificationStamp = 0;
|
||||||
|
|
||||||
public JetDeclarationStub(@NotNull T stub, @NotNull IStubElementType nodeType) {
|
public JetDeclarationStub(@NotNull T stub, @NotNull IStubElementType nodeType) {
|
||||||
super(stub, nodeType);
|
super(stub, nodeType);
|
||||||
}
|
}
|
||||||
@@ -36,6 +38,16 @@ abstract class JetDeclarationStub<T extends StubElement<?>> extends JetModifierL
|
|||||||
super(node);
|
super(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void subtreeChanged() {
|
||||||
|
super.subtreeChanged();
|
||||||
|
modificationStamp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getModificationStamp() {
|
||||||
|
return modificationStamp;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public KDoc getDocComment() {
|
public KDoc getDocComment() {
|
||||||
|
|||||||
@@ -29,3 +29,11 @@ public interface JetElement : NavigatablePsiElement {
|
|||||||
@deprecated("Don't use getReference() on JetElement for the choice is unpredictable")
|
@deprecated("Don't use getReference() on JetElement for the choice is unpredictable")
|
||||||
override fun getReference(): PsiReference?
|
override fun getReference(): PsiReference?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun JetElement.getModificationStamp(): Long {
|
||||||
|
return when (this) {
|
||||||
|
is JetFile -> this.modificationStamp
|
||||||
|
is JetDeclarationStub<*> -> this.modificationStamp
|
||||||
|
else -> (parent as JetElement).getModificationStamp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
-81
@@ -1,81 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.util.Key
|
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import com.intellij.psi.impl.PsiTreeChangeEventImpl
|
|
||||||
import com.intellij.psi.impl.PsiTreeChangePreprocessor
|
|
||||||
import org.jetbrains.kotlin.psi.JetFile
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
|
||||||
|
|
||||||
public class SubtreeModificationCountUpdater : PsiTreeChangePreprocessor {
|
|
||||||
override fun treeChanged(event: PsiTreeChangeEventImpl) {
|
|
||||||
if (event.file !is JetFile) return
|
|
||||||
|
|
||||||
when (event.code!!) {
|
|
||||||
PsiTreeChangeEventImpl.PsiEventType.BEFORE_CHILDREN_CHANGE,
|
|
||||||
PsiTreeChangeEventImpl.PsiEventType.BEFORE_PROPERTY_CHANGE,
|
|
||||||
PsiTreeChangeEventImpl.PsiEventType.BEFORE_CHILD_MOVEMENT,
|
|
||||||
PsiTreeChangeEventImpl.PsiEventType.BEFORE_CHILD_REPLACEMENT,
|
|
||||||
PsiTreeChangeEventImpl.PsiEventType.BEFORE_CHILD_ADDITION,
|
|
||||||
PsiTreeChangeEventImpl.PsiEventType.BEFORE_CHILD_REMOVAL,
|
|
||||||
PsiTreeChangeEventImpl.PsiEventType.PROPERTY_CHANGED -> {
|
|
||||||
// skip
|
|
||||||
}
|
|
||||||
|
|
||||||
PsiTreeChangeEventImpl.PsiEventType.CHILD_ADDED,
|
|
||||||
PsiTreeChangeEventImpl.PsiEventType.CHILD_REMOVED,
|
|
||||||
PsiTreeChangeEventImpl.PsiEventType.CHILD_REPLACED -> {
|
|
||||||
incCounters(event.parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
PsiTreeChangeEventImpl.PsiEventType.CHILDREN_CHANGED -> {
|
|
||||||
if (!event.isGenericChange) {
|
|
||||||
incCounters(event.parent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PsiTreeChangeEventImpl.PsiEventType.CHILD_MOVED -> {
|
|
||||||
incCounters(event.oldParent)
|
|
||||||
incCounters(event.newParent)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> error("Unknown code:${event.code}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun incCounters(element: PsiElement?) {
|
|
||||||
element?.parentsWithSelf?.forEach {
|
|
||||||
val count = it.getUserData(MODIFICATION_COUNT_KEY)
|
|
||||||
if (count != null) {
|
|
||||||
it.putUserData(MODIFICATION_COUNT_KEY, count + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val MODIFICATION_COUNT_KEY = Key<Int>("SubtreeModificationCountUpdater.MODIFICATION_COUNT_KEY")
|
|
||||||
|
|
||||||
public fun getModificationCount(element: PsiElement): Int {
|
|
||||||
element.getUserData(MODIFICATION_COUNT_KEY)?.let { return it }
|
|
||||||
|
|
||||||
element.putUserData(MODIFICATION_COUNT_KEY, 0)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -23,8 +23,6 @@ import com.intellij.psi.util.CachedValuesManager
|
|||||||
import com.intellij.psi.util.PsiModificationTracker
|
import com.intellij.psi.util.PsiModificationTracker
|
||||||
import com.intellij.util.containers.ContainerUtil
|
import com.intellij.util.containers.ContainerUtil
|
||||||
import org.jetbrains.kotlin.asJava.KotlinCodeBlockModificationListener
|
import org.jetbrains.kotlin.asJava.KotlinCodeBlockModificationListener
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.SubtreeModificationCountUpdater
|
|
||||||
import org.jetbrains.kotlin.idea.stubindex.JetProbablyNothingFunctionShortNameIndex
|
import org.jetbrains.kotlin.idea.stubindex.JetProbablyNothingFunctionShortNameIndex
|
||||||
import org.jetbrains.kotlin.idea.stubindex.JetProbablyNothingPropertyShortNameIndex
|
import org.jetbrains.kotlin.idea.stubindex.JetProbablyNothingPropertyShortNameIndex
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
@@ -33,34 +31,53 @@ import org.jetbrains.kotlin.resolve.BodyResolveCache
|
|||||||
import org.jetbrains.kotlin.resolve.StatementFilter
|
import org.jetbrains.kotlin.resolve.StatementFilter
|
||||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||||
import org.jetbrains.kotlin.resolve.lazy.*
|
import org.jetbrains.kotlin.resolve.lazy.*
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||||
|
|
||||||
public class ResolveElementCache(resolveSession: ResolveSession, private val project: Project) : ElementResolver(resolveSession), BodyResolveCache {
|
public class ResolveElementCache(resolveSession: ResolveSession, private val project: Project) : ElementResolver(resolveSession), BodyResolveCache {
|
||||||
private class CachedResolve(val bindingContext: BindingContext, resolveElement: JetElement) {
|
private class CachedFullResolve(val bindingContext: BindingContext, resolveElement: JetElement) {
|
||||||
private val elementModificationCount: Int? =
|
private val modificationStamp: Long? = modificationStamp(resolveElement)
|
||||||
if (resolveElement is JetDeclaration && KotlinCodeBlockModificationListener.isBlockDeclaration(resolveElement))
|
|
||||||
SubtreeModificationCountUpdater.getModificationCount(resolveElement)
|
|
||||||
else
|
|
||||||
null
|
|
||||||
|
|
||||||
fun isUpToDate(resolveElement: JetElement)
|
fun isUpToDate(resolveElement: JetElement) = modificationStamp == modificationStamp(resolveElement)
|
||||||
= elementModificationCount == null || elementModificationCount == SubtreeModificationCountUpdater.getModificationCount(resolveElement)
|
|
||||||
|
private fun modificationStamp(resolveElement: JetElement): Long? {
|
||||||
|
val file = resolveElement.containingFile
|
||||||
|
return if (!file.isPhysical) // for non-physical file we don't get OUT_OF_CODE_BLOCK_MODIFICATION_COUNT increased and must reset data on any modification of the file
|
||||||
|
file.modificationStamp
|
||||||
|
else if (resolveElement is JetDeclaration && KotlinCodeBlockModificationListener.isBlockDeclaration(resolveElement))
|
||||||
|
resolveElement.getModificationStamp()
|
||||||
|
else
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// drop whole cache after change "out of code block"
|
// drop whole cache after change "out of code block"
|
||||||
private val fullResolveCache: CachedValue<MutableMap<JetElement, CachedResolve>> = CachedValuesManager.getManager(project).createCachedValue(
|
private val fullResolveCache: CachedValue<MutableMap<JetElement, CachedFullResolve>> = CachedValuesManager.getManager(project).createCachedValue(
|
||||||
object : CachedValueProvider<MutableMap<JetElement, CachedResolve>> {
|
object : CachedValueProvider<MutableMap<JetElement, CachedFullResolve>> {
|
||||||
override fun compute(): CachedValueProvider.Result<MutableMap<JetElement, CachedResolve>> {
|
override fun compute(): CachedValueProvider.Result<MutableMap<JetElement, CachedFullResolve>> {
|
||||||
return CachedValueProvider.Result.create(ContainerUtil.createConcurrentSoftValueMap<JetElement, CachedResolve>(),
|
return CachedValueProvider.Result.create(ContainerUtil.createConcurrentSoftValueMap<JetElement, CachedFullResolve>(),
|
||||||
PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT,
|
PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT,
|
||||||
resolveSession.exceptionTracker)
|
resolveSession.exceptionTracker)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
false)
|
false)
|
||||||
|
|
||||||
private val partialBodyResolveCache: CachedValue<MutableMap<JetExpression, BindingContext>> = CachedValuesManager.getManager(project).createCachedValue(
|
private class CachedPartialResolve(val bindingContext: BindingContext, file: JetFile) {
|
||||||
object : CachedValueProvider<MutableMap<JetExpression, BindingContext>> {
|
private val modificationStamp: Long? = modificationStamp(file)
|
||||||
override fun compute(): CachedValueProvider.Result<MutableMap<JetExpression, BindingContext>> {
|
|
||||||
return CachedValueProvider.Result.create(ContainerUtil.createConcurrentSoftValueMap<JetExpression, BindingContext>(),
|
fun isUpToDate(file: JetFile) = modificationStamp == modificationStamp(file)
|
||||||
|
|
||||||
|
private fun modificationStamp(file: JetFile): Long? {
|
||||||
|
return if (!file.isPhysical) // for non-physical file we don't get MODIFICATION_COUNT increased and must reset data on any modification of the file
|
||||||
|
file.modificationStamp
|
||||||
|
else
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val partialBodyResolveCache: CachedValue<MutableMap<JetExpression, CachedPartialResolve>> = CachedValuesManager.getManager(project).createCachedValue(
|
||||||
|
object : CachedValueProvider<MutableMap<JetExpression, CachedPartialResolve>> {
|
||||||
|
override fun compute(): CachedValueProvider.Result<MutableMap<JetExpression, CachedPartialResolve>> {
|
||||||
|
return CachedValueProvider.Result.create(ContainerUtil.createConcurrentSoftValueMap<JetExpression, CachedPartialResolve>(),
|
||||||
PsiModificationTracker.MODIFICATION_COUNT,
|
PsiModificationTracker.MODIFICATION_COUNT,
|
||||||
resolveSession.exceptionTracker)
|
resolveSession.exceptionTracker)
|
||||||
}
|
}
|
||||||
@@ -83,7 +100,7 @@ public class ResolveElementCache(resolveSession: ResolveSession, private val pro
|
|||||||
when (bodyResolveMode) {
|
when (bodyResolveMode) {
|
||||||
BodyResolveMode.FULL -> {
|
BodyResolveMode.FULL -> {
|
||||||
val bindingContext = performElementAdditionalResolve(resolveElement, resolveElement, BodyResolveMode.FULL).first
|
val bindingContext = performElementAdditionalResolve(resolveElement, resolveElement, BodyResolveMode.FULL).first
|
||||||
fullResolveMap[resolveElement] = CachedResolve(bindingContext, resolveElement)
|
fullResolveMap[resolveElement] = CachedFullResolve(bindingContext, resolveElement)
|
||||||
return bindingContext
|
return bindingContext
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,23 +109,28 @@ public class ResolveElementCache(resolveSession: ResolveSession, private val pro
|
|||||||
return getElementAdditionalResolve(resolveElement, contextElement, BodyResolveMode.FULL)
|
return getElementAdditionalResolve(resolveElement, contextElement, BodyResolveMode.FULL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val file = resolveElement.getContainingJetFile()
|
||||||
val statementToResolve = PartialBodyResolveFilter.findStatementToResolve(contextElement, resolveElement)
|
val statementToResolve = PartialBodyResolveFilter.findStatementToResolve(contextElement, resolveElement)
|
||||||
val partialResolveMap = partialBodyResolveCache.value
|
val partialResolveMap = partialBodyResolveCache.value
|
||||||
partialResolveMap[statementToResolve ?: resolveElement]?.let { return it } // partial resolve is already cached for this statement
|
partialResolveMap[statementToResolve ?: resolveElement]
|
||||||
|
?.check { it.isUpToDate(file) }
|
||||||
|
?.let { return it.bindingContext } // partial resolve is already cached for this statement
|
||||||
|
|
||||||
val (bindingContext, statementFilter) = performElementAdditionalResolve(resolveElement, contextElement, BodyResolveMode.PARTIAL)
|
val (bindingContext, statementFilter) = performElementAdditionalResolve(resolveElement, contextElement, BodyResolveMode.PARTIAL)
|
||||||
|
|
||||||
if (statementFilter == StatementFilter.NONE) { // partial resolve is not supported for the given declaration - full resolve performed instead
|
if (statementFilter == StatementFilter.NONE) { // partial resolve is not supported for the given declaration - full resolve performed instead
|
||||||
fullResolveMap[resolveElement] = CachedResolve(bindingContext, resolveElement)
|
fullResolveMap[resolveElement] = CachedFullResolve(bindingContext, resolveElement)
|
||||||
return bindingContext
|
return bindingContext
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val resolveToCache = CachedPartialResolve(bindingContext, file)
|
||||||
|
|
||||||
for (statement in (statementFilter as PartialBodyResolveFilter).allStatementsToResolve) {
|
for (statement in (statementFilter as PartialBodyResolveFilter).allStatementsToResolve) {
|
||||||
if (!partialResolveMap.containsKey(statement) && bindingContext[BindingContext.PROCESSED, statement] == true) {
|
if (!partialResolveMap.containsKey(statement) && bindingContext[BindingContext.PROCESSED, statement] == true) {
|
||||||
partialResolveMap[statement] = bindingContext
|
partialResolveMap[statement] = resolveToCache
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
partialResolveMap[resolveElement] = bindingContext // we use the whole declaration key in the map to obtain resolve not inside any block (e.g. default parameter values)
|
partialResolveMap[resolveElement] = resolveToCache // we use the whole declaration key in the map to obtain resolve not inside any block (e.g. default parameter values)
|
||||||
|
|
||||||
return bindingContext
|
return bindingContext
|
||||||
}
|
}
|
||||||
@@ -136,3 +158,4 @@ public class ResolveElementCache(resolveSession: ResolveSession, private val pro
|
|||||||
override fun resolveFunctionBody(function: JetNamedFunction)
|
override fun resolveFunctionBody(function: JetNamedFunction)
|
||||||
= getElementAdditionalResolve(function, function, BodyResolveMode.FULL)
|
= getElementAdditionalResolve(function, function, BodyResolveMode.FULL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -503,7 +503,6 @@
|
|||||||
<editorNotificationProvider implementation="org.jetbrains.kotlin.idea.highlighter.ErrorDuringFileAnalyzeNotificationProvider"/>
|
<editorNotificationProvider implementation="org.jetbrains.kotlin.idea.highlighter.ErrorDuringFileAnalyzeNotificationProvider"/>
|
||||||
|
|
||||||
<psi.treeChangePreprocessor implementation="org.jetbrains.kotlin.asJava.KotlinCodeBlockModificationListener"/>
|
<psi.treeChangePreprocessor implementation="org.jetbrains.kotlin.asJava.KotlinCodeBlockModificationListener"/>
|
||||||
<psi.treeChangePreprocessor implementation="org.jetbrains.kotlin.idea.caches.resolve.SubtreeModificationCountUpdater"/>
|
|
||||||
|
|
||||||
<referencesSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearcher"/>
|
<referencesSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearcher"/>
|
||||||
<directClassInheritorsSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinDirectInheritorsSearcher"/>
|
<directClassInheritorsSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinDirectInheritorsSearcher"/>
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ class C(param1: String = "", param2: Int = 0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun b() {
|
fun b() {
|
||||||
|
x(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun c() {
|
fun c() {
|
||||||
@@ -56,45 +57,83 @@ class C(param1: String = "", param2: Int = 0) {
|
|||||||
|
|
||||||
private fun doTest(handler: Data.() -> Unit) {
|
private fun doTest(handler: Data.() -> Unit) {
|
||||||
val file = myFixture.configureByText("Test.kt", FILE_TEXT) as JetFile
|
val file = myFixture.configureByText("Test.kt", FILE_TEXT) as JetFile
|
||||||
val klass = file.getDeclarations().single() as JetClass
|
val data = extractData(file)
|
||||||
val members = klass.getDeclarations()
|
myFixture.project.executeWriteCommand("") { data.handler() }
|
||||||
val function = members.first() as JetNamedFunction
|
|
||||||
val statements = (function.getBodyExpression() as JetBlockExpression).getStatements()
|
|
||||||
myFixture.getProject().executeWriteCommand("") {
|
|
||||||
Data(file, klass, members, statements, JetPsiFactory(getProject())).handler()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun testResolveCaching() {
|
private fun extractData(file: JetFile): Data {
|
||||||
|
val klass = file.declarations.single() as JetClass
|
||||||
|
val members = klass.declarations
|
||||||
|
val function = members.first() as JetNamedFunction
|
||||||
|
val statements = (function.bodyExpression as JetBlockExpression).statements
|
||||||
|
return Data(file, klass, members, statements, JetPsiFactory(getProject()))
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun testFullResolveCaching() {
|
||||||
|
doTest { this.testResolveCaching() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Data.testResolveCaching() {
|
||||||
|
// resolve statements in "a()"
|
||||||
|
val statement1 = statements[0]
|
||||||
|
val statement2 = statements[1]
|
||||||
|
val aFunBodyContext1 = statement1.analyze(BodyResolveMode.FULL)
|
||||||
|
val aFunBodyContext2 = statement2.analyze(BodyResolveMode.FULL)
|
||||||
|
assert(aFunBodyContext1 === aFunBodyContext2)
|
||||||
|
|
||||||
|
val aFunBodyContext3 = statement1.analyze(BodyResolveMode.FULL)
|
||||||
|
assert(aFunBodyContext3 === aFunBodyContext1)
|
||||||
|
|
||||||
|
val bFun = members[1] as JetNamedFunction
|
||||||
|
val bBody = bFun.bodyExpression as JetBlockExpression
|
||||||
|
val bStatement = bBody.statements[0]
|
||||||
|
val bFunBodyContext = bStatement.analyze(BodyResolveMode.FULL)
|
||||||
|
|
||||||
|
// modify body of "b()"
|
||||||
|
bBody.addAfter(factory.createExpression("x()"), bBody.lBrace)
|
||||||
|
val bFunBodyContextAfterChange1 = bStatement.analyze(BodyResolveMode.FULL)
|
||||||
|
assert(bFunBodyContext !== bFunBodyContextAfterChange1)
|
||||||
|
|
||||||
|
if (file.isPhysical) { // for non-physical files we reset caches for whole file
|
||||||
|
val aFunBodyContextAfterChange1 = statement1.analyze(BodyResolveMode.FULL)
|
||||||
|
assert(aFunBodyContextAfterChange1 === aFunBodyContext1) // change in other function's body should not affect resolve of other one
|
||||||
|
}
|
||||||
|
|
||||||
|
// add parameter to "b()" this should invalidate all resolve
|
||||||
|
bFun.valueParameterList!!.addParameter(factory.createParameter("p: Int"))
|
||||||
|
|
||||||
|
val aFunBodyContextAfterChange2 = statement1.analyze(BodyResolveMode.FULL)
|
||||||
|
assert(aFunBodyContextAfterChange2 !== aFunBodyContext1)
|
||||||
|
|
||||||
|
val bFunBodyContextAfterChange2 = bStatement.analyze(BodyResolveMode.FULL)
|
||||||
|
assert(bFunBodyContextAfterChange2 !== bFunBodyContextAfterChange1)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun testNonPhysicalFileFullResolveCaching() {
|
||||||
doTest {
|
doTest {
|
||||||
// resolve statements in "a()"
|
val nonPhysicalFile = JetPsiFactory(getProject()).createAnalyzableFile("NonPhysical.kt", FILE_TEXT, file)
|
||||||
val statement1 = statements[0]
|
val nonPhysicalData = extractData(nonPhysicalFile)
|
||||||
val statement2 = statements[1]
|
nonPhysicalData.testResolveCaching()
|
||||||
val bindingContext1 = statement1.analyze(BodyResolveMode.FULL)
|
|
||||||
val bindingContext2 = statement2.analyze(BodyResolveMode.FULL)
|
|
||||||
assert(bindingContext1 === bindingContext2)
|
|
||||||
|
|
||||||
val bindingContext3 = statement1.analyze(BodyResolveMode.FULL)
|
// now check how changes in the physical file affect non-physical one
|
||||||
assert(bindingContext3 === bindingContext1)
|
val statement = nonPhysicalData.statements[0]
|
||||||
|
val nonPhysicalContext1 = statement.analyze(BodyResolveMode.FULL)
|
||||||
|
|
||||||
// modify body of "b()"
|
statements[0].delete()
|
||||||
val bFun = members[1] as JetNamedFunction
|
|
||||||
val bBody = bFun.bodyExpression as JetBlockExpression
|
|
||||||
bBody.addAfter(factory.createExpression("x()"), bBody.lBrace)
|
|
||||||
|
|
||||||
val bindingContext4 = statement1.analyze(BodyResolveMode.FULL)
|
val nonPhysicalContext2 = statement.analyze(BodyResolveMode.FULL)
|
||||||
assert(bindingContext4 === bindingContext1) // change in other function's body should not affect resolve of other one
|
assert(nonPhysicalContext2 === nonPhysicalContext1) // change inside function's body should not affect other files
|
||||||
|
|
||||||
// add parameter to "b()" this should invalidate all resolve
|
members[2].delete()
|
||||||
bFun.valueParameterList!!.addParameter(factory.createParameter("p: Int"))
|
|
||||||
|
|
||||||
val bindingContext5 = statement1.analyze(BodyResolveMode.FULL)
|
val nonPhysicalContext3 = statement.analyze(BodyResolveMode.FULL)
|
||||||
assert(bindingContext5 !== bindingContext1)
|
assert(nonPhysicalContext3 !== nonPhysicalContext1)
|
||||||
|
|
||||||
statement1.parent.addAfter(factory.createExpression("x()"), statement1)
|
// and now check that non-physical changes do not affect physical world
|
||||||
|
val physicalContext1 = statements[1].analyze(BodyResolveMode.FULL)
|
||||||
val bindingContext6 = statement1.analyze(BodyResolveMode.FULL)
|
nonPhysicalData.members[0].delete()
|
||||||
assert(bindingContext6 !== bindingContext5)
|
val physicalContext2 = statements[1].analyze(BodyResolveMode.FULL)
|
||||||
|
assert(physicalContext1 === physicalContext2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,27 +188,37 @@ class C(param1: String = "", param2: Int = 0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public fun testPartialResolveCaching() {
|
public fun testPartialResolveCaching() {
|
||||||
|
doTest { this.testPartialResolveCaching() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Data.testPartialResolveCaching() {
|
||||||
|
val statement1 = statements[0]
|
||||||
|
val statement2 = statements[1]
|
||||||
|
val bindingContext1 = statement1.analyze(BodyResolveMode.PARTIAL)
|
||||||
|
val bindingContext2 = statement2.analyze(BodyResolveMode.PARTIAL)
|
||||||
|
assert(bindingContext1 !== bindingContext2)
|
||||||
|
|
||||||
|
val bindingContext3 = statement1.analyze(BodyResolveMode.PARTIAL)
|
||||||
|
val bindingContext4 = statement2.analyze(BodyResolveMode.PARTIAL)
|
||||||
|
assert(bindingContext3 === bindingContext1)
|
||||||
|
assert(bindingContext4 === bindingContext2)
|
||||||
|
|
||||||
|
file.add(factory.createFunction("fun foo(){}"))
|
||||||
|
|
||||||
|
val bindingContext5 = statement1.analyze(BodyResolveMode.PARTIAL)
|
||||||
|
assert(bindingContext5 !== bindingContext1)
|
||||||
|
|
||||||
|
statement1.getParent().addAfter(factory.createExpression("x()"), statement1)
|
||||||
|
|
||||||
|
val bindingContext6 = statement1.analyze(BodyResolveMode.PARTIAL)
|
||||||
|
assert(bindingContext6 !== bindingContext5)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun testNonPhysicalFilePartialResolveCaching() {
|
||||||
doTest {
|
doTest {
|
||||||
val statement1 = statements[0]
|
val nonPhysicalFile = JetPsiFactory(getProject()).createAnalyzableFile("NonPhysical.kt", FILE_TEXT, file)
|
||||||
val statement2 = statements[1]
|
val nonPhysicalData = extractData(nonPhysicalFile)
|
||||||
val bindingContext1 = statement1.analyze(BodyResolveMode.PARTIAL)
|
nonPhysicalData.testPartialResolveCaching()
|
||||||
val bindingContext2 = statement2.analyze(BodyResolveMode.PARTIAL)
|
|
||||||
assert(bindingContext1 !== bindingContext2)
|
|
||||||
|
|
||||||
val bindingContext3 = statement1.analyze(BodyResolveMode.PARTIAL)
|
|
||||||
val bindingContext4 = statement2.analyze(BodyResolveMode.PARTIAL)
|
|
||||||
assert(bindingContext3 === bindingContext1)
|
|
||||||
assert(bindingContext4 === bindingContext2)
|
|
||||||
|
|
||||||
file.add(factory.createFunction("fun foo(){}"))
|
|
||||||
|
|
||||||
val bindingContext5 = statement1.analyze(BodyResolveMode.PARTIAL)
|
|
||||||
assert(bindingContext5 !== bindingContext1)
|
|
||||||
|
|
||||||
statement1.getParent().addAfter(factory.createExpression("x()"), statement1)
|
|
||||||
|
|
||||||
val bindingContext6 = statement1.analyze(BodyResolveMode.PARTIAL)
|
|
||||||
assert(bindingContext6 !== bindingContext5)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user