From fb94cb141fd38a40015295f47c6020ef8503eb47 Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Tue, 18 Jun 2019 23:33:09 +0900 Subject: [PATCH] Add "Add constructor parameter" quick fix for NO_VALUE_FOR_PARAMETER #KT-27353 Fixed --- ...onstructorParameterFromSuperTypeCallFix.kt | 58 +++++++++++++++++++ .../kotlin/idea/quickfix/QuickFixRegistrar.kt | 2 + .../backticks.kt | 5 ++ .../backticks.kt.after | 5 ++ .../basic.kt | 4 ++ .../basic.kt.after | 4 ++ .../basic2.kt | 4 ++ .../basic2.kt.after | 4 ++ .../basic3.kt | 4 ++ .../basic3.kt.after | 4 ++ .../basic4.kt | 3 + .../basic4.kt.after | 3 + .../fewerArguments.kt | 9 +++ .../hasDifferentNameParameter.kt | 4 ++ .../hasDifferentNameParameter.kt.after | 4 ++ .../hasSameNameParameter.kt | 8 +++ .../QuickFixMultiFileTestGenerated.java | 13 +++++ .../idea/quickfix/QuickFixTestGenerated.java | 53 +++++++++++++++++ 18 files changed, 191 insertions(+) create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/AddConstructorParameterFromSuperTypeCallFix.kt create mode 100644 idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/backticks.kt create mode 100644 idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/backticks.kt.after create mode 100644 idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic.kt create mode 100644 idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic.kt.after create mode 100644 idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic2.kt create mode 100644 idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic2.kt.after create mode 100644 idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic3.kt create mode 100644 idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic3.kt.after create mode 100644 idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic4.kt create mode 100644 idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic4.kt.after create mode 100644 idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/fewerArguments.kt create mode 100644 idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/hasDifferentNameParameter.kt create mode 100644 idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/hasDifferentNameParameter.kt.after create mode 100644 idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/hasSameNameParameter.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddConstructorParameterFromSuperTypeCallFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddConstructorParameterFromSuperTypeCallFix.kt new file mode 100644 index 00000000000..b23a6bc55f6 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddConstructorParameterFromSuperTypeCallFix.kt @@ -0,0 +1,58 @@ +/* + * 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.diagnostics.DiagnosticFactory +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny +import org.jetbrains.kotlin.idea.core.ShortenReferences +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.containingClass +import org.jetbrains.kotlin.psi.psiUtil.endOffset +import org.jetbrains.kotlin.renderer.render +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode + +class AddConstructorParameterFromSuperTypeCallFix( + constructor: KtValueArgumentList, + private val parameterName: String, + private val parameterType: FqName +) : KotlinQuickFixAction(constructor) { + override fun getText() = "Add constructor parameter '$parameterName'" + + override fun getFamilyName() = text + + override fun invoke(project: Project, editor: Editor?, file: KtFile) { + val superTypeCallArgList = element ?: return + val constructorParamList = superTypeCallArgList.containingClass()?.createPrimaryConstructorIfAbsent()?.valueParameterList ?: return + val psiFactory = KtPsiFactory(superTypeCallArgList) + val constructorParam = constructorParamList.addParameter(psiFactory.createParameter("$parameterName: ${parameterType.render()}")) + val superTypeCallArg = superTypeCallArgList.addArgument(psiFactory.createArgument(parameterName)) + ShortenReferences.DEFAULT.process(constructorParam) + editor?.caretModel?.moveToOffset(superTypeCallArg.endOffset) + } + + companion object : KotlinSingleIntentionActionFactory() { + override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction? { + val superTypeCallArgList = diagnostic.psiElement as? KtValueArgumentList ?: return null + val superTypeCall = superTypeCallArgList.parent as? KtSuperTypeCallEntry ?: return null + val containingClass = superTypeCall.containingClass() ?: return null + + val parameter = DiagnosticFactory.cast(diagnostic, Errors.NO_VALUE_FOR_PARAMETER).a + if (parameter.index != superTypeCallArgList.arguments.size) return null + val parameterName = parameter.name.render() + val constructor = containingClass.resolveToDescriptorIfAny(BodyResolveMode.PARTIAL)?.constructors?.firstOrNull() ?: return null + if (constructor.valueParameters.any { it.name.asString() == parameterName }) return null + val parameterType = parameter.type.constructor.declarationDescriptor?.fqNameOrNull() ?: return null + + return AddConstructorParameterFromSuperTypeCallFix(superTypeCallArgList, parameterName, parameterType) + } + } +} \ 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 1b64e2622b6..8d76240ee19 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -609,5 +609,7 @@ class QuickFixRegistrar : QuickFixContributor { RESTRICTED_RETENTION_FOR_EXPRESSION_ANNOTATION.registerFactory(RestrictedRetentionForExpressionAnnotationFactory) RESTRICTED_RETENTION_FOR_EXPRESSION_ANNOTATION_WARNING.registerFactory(RestrictedRetentionForExpressionAnnotationFactory) + + NO_VALUE_FOR_PARAMETER.registerFactory(AddConstructorParameterFromSuperTypeCallFix) } } diff --git a/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/backticks.kt b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/backticks.kt new file mode 100644 index 00000000000..4f8152fff97 --- /dev/null +++ b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/backticks.kt @@ -0,0 +1,5 @@ +// "Add constructor parameter '`first param`'" "true" +// DISABLE-ERRORS +class `My class` +open class A(`first param`: `My class`) +class B : A() \ No newline at end of file diff --git a/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/backticks.kt.after b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/backticks.kt.after new file mode 100644 index 00000000000..079b5731fc7 --- /dev/null +++ b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/backticks.kt.after @@ -0,0 +1,5 @@ +// "Add constructor parameter '`first param`'" "true" +// DISABLE-ERRORS +class `My class` +open class A(`first param`: `My class`) +class B(`first param`: `My class`) : A(`first param`) \ No newline at end of file diff --git a/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic.kt b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic.kt new file mode 100644 index 00000000000..2a403025f55 --- /dev/null +++ b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic.kt @@ -0,0 +1,4 @@ +// "Add constructor parameter 'x'" "true" +// DISABLE-ERRORS +abstract class A(val x: Int, val y: String, val z: Long) +class B : A() \ No newline at end of file diff --git a/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic.kt.after b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic.kt.after new file mode 100644 index 00000000000..0622a548ff4 --- /dev/null +++ b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic.kt.after @@ -0,0 +1,4 @@ +// "Add constructor parameter 'x'" "true" +// DISABLE-ERRORS +abstract class A(val x: Int, val y: String, val z: Long) +class B(x: Int) : A(x) \ No newline at end of file diff --git a/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic2.kt b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic2.kt new file mode 100644 index 00000000000..6d4be74d7aa --- /dev/null +++ b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic2.kt @@ -0,0 +1,4 @@ +// "Add constructor parameter 'x'" "true" +// DISABLE-ERRORS +abstract class A(val x: Int, val y: String, val z: Long) +class B() : A() \ No newline at end of file diff --git a/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic2.kt.after b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic2.kt.after new file mode 100644 index 00000000000..0622a548ff4 --- /dev/null +++ b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic2.kt.after @@ -0,0 +1,4 @@ +// "Add constructor parameter 'x'" "true" +// DISABLE-ERRORS +abstract class A(val x: Int, val y: String, val z: Long) +class B(x: Int) : A(x) \ No newline at end of file diff --git a/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic3.kt b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic3.kt new file mode 100644 index 00000000000..f43bc273123 --- /dev/null +++ b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic3.kt @@ -0,0 +1,4 @@ +// "Add constructor parameter 'y'" "true" +// DISABLE-ERRORS +abstract class A(val x: Int, val y: String, val z: Long) +class B(x: Int) : A(x) \ No newline at end of file diff --git a/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic3.kt.after b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic3.kt.after new file mode 100644 index 00000000000..90b503af26d --- /dev/null +++ b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic3.kt.after @@ -0,0 +1,4 @@ +// "Add constructor parameter 'y'" "true" +// DISABLE-ERRORS +abstract class A(val x: Int, val y: String, val z: Long) +class B(x: Int, y: String) : A(x, y) \ No newline at end of file diff --git a/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic4.kt b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic4.kt new file mode 100644 index 00000000000..ed7cb4b3f50 --- /dev/null +++ b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic4.kt @@ -0,0 +1,3 @@ +// "Add constructor parameter 'z'" "true" +abstract class A(val x: Int, val y: String, val z: Long) +class B(x: Int, y: String) : A(x, y) \ No newline at end of file diff --git a/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic4.kt.after b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic4.kt.after new file mode 100644 index 00000000000..8f681112f8b --- /dev/null +++ b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic4.kt.after @@ -0,0 +1,3 @@ +// "Add constructor parameter 'z'" "true" +abstract class A(val x: Int, val y: String, val z: Long) +class B(x: Int, y: String, z: Long) : A(x, y, z) \ No newline at end of file diff --git a/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/fewerArguments.kt b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/fewerArguments.kt new file mode 100644 index 00000000000..142f8c1b86a --- /dev/null +++ b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/fewerArguments.kt @@ -0,0 +1,9 @@ +// "Add constructor parameter 'z'" "false" +// DISABLE-ERRORS +// ACTION: Add 'x =' to argument +// ACTION: Add constructor parameter 'y' +// ACTION: Create secondary constructor +// ACTION: Remove parameter 'y' +// ACTION: Remove parameter 'z' +abstract class A(val x: Int, val y: String, val z: Long) +class B(x: Int) : A(x) \ No newline at end of file diff --git a/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/hasDifferentNameParameter.kt b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/hasDifferentNameParameter.kt new file mode 100644 index 00000000000..f6afc6c518a --- /dev/null +++ b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/hasDifferentNameParameter.kt @@ -0,0 +1,4 @@ +// "Add constructor parameter 'y'" "true" +// DISABLE-ERRORS +abstract class A(val x: Int, val y: String, val z: Long) +class B(a: Int, b: String, c: Long) : A(a) \ No newline at end of file diff --git a/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/hasDifferentNameParameter.kt.after b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/hasDifferentNameParameter.kt.after new file mode 100644 index 00000000000..f4d685325b6 --- /dev/null +++ b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/hasDifferentNameParameter.kt.after @@ -0,0 +1,4 @@ +// "Add constructor parameter 'y'" "true" +// DISABLE-ERRORS +abstract class A(val x: Int, val y: String, val z: Long) +class B(a: Int, b: String, c: Long, y: String) : A(a, y) \ No newline at end of file diff --git a/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/hasSameNameParameter.kt b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/hasSameNameParameter.kt new file mode 100644 index 00000000000..439b22b9cc5 --- /dev/null +++ b/idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/hasSameNameParameter.kt @@ -0,0 +1,8 @@ +// "Add constructor parameter 'y'" "false" +// DISABLE-ERRORS +// ACTION: Add 'x =' to argument +// ACTION: Create secondary constructor +// ACTION: Remove parameter 'y' +// ACTION: Remove parameter 'z' +abstract class A(val x: Int, val y: String, val z: Long) +class B(x: Int, y: String, z: Long) : A(x) \ 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 4d6717f24b8..64936309bcd 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -60,6 +60,19 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes } } + @TestMetadata("idea/testData/quickfix/addConstructorParameterFromSuperTypeCall") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class AddConstructorParameterFromSuperTypeCall extends AbstractQuickFixMultiFileTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTestWithExtraFile, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInAddConstructorParameterFromSuperTypeCall() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/addConstructorParameterFromSuperTypeCall"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true); + } + } + @TestMetadata("idea/testData/quickfix/addCrossinline") @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 63665778c12..e4409223209 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -360,6 +360,59 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { } } + @TestMetadata("idea/testData/quickfix/addConstructorParameterFromSuperTypeCall") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class AddConstructorParameterFromSuperTypeCall extends AbstractQuickFixTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInAddConstructorParameterFromSuperTypeCall() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/addConstructorParameterFromSuperTypeCall"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("backticks.kt") + public void testBackticks() throws Exception { + runTest("idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/backticks.kt"); + } + + @TestMetadata("basic.kt") + public void testBasic() throws Exception { + runTest("idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic.kt"); + } + + @TestMetadata("basic2.kt") + public void testBasic2() throws Exception { + runTest("idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic2.kt"); + } + + @TestMetadata("basic3.kt") + public void testBasic3() throws Exception { + runTest("idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic3.kt"); + } + + @TestMetadata("basic4.kt") + public void testBasic4() throws Exception { + runTest("idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/basic4.kt"); + } + + @TestMetadata("fewerArguments.kt") + public void testFewerArguments() throws Exception { + runTest("idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/fewerArguments.kt"); + } + + @TestMetadata("hasDifferentNameParameter.kt") + public void testHasDifferentNameParameter() throws Exception { + runTest("idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/hasDifferentNameParameter.kt"); + } + + @TestMetadata("hasSameNameParameter.kt") + public void testHasSameNameParameter() throws Exception { + runTest("idea/testData/quickfix/addConstructorParameterFromSuperTypeCall/hasSameNameParameter.kt"); + } + } + @TestMetadata("idea/testData/quickfix/addCrossinline") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)