diff --git a/ChangeLog.md b/ChangeLog.md index 3ab3ac0ebab..6075806e10c 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -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 diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtElement.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtElement.kt index 9fb0081e939..0032167e9d7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtElement.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtElement.kt @@ -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() } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtSuperTypeList.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtSuperTypeList.java index c3ff7c2cec0..330656a0721 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtSuperTypeList.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtSuperTypeList.java @@ -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> { + private final AtomicLong modificationStamp = new AtomicLong(); + public KtSuperTypeList(@NotNull ASTNode node) { super(node); } @@ -66,4 +69,15 @@ public class KtSuperTypeList extends KtElementImplStub 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(); + } } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/KotlinCodeBlockModificationListener.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/KotlinCodeBlockModificationListener.kt index 20acf60ff97..43063136b49 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/KotlinCodeBlockModificationListener.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/KotlinCodeBlockModificationListener.kt @@ -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 diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/ResolveElementCache.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/ResolveElementCache.kt index 71044f046e3..3e456d29ff1 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/ResolveElementCache.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/ResolveElementCache.kt @@ -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 + } } } diff --git a/idea/testData/codeInsight/outOfBlock/InSuperTypeCallCallInLambdaInCall.kt b/idea/testData/codeInsight/outOfBlock/InSuperTypeCallCallInLambdaInCall.kt new file mode 100644 index 00000000000..4f7ddd1068b --- /dev/null +++ b/idea/testData/codeInsight/outOfBlock/InSuperTypeCallCallInLambdaInCall.kt @@ -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 foo(l: () -> T) = l() + +class B: A(foo { "1" }) + diff --git a/idea/testData/codeInsight/outOfBlock/InSuperTypeCallInLambaInBody.kt b/idea/testData/codeInsight/outOfBlock/InSuperTypeCallInLambaInBody.kt new file mode 100644 index 00000000000..695e6ed7ec8 --- /dev/null +++ b/idea/testData/codeInsight/outOfBlock/InSuperTypeCallInLambaInBody.kt @@ -0,0 +1,5 @@ +// FALSE + +open class A(a: () -> Unit) + +class B: A({ "1" }) \ No newline at end of file diff --git a/idea/testData/codeInsight/outOfBlock/InSuperTypeCallInLambdaParameters.kt b/idea/testData/codeInsight/outOfBlock/InSuperTypeCallInLambdaParameters.kt new file mode 100644 index 00000000000..b179bc4e3fd --- /dev/null +++ b/idea/testData/codeInsight/outOfBlock/InSuperTypeCallInLambdaParameters.kt @@ -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 -> "1" }) + +// SKIP_ANALYZE_CHECK \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/OutOfBlockModificationTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/OutOfBlockModificationTestGenerated.java index 5895a127844..a743efe7ffe 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/OutOfBlockModificationTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/OutOfBlockModificationTestGenerated.java @@ -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");