From 79abf90916144b2b44eec058af9ffa74e407b770 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Mon, 6 Aug 2018 13:22:59 +0300 Subject: [PATCH] Introduce "add default constructor for expect class" fix Related to KT-24597 --- .../idea/quickfix/AddDefaultConstructorFix.kt | 44 +++++++++++++++++++ .../kotlin/idea/quickfix/QuickFixRegistrar.kt | 1 + .../quickfix/addDefaultConstructor/expect.kt | 7 +++ .../addDefaultConstructor/expect.kt.after | 7 +++ .../addDefaultConstructor/expectInterface.kt | 11 +++++ .../addDefaultConstructor/interface.kt | 8 ++++ .../QuickFixMultiFileTestGenerated.java | 13 ++++++ .../idea/quickfix/QuickFixTestGenerated.java | 28 ++++++++++++ 8 files changed, 119 insertions(+) create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/AddDefaultConstructorFix.kt create mode 100644 idea/testData/quickfix/addDefaultConstructor/expect.kt create mode 100644 idea/testData/quickfix/addDefaultConstructor/expect.kt.after create mode 100644 idea/testData/quickfix/addDefaultConstructor/expectInterface.kt create mode 100644 idea/testData/quickfix/addDefaultConstructor/interface.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddDefaultConstructorFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddDefaultConstructorFix.kt new file mode 100644 index 00000000000..fa4154cdafd --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddDefaultConstructorFix.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.quickfix + +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils + +class AddDefaultConstructorFix(expectClass: KtClass) : KotlinQuickFixAction(expectClass) { + + override fun getText() = "Add default constructor to expect class" + + override fun getFamilyName() = text + + override fun invoke(project: Project, editor: Editor?, file: KtFile) { + element?.createPrimaryConstructorIfAbsent() + } + + companion object : KotlinSingleIntentionActionFactory() { + override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction? { + val argumentList = diagnostic.psiElement as? KtValueArgumentList ?: return null + if (argumentList.arguments.isNotEmpty()) return null + val derivedClass = argumentList.getStrictParentOfType() ?: return null + val context = derivedClass.analyze() + val baseTypeCallEntry = derivedClass.superTypeListEntries.filterIsInstance().firstOrNull() ?: return null + val baseType = context[BindingContext.TYPE, baseTypeCallEntry.typeReference] ?: return null + val baseClassDescriptor = baseType.constructor.declarationDescriptor as? ClassDescriptor ?: return null + if (!baseClassDescriptor.isExpect) return null + if (baseClassDescriptor.kind != ClassKind.CLASS) return null + val baseClass = DescriptorToSourceUtils.descriptorToDeclaration(baseClassDescriptor) as? KtClass ?: return null + return AddDefaultConstructorFix(baseClass) + } + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index 2debddd0a68..93cbe7ce923 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -541,6 +541,7 @@ class QuickFixRegistrar : QuickFixContributor { WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET.registerFactory(MoveReceiverAnnotationFix, AddAnnotationTargetFix) NO_CONSTRUCTOR.registerFactory(RemoveNoConstructorFix) + NO_CONSTRUCTOR.registerFactory(AddDefaultConstructorFix) ANNOTATION_USED_AS_ANNOTATION_ARGUMENT.registerFactory(RemoveAtFromAnnotationArgument) diff --git a/idea/testData/quickfix/addDefaultConstructor/expect.kt b/idea/testData/quickfix/addDefaultConstructor/expect.kt new file mode 100644 index 00000000000..eb8eec14a56 --- /dev/null +++ b/idea/testData/quickfix/addDefaultConstructor/expect.kt @@ -0,0 +1,7 @@ +// "Add default constructor to expect class" "true" +// ENABLE_MULTIPLATFORM +// ERROR: Expected class 'A' has no actual declaration in module light_idea_test_case for JVM + +expect open class A + +open class C : A() diff --git a/idea/testData/quickfix/addDefaultConstructor/expect.kt.after b/idea/testData/quickfix/addDefaultConstructor/expect.kt.after new file mode 100644 index 00000000000..f63194d078b --- /dev/null +++ b/idea/testData/quickfix/addDefaultConstructor/expect.kt.after @@ -0,0 +1,7 @@ +// "Add default constructor to expect class" "true" +// ENABLE_MULTIPLATFORM +// ERROR: Expected class 'A' has no actual declaration in module light_idea_test_case for JVM + +expect open class A() + +open class C : A() diff --git a/idea/testData/quickfix/addDefaultConstructor/expectInterface.kt b/idea/testData/quickfix/addDefaultConstructor/expectInterface.kt new file mode 100644 index 00000000000..b03c75547b2 --- /dev/null +++ b/idea/testData/quickfix/addDefaultConstructor/expectInterface.kt @@ -0,0 +1,11 @@ +// "Add default constructor to expect class" "false" +// ENABLE_MULTIPLATFORM +// ACTION: Create subclass +// ACTION: Remove constructor call +// ERROR: This class does not have a constructor + +expect interface A + +open class C : A() + +actual interface A \ No newline at end of file diff --git a/idea/testData/quickfix/addDefaultConstructor/interface.kt b/idea/testData/quickfix/addDefaultConstructor/interface.kt new file mode 100644 index 00000000000..c44bf374277 --- /dev/null +++ b/idea/testData/quickfix/addDefaultConstructor/interface.kt @@ -0,0 +1,8 @@ +// "Add default constructor to expect class" "false" +// ACTION: Create subclass +// ACTION: Remove constructor call +// ERROR: This class does not have a constructor + +interface A + +open class C : A() \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java index 5c32647d552..4eb40ef214d 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -86,6 +86,19 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes } } + @TestMetadata("idea/testData/quickfix/addDefaultConstructor") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class AddDefaultConstructor extends AbstractQuickFixMultiFileTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTestWithExtraFile, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInAddDefaultConstructor() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/addDefaultConstructor"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true); + } + } + @TestMetadata("idea/testData/quickfix/addExclExclCall") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index cff47e7942d..cfbf66e619c 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -461,6 +461,34 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { } } + @TestMetadata("idea/testData/quickfix/addDefaultConstructor") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class AddDefaultConstructor extends AbstractQuickFixTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInAddDefaultConstructor() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/addDefaultConstructor"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("expect.kt") + public void testExpect() throws Exception { + runTest("idea/testData/quickfix/addDefaultConstructor/expect.kt"); + } + + @TestMetadata("expectInterface.kt") + public void testExpectInterface() throws Exception { + runTest("idea/testData/quickfix/addDefaultConstructor/expectInterface.kt"); + } + + @TestMetadata("interface.kt") + public void testInterface() throws Exception { + runTest("idea/testData/quickfix/addDefaultConstructor/interface.kt"); + } + } + @TestMetadata("idea/testData/quickfix/addExclExclCall") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)