From 4b2b76ea0d78810700721844fda3a27ef96119da Mon Sep 17 00:00:00 2001 From: Roman Golyshev Date: Wed, 27 May 2020 14:16:18 +0300 Subject: [PATCH] KT-18538 Unwrap fake override in `ShortenReferences` - Fake override prevents reference shortener from shortening of static methods declared in the class bases when they are located not in direct parent of the class (for example, in grand- or grand-grand-parent) - The completion uses descriptor with unwrapped fake override when it performs the insertion. It leads to inserting the name of the base which actually contains the static method instead of the direct parent class. Now, when reference shortener compares unwrapped descriptors, this problem should be fixed during insertion handling Original commit: 2c12d26d281f43a6eb0b67c7154c42bc7f0120e7 GitOrigin-RevId: f990b3a2cded28a980436e37bf64d750698161c6 --- .../idea/completion/BasicLookupElementFactory.kt | 8 +++----- .../test/handlers/CompletionMultifileHandlerTest.kt | 11 ++++++++++- .../multifile/StaticMethodFromGrandParent-1.java | 2 ++ .../multifile/StaticMethodFromGrandParent-1.kt | 5 +++++ .../multifile/StaticMethodFromGrandParent-2.java | 3 +++ .../multifile/StaticMethodFromGrandParent.kt.after | 5 +++++ .../jetbrains/kotlin/idea/core/ShortenReferences.kt | 5 ++++- .../org/jetbrains/kotlin/idea/core/descriptorUtils.kt | 4 ++++ .../replaceWith/ReplaceWithAnnotationAnalyzer.kt | 6 ++---- .../kotlin/shortenRefs/ShortenRefsTestGenerated.java | 10 ++++++++++ ...redundantGrandParentClassQualifier.dependency.java | 7 +++++++ .../java/redundantGrandParentClassQualifier.kt | 5 +++++ .../java/redundantGrandParentClassQualifier.kt.after | 5 +++++ ...GrandParentClassQualifierAmbiguous.dependency.java | 9 +++++++++ .../redundantGrandParentClassQualifierAmbiguous.kt | 5 +++++ ...dundantGrandParentClassQualifierAmbiguous.kt.after | 5 +++++ 16 files changed, 84 insertions(+), 11 deletions(-) create mode 100644 plugins/kotlin/completion/tests/testData/handlers/multifile/StaticMethodFromGrandParent-1.java create mode 100644 plugins/kotlin/completion/tests/testData/handlers/multifile/StaticMethodFromGrandParent-1.kt create mode 100644 plugins/kotlin/completion/tests/testData/handlers/multifile/StaticMethodFromGrandParent-2.java create mode 100644 plugins/kotlin/completion/tests/testData/handlers/multifile/StaticMethodFromGrandParent.kt.after create mode 100644 plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifier.dependency.java create mode 100644 plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifier.kt create mode 100644 plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifier.kt.after create mode 100644 plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifierAmbiguous.dependency.java create mode 100644 plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifierAmbiguous.kt create mode 100644 plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifierAmbiguous.kt.after diff --git a/plugins/kotlin/completion/src/org/jetbrains/kotlin/idea/completion/BasicLookupElementFactory.kt b/plugins/kotlin/completion/src/org/jetbrains/kotlin/idea/completion/BasicLookupElementFactory.kt index 5a1d7d67b0bf..f73c75257cb8 100644 --- a/plugins/kotlin/completion/src/org/jetbrains/kotlin/idea/completion/BasicLookupElementFactory.kt +++ b/plugins/kotlin/completion/src/org/jetbrains/kotlin/idea/completion/BasicLookupElementFactory.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.idea.completion.handlers.KotlinClassifierInsertHandl import org.jetbrains.kotlin.idea.completion.handlers.KotlinFunctionInsertHandler import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject import org.jetbrains.kotlin.idea.core.completion.PackageLookupObject +import org.jetbrains.kotlin.idea.core.unwrapIfFakeOverride import org.jetbrains.kotlin.idea.highlighter.dsl.DslHighlighterExtension import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor @@ -51,10 +52,7 @@ class BasicLookupElementFactory( includeClassTypeArguments: Boolean = true, parametersAndTypeGrayed: Boolean = false ): LookupElement { - val _descriptor = if (descriptor is CallableMemberDescriptor) - DescriptorUtils.unwrapFakeOverride(descriptor) - else - descriptor + val _descriptor = descriptor.unwrapIfFakeOverride() return createLookupElementUnwrappedDescriptor(_descriptor, qualifyNestedClasses, includeClassTypeArguments, parametersAndTypeGrayed) } @@ -343,4 +341,4 @@ class BasicLookupElementFactory( presentation.icon = DefaultLookupItemRenderer.getRawIcon(this@withIconFromLookupObject, presentation.isReal) } } -} \ No newline at end of file +} diff --git a/plugins/kotlin/completion/tests/test/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionMultifileHandlerTest.kt b/plugins/kotlin/completion/tests/test/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionMultifileHandlerTest.kt index db2d62d0ba02..4592e2898fd6 100644 --- a/plugins/kotlin/completion/tests/test/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionMultifileHandlerTest.kt +++ b/plugins/kotlin/completion/tests/test/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionMultifileHandlerTest.kt @@ -30,6 +30,10 @@ class CompletionMultiFileHandlerTest : KotlinCompletionTestCase() { doTest() } + fun testStaticMethodFromGrandParent() { + doTest('\n', "StaticMethodFromGrandParent-1.java", "StaticMethodFromGrandParent-2.java") + } + fun testTopLevelFunctionImport() { doTest() } @@ -113,8 +117,13 @@ class CompletionMultiFileHandlerTest : KotlinCompletionTestCase() { fun doTest(completionChar: Char = '\n', vararg extraFileNames: String, tailText: String? = null) { val fileName = getTestName(false) + val defaultFiles = listOf("$fileName-1.kt", "$fileName-2.kt") + val filteredFiles = defaultFiles.filter { File(testDataPath, it).exists() } + + require(filteredFiles.isNotEmpty()) { "At least one of $defaultFiles should exist!" } + configureByFiles(null, *extraFileNames) - configureByFiles(null, "$fileName-1.kt", "$fileName-2.kt") + configureByFiles(null, *filteredFiles.toTypedArray()) complete(2) if (myItems != null) { val item = if (tailText == null) diff --git a/plugins/kotlin/completion/tests/testData/handlers/multifile/StaticMethodFromGrandParent-1.java b/plugins/kotlin/completion/tests/testData/handlers/multifile/StaticMethodFromGrandParent-1.java new file mode 100644 index 000000000000..5e99ae99df1e --- /dev/null +++ b/plugins/kotlin/completion/tests/testData/handlers/multifile/StaticMethodFromGrandParent-1.java @@ -0,0 +1,2 @@ +public class Base extends GrandBase { +} \ No newline at end of file diff --git a/plugins/kotlin/completion/tests/testData/handlers/multifile/StaticMethodFromGrandParent-1.kt b/plugins/kotlin/completion/tests/testData/handlers/multifile/StaticMethodFromGrandParent-1.kt new file mode 100644 index 000000000000..983f43016c5d --- /dev/null +++ b/plugins/kotlin/completion/tests/testData/handlers/multifile/StaticMethodFromGrandParent-1.kt @@ -0,0 +1,5 @@ +class Inheritor : Base() { + init { + fooBa + } +} \ No newline at end of file diff --git a/plugins/kotlin/completion/tests/testData/handlers/multifile/StaticMethodFromGrandParent-2.java b/plugins/kotlin/completion/tests/testData/handlers/multifile/StaticMethodFromGrandParent-2.java new file mode 100644 index 000000000000..6aaa195d98a5 --- /dev/null +++ b/plugins/kotlin/completion/tests/testData/handlers/multifile/StaticMethodFromGrandParent-2.java @@ -0,0 +1,3 @@ +public class GrandBase { + public static void fooBar() {} +} \ No newline at end of file diff --git a/plugins/kotlin/completion/tests/testData/handlers/multifile/StaticMethodFromGrandParent.kt.after b/plugins/kotlin/completion/tests/testData/handlers/multifile/StaticMethodFromGrandParent.kt.after new file mode 100644 index 000000000000..5349a1ae558c --- /dev/null +++ b/plugins/kotlin/completion/tests/testData/handlers/multifile/StaticMethodFromGrandParent.kt.after @@ -0,0 +1,5 @@ +class Inheritor : Base() { + init { + fooBar() + } +} \ No newline at end of file diff --git a/plugins/kotlin/core/src/org/jetbrains/kotlin/idea/core/ShortenReferences.kt b/plugins/kotlin/core/src/org/jetbrains/kotlin/idea/core/ShortenReferences.kt index 32879bff09d5..b6a58aa72639 100644 --- a/plugins/kotlin/core/src/org/jetbrains/kotlin/idea/core/ShortenReferences.kt +++ b/plugins/kotlin/core/src/org/jetbrains/kotlin/idea/core/ShortenReferences.kt @@ -552,7 +552,10 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT // targetMatch == false, but shorten still can be preformed // TODO: Add possibility to check if descriptor from completion can't be resolved after shorten and not preform shorten than val resolvedCallsMatch = if (resolvedCall != null && resolvedCallWhenShort != null) { - resolvedCall.resultingDescriptor.original == resolvedCallWhenShort.resultingDescriptor.original + val originalCallDescriptor = resolvedCall.resultingDescriptor.original.unwrapIfFakeOverride() + val shortenedCallDescriptor = resolvedCallWhenShort.resultingDescriptor.original.unwrapIfFakeOverride() + + originalCallDescriptor == shortenedCallDescriptor } else { val resolvedCalls = selector.getCall(bindingContext)?.resolveCandidates(bindingContext, resolutionFacade) ?: emptyList() val callWhenShort = selectorAfterShortening.getCall(newContext) diff --git a/plugins/kotlin/core/src/org/jetbrains/kotlin/idea/core/descriptorUtils.kt b/plugins/kotlin/core/src/org/jetbrains/kotlin/idea/core/descriptorUtils.kt index 1d1019556b87..695deabe6a04 100644 --- a/plugins/kotlin/core/src/org/jetbrains/kotlin/idea/core/descriptorUtils.kt +++ b/plugins/kotlin/core/src/org/jetbrains/kotlin/idea/core/descriptorUtils.kt @@ -141,3 +141,7 @@ fun D.getDeepestSuperDeclarations(withThis: Boole return overriddenDeclarations.filterNot(DescriptorUtils::isOverride) } + +fun T.unwrapIfFakeOverride(): T { + return if (this is CallableMemberDescriptor) DescriptorUtils.unwrapFakeOverride(this) else this +} \ No newline at end of file diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ReplaceWithAnnotationAnalyzer.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ReplaceWithAnnotationAnalyzer.kt index 9954dff28ffa..68f8427ddf72 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ReplaceWithAnnotationAnalyzer.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ReplaceWithAnnotationAnalyzer.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.idea.analysis.analyzeInContext import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference import org.jetbrains.kotlin.idea.codeInliner.CodeToInline import org.jetbrains.kotlin.idea.codeInliner.CodeToInlineBuilder +import org.jetbrains.kotlin.idea.core.unwrapIfFakeOverride import org.jetbrains.kotlin.idea.project.findAnalyzerServices import org.jetbrains.kotlin.idea.references.KtSimpleNameReference import org.jetbrains.kotlin.idea.references.mainReference @@ -43,10 +44,7 @@ object ReplaceWithAnnotationAnalyzer { resolutionFacade: ResolutionFacade, reformat: Boolean ): CodeToInline? { - val originalDescriptor = when (symbolDescriptor) { - is CallableMemberDescriptor -> DescriptorUtils.unwrapFakeOverride(symbolDescriptor) - else -> symbolDescriptor - }.original + val originalDescriptor = symbolDescriptor.unwrapIfFakeOverride().original return analyzeOriginal(annotation, originalDescriptor, resolutionFacade, reformat) } diff --git a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/shortenRefs/ShortenRefsTestGenerated.java b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/shortenRefs/ShortenRefsTestGenerated.java index 07748712ea8d..0060bbeafcbd 100644 --- a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/shortenRefs/ShortenRefsTestGenerated.java +++ b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/shortenRefs/ShortenRefsTestGenerated.java @@ -284,6 +284,16 @@ public class ShortenRefsTestGenerated extends AbstractShortenRefsTest { runTest("testData/shortenRefs/java/innerClassOnDemandImport.kt"); } + @TestMetadata("redundantGrandParentClassQualifier.kt") + public void testRedundantGrandParentClassQualifier() throws Exception { + runTest("testData/shortenRefs/java/redundantGrandParentClassQualifier.kt"); + } + + @TestMetadata("redundantGrandParentClassQualifierAmbiguous.kt") + public void testRedundantGrandParentClassQualifierAmbiguous() throws Exception { + runTest("testData/shortenRefs/java/redundantGrandParentClassQualifierAmbiguous.kt"); + } + @TestMetadata("staticClassNoImports.kt") public void testStaticClassNoImports() throws Exception { runTest("testData/shortenRefs/java/staticClassNoImports.kt"); diff --git a/plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifier.dependency.java b/plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifier.dependency.java new file mode 100644 index 000000000000..d49437c89534 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifier.dependency.java @@ -0,0 +1,7 @@ +public class Container { + public static class GrandBase { + public static void foo() {} + } + + public static class Base extends GrandBase {} +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifier.kt b/plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifier.kt new file mode 100644 index 000000000000..371a9fa58e0d --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifier.kt @@ -0,0 +1,5 @@ +class C : Container.Base() { + init { + Container.GrandBase.foo() + } +} diff --git a/plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifier.kt.after b/plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifier.kt.after new file mode 100644 index 000000000000..fc48439e4bc1 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifier.kt.after @@ -0,0 +1,5 @@ +class C : Container.Base() { + init { + foo() + } +} diff --git a/plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifierAmbiguous.dependency.java b/plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifierAmbiguous.dependency.java new file mode 100644 index 000000000000..c8f642a7db0a --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifierAmbiguous.dependency.java @@ -0,0 +1,9 @@ +public class Container { + public static class GrandBase { + public static void foo() {} + } + + public static class Base extends GrandBase { + public static void foo() {} + } +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifierAmbiguous.kt b/plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifierAmbiguous.kt new file mode 100644 index 000000000000..371a9fa58e0d --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifierAmbiguous.kt @@ -0,0 +1,5 @@ +class C : Container.Base() { + init { + Container.GrandBase.foo() + } +} diff --git a/plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifierAmbiguous.kt.after b/plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifierAmbiguous.kt.after new file mode 100644 index 000000000000..70fe2d4b77b3 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/shortenRefs/java/redundantGrandParentClassQualifierAmbiguous.kt.after @@ -0,0 +1,5 @@ +class C : Container.Base() { + init { + Container.GrandBase.foo() + } +}