Introduce "add default constructor for expect class" fix

Related to KT-24597
This commit is contained in:
Mikhail Glukhikh
2018-08-06 13:22:59 +03:00
parent 3dd99ce4cb
commit 79abf90916
8 changed files with 119 additions and 0 deletions
@@ -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<KtClass>(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<KtClass>? {
val argumentList = diagnostic.psiElement as? KtValueArgumentList ?: return null
if (argumentList.arguments.isNotEmpty()) return null
val derivedClass = argumentList.getStrictParentOfType<KtClassOrObject>() ?: return null
val context = derivedClass.analyze()
val baseTypeCallEntry = derivedClass.superTypeListEntries.filterIsInstance<KtSuperTypeCallEntry>().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)
}
}
}
@@ -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)
@@ -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<caret>()
@@ -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()
@@ -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<caret>()
actual interface A
@@ -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<caret>()
@@ -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)
@@ -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)