diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt b/compiler/psi/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt index 046e058a60a..973e521e823 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt @@ -472,6 +472,8 @@ class KtPsiFactory @JvmOverloads constructor(private val project: Project, val m return file.importDirectives } + fun createClassKeyword(): PsiElement = createClass("class A").getClassKeyword()!! + fun createPrimaryConstructor(text: String = ""): KtPrimaryConstructor { return createClass(if (text.isNotEmpty()) "class A $text" else "class A()").primaryConstructor!! } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeObjectToClassFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeObjectToClassFix.kt new file mode 100644 index 00000000000..8ba75aa945c --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeObjectToClassFix.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.diagnostics.Diagnostic +import org.jetbrains.kotlin.psi.KtConstructor +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtObjectDeclaration +import org.jetbrains.kotlin.psi.KtPsiFactory +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject + +class ChangeObjectToClassFix(element: KtObjectDeclaration) : KotlinQuickFixAction(element) { + override fun getText(): String = "Change object to class" + + override fun getFamilyName(): String = text + + override fun invoke(project: Project, editor: Editor?, file: KtFile) { + val objectDeclaration = element ?: return + val psiFactory = KtPsiFactory(objectDeclaration) + objectDeclaration.getObjectKeyword()?.replace(psiFactory.createClassKeyword()) + objectDeclaration.replace(psiFactory.createClass(objectDeclaration.text)) + } + + companion object : KotlinSingleIntentionActionFactory() { + override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction? { + val element = diagnostic.psiElement as? KtConstructor<*> ?: return null + val containingObject = element.containingClassOrObject as? KtObjectDeclaration ?: return null + return ChangeObjectToClassFix(containingObject) + } + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index d719d238bfe..028e5786676 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -614,6 +614,8 @@ class QuickFixRegistrar : QuickFixContributor { UNEXPECTED_TRAILING_LAMBDA_ON_A_NEW_LINE.registerFactory(AddSemicolonBeforeLambdaExpressionFix.Factory) + CONSTRUCTOR_IN_OBJECT.registerFactory(ChangeObjectToClassFix) + REDUNDANT_LABEL_WARNING.registerFactory(RemoveRedundantLabelFix) } } diff --git a/idea/testData/quickfix/changeObjectToClass/primaryConstructor.kt b/idea/testData/quickfix/changeObjectToClass/primaryConstructor.kt new file mode 100644 index 00000000000..aa9087ef1f0 --- /dev/null +++ b/idea/testData/quickfix/changeObjectToClass/primaryConstructor.kt @@ -0,0 +1,8 @@ +// "Change object to class" "true" +annotation class Ann + +// comment +@Ann +object Foo(val s: String) : Any() { + constructor() : this("") +} \ No newline at end of file diff --git a/idea/testData/quickfix/changeObjectToClass/primaryConstructor.kt.after b/idea/testData/quickfix/changeObjectToClass/primaryConstructor.kt.after new file mode 100644 index 00000000000..7b2ca85acd4 --- /dev/null +++ b/idea/testData/quickfix/changeObjectToClass/primaryConstructor.kt.after @@ -0,0 +1,8 @@ +// "Change object to class" "true" +annotation class Ann + +// comment +@Ann +class Foo(val s: String) : Any() { + constructor() : this("") +} \ No newline at end of file diff --git a/idea/testData/quickfix/changeObjectToClass/secondaryConstructor.kt b/idea/testData/quickfix/changeObjectToClass/secondaryConstructor.kt new file mode 100644 index 00000000000..76efb4245ff --- /dev/null +++ b/idea/testData/quickfix/changeObjectToClass/secondaryConstructor.kt @@ -0,0 +1,8 @@ +// "Change object to class" "true" +annotation class Ann + +// comment +@Ann +object Foo(val s: String) : Any() { + constructor() : this("") +} \ No newline at end of file diff --git a/idea/testData/quickfix/changeObjectToClass/secondaryConstructor.kt.after b/idea/testData/quickfix/changeObjectToClass/secondaryConstructor.kt.after new file mode 100644 index 00000000000..7b2ca85acd4 --- /dev/null +++ b/idea/testData/quickfix/changeObjectToClass/secondaryConstructor.kt.after @@ -0,0 +1,8 @@ +// "Change object to class" "true" +annotation class Ann + +// comment +@Ann +class Foo(val s: String) : Any() { + constructor() : this("") +} \ 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 ebbcc3e1d3b..0cd4800805b 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -1197,6 +1197,19 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes } } + @TestMetadata("idea/testData/quickfix/changeObjectToClass") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ChangeObjectToClass extends AbstractQuickFixMultiFileTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTestWithExtraFile, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInChangeObjectToClass() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/changeObjectToClass"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true); + } + } + @TestMetadata("idea/testData/quickfix/changeSignature") @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 a220cb7cf8c..2c8b653d424 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -2057,6 +2057,29 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { } } + @TestMetadata("idea/testData/quickfix/changeObjectToClass") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ChangeObjectToClass extends AbstractQuickFixTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInChangeObjectToClass() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/changeObjectToClass"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("primaryConstructor.kt") + public void testPrimaryConstructor() throws Exception { + runTest("idea/testData/quickfix/changeObjectToClass/primaryConstructor.kt"); + } + + @TestMetadata("secondaryConstructor.kt") + public void testSecondaryConstructor() throws Exception { + runTest("idea/testData/quickfix/changeObjectToClass/secondaryConstructor.kt"); + } + } + @TestMetadata("idea/testData/quickfix/changeSignature") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)