Make changes in super call lambda not cause out-of-block modification (KT-13474)

#KT-13474 Fixed
This commit is contained in:
Nikolay Krasko
2016-08-26 21:17:39 +03:00
parent 64d511566e
commit 52dd02fe08
9 changed files with 77 additions and 7 deletions
+1
View File
@@ -104,6 +104,7 @@ These artifacts include extensions for the types available in the latter JDKs, s
- [`KT-13589`](https://youtrack.jetbrains.com/issue/KT-13589) Use TODO() consistently in implementation stubs
- [`KT-13630`](https://youtrack.jetbrains.com/issue/KT-13630) Do not show Change Signature dialog when applying "Remove parameter" quick-fix
- Re-highlight only single function after local modifications
- [`KT-13474`](https://youtrack.jetbrains.com/issue/KT-13474) Fix performance of typing super call lambda
#### Intention actions, inspections and quickfixes
@@ -34,6 +34,7 @@ fun KtElement.getModificationStamp(): Long {
return when (this) {
is KtFile -> this.modificationStamp
is KtDeclarationStub<*> -> this.modificationStamp
is KtSuperTypeList -> this.modificationStamp
else -> (parent as KtElement).getModificationStamp()
}
}
@@ -29,8 +29,11 @@ import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
public class KtSuperTypeList extends KtElementImplStub<KotlinPlaceHolderStub<KtSuperTypeList>> {
private final AtomicLong modificationStamp = new AtomicLong();
public KtSuperTypeList(@NotNull ASTNode node) {
super(node);
}
@@ -66,4 +69,15 @@ public class KtSuperTypeList extends KtElementImplStub<KotlinPlaceHolderStub<KtS
public List<KtSuperTypeListEntry> getEntries() {
return Arrays.asList(getStubOrPsiChildren(KtStubElementTypes.SUPER_TYPE_LIST_ENTRIES, KtSuperTypeListEntry.ARRAY_FACTORY));
}
@Override
public void subtreeChanged() {
super.subtreeChanged();
modificationStamp.getAndIncrement();
}
public long getModificationStamp() {
return modificationStamp.get();
}
}
@@ -68,7 +68,13 @@ class KotlinCodeBlockModificationListener(
}
private fun isInsideCodeBlock(element: PsiElement): Boolean {
//TODO: other types
val lambda = KtPsiUtil.getTopmostParentOfTypes(element, KtLambdaExpression::class.java)
if (lambda is KtLambdaExpression) {
if (KtPsiUtil.getTopmostParentOfTypes(lambda, KtSuperTypeCallEntry::class.java) != null) {
return true
}
}
val blockDeclaration = KtPsiUtil.getTopmostParentOfTypes(element, *BLOCK_DECLARATION_TYPES) ?: return false
if (blockDeclaration.parents.any { it !is KtClassBody && it !is KtClassOrObject && it !is KtFile }) return false // should not be local declaration
@@ -60,12 +60,15 @@ class ResolveElementCache(
private fun modificationStamp(resolveElement: KtElement): 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 KtDeclaration && KotlinCodeBlockModificationListener.isBlockDeclaration(resolveElement))
resolveElement.getModificationStamp()
else
null
return when {
// 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.isPhysical -> file.modificationStamp
resolveElement is KtDeclaration && KotlinCodeBlockModificationListener.isBlockDeclaration(resolveElement) -> resolveElement.getModificationStamp()
resolveElement is KtSuperTypeList -> resolveElement.modificationStamp
else -> null
}
}
}
@@ -0,0 +1,12 @@
// FALSE
// Navigation from "class B: A()" should move to valid constructor even after changing type in lambda
open class A(l: String) {
constructor(x: Int) : this("$x")
}
fun <T> foo(l: () -> T) = l()
class B: A(foo { "1"<caret> })
@@ -0,0 +1,5 @@
// FALSE
open class A(a: () -> Unit)
class B: A({ "1"<caret> })
@@ -0,0 +1,10 @@
// TRUE
// Can't result to false as there's no body expression, so it's considered to be changes in JavaCodeBlockModificationListener.
open class A(a: () -> Unit) {
constructor(f: (String) -> Unit) : this({ -> f("") })
}
class B: A({ s<caret> -> "1" })
// SKIP_ANALYZE_CHECK
@@ -191,6 +191,24 @@ public class OutOfBlockModificationTestGenerated extends AbstractOutOfBlockModif
doTest(fileName);
}
@TestMetadata("InSuperTypeCallCallInLambdaInCall.kt")
public void testInSuperTypeCallCallInLambdaInCall() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/outOfBlock/InSuperTypeCallCallInLambdaInCall.kt");
doTest(fileName);
}
@TestMetadata("InSuperTypeCallInLambaInBody.kt")
public void testInSuperTypeCallInLambaInBody() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/outOfBlock/InSuperTypeCallInLambaInBody.kt");
doTest(fileName);
}
@TestMetadata("InSuperTypeCallInLambdaParameters.kt")
public void testInSuperTypeCallInLambdaParameters() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/outOfBlock/InSuperTypeCallInLambdaParameters.kt");
doTest(fileName);
}
@TestMetadata("InitBlock.kt")
public void testInitBlock() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/outOfBlock/InitBlock.kt");