From 44e69d8adc79c7df6b2f471b2db9bada8ec60cb1 Mon Sep 17 00:00:00 2001 From: shiraji Date: Tue, 27 Dec 2016 01:35:18 +0900 Subject: [PATCH] Quick fix for applying spread operator where vararg is expected #KT-6824 Fixed --- .../quickfix/ChangeToUseSpreadOperatorFix.kt | 35 ++++++++++++ .../QuickFixFactoryForTypeMismatchError.kt | 27 ++++++--- .../differentTypeParameter.kt | 10 ++++ .../changeToUseSpreadOperator/mapOf.kt | 8 +++ .../changeToUseSpreadOperator/mapOf.kt.after | 8 +++ .../changeToUseSpreadOperator/mapOfBug.kt | 11 ++++ .../multipleParams.kt | 5 ++ .../multipleParams.kt.after | 5 ++ .../changeToUseSpreadOperator/nonArray.kt | 12 ++++ .../changeToUseSpreadOperator/nonVarArg.kt | 10 ++++ .../changeToUseSpreadOperator/normal.kt | 5 ++ .../changeToUseSpreadOperator/normal.kt.after | 5 ++ .../changeToUseSpreadOperator/vararg.kt | 5 ++ .../changeToUseSpreadOperator/vararg.kt.after | 5 ++ .../idea/quickfix/QuickFixTestGenerated.java | 57 +++++++++++++++++++ 15 files changed, 201 insertions(+), 7 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeToUseSpreadOperatorFix.kt create mode 100644 idea/testData/quickfix/changeToUseSpreadOperator/differentTypeParameter.kt create mode 100644 idea/testData/quickfix/changeToUseSpreadOperator/mapOf.kt create mode 100644 idea/testData/quickfix/changeToUseSpreadOperator/mapOf.kt.after create mode 100644 idea/testData/quickfix/changeToUseSpreadOperator/mapOfBug.kt create mode 100644 idea/testData/quickfix/changeToUseSpreadOperator/multipleParams.kt create mode 100644 idea/testData/quickfix/changeToUseSpreadOperator/multipleParams.kt.after create mode 100644 idea/testData/quickfix/changeToUseSpreadOperator/nonArray.kt create mode 100644 idea/testData/quickfix/changeToUseSpreadOperator/nonVarArg.kt create mode 100644 idea/testData/quickfix/changeToUseSpreadOperator/normal.kt create mode 100644 idea/testData/quickfix/changeToUseSpreadOperator/normal.kt.after create mode 100644 idea/testData/quickfix/changeToUseSpreadOperator/vararg.kt create mode 100644 idea/testData/quickfix/changeToUseSpreadOperator/vararg.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeToUseSpreadOperatorFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeToUseSpreadOperatorFix.kt new file mode 100644 index 00000000000..96c7df478b7 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeToUseSpreadOperatorFix.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.quickfix + +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtPsiFactory + +class ChangeToUseSpreadOperatorFix(element: KtExpression) : KotlinQuickFixAction(element) { + override fun getFamilyName() = "Change to use spread operator" + + override fun getText() = "Change '${element?.text}' to '*${element?.text}'" + + override fun invoke(project: Project, editor: Editor?, file: KtFile) { + val element = element ?: return + val star = KtPsiFactory(project).createStar() + element.parent.addBefore(star, element) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt index c8da7979c11..519be3dcad1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.idea.quickfix import com.intellij.codeInsight.intention.IntentionAction import com.intellij.openapi.diagnostic.Logger import com.intellij.psi.util.PsiTreeUtil +import com.intellij.util.containers.isNullOrEmpty import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Errors @@ -31,6 +32,7 @@ import org.jetbrains.kotlin.idea.util.getResolutionScope import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.bindingContextUtil.getTargetFunction +import org.jetbrains.kotlin.resolve.calls.callUtil.getParameterForArgument import org.jetbrains.kotlin.resolve.calls.callUtil.getParentResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getValueArgumentForExpression @@ -189,16 +191,27 @@ class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() { val argumentExpression = parentIf ?: diagnosticElement val valueArgument = resolvedCall.call.getValueArgumentForExpression(argumentExpression) if (valueArgument != null) { - val correspondingParameter = QuickFixUtil.getParameterDeclarationForValueArgument(resolvedCall, valueArgument) + val correspondingParameterDescriptor = resolvedCall.getParameterForArgument(valueArgument) + val correspondingParameter = QuickFixUtil.safeGetDeclaration(correspondingParameterDescriptor) as? KtParameter val expressionFromArgument = valueArgument.getArgumentExpression() val valueArgumentType = if (diagnostic.factory === Errors.NULL_FOR_NONNULL_TYPE) expressionType - else if (expressionFromArgument != null) context.getType(expressionFromArgument) else null - if (correspondingParameter != null && valueArgumentType != null) { - val callable = PsiTreeUtil.getParentOfType(correspondingParameter, KtCallableDeclaration::class.java, true) - val scope = callable?.getResolutionScope(context, callable.getResolutionFacade()) - val typeToInsert = valueArgumentType.approximateWithResolvableType(scope, true) - actions.add(ChangeParameterTypeFix(correspondingParameter, typeToInsert)) + else + expressionFromArgument?.let { context.getType(it) } + if (valueArgumentType != null) { + if (correspondingParameter != null) { + val callable = PsiTreeUtil.getParentOfType(correspondingParameter, KtCallableDeclaration::class.java, true) + val scope = callable?.getResolutionScope(context, callable.getResolutionFacade()) + val typeToInsert = valueArgumentType.approximateWithResolvableType(scope, true) + actions.add(ChangeParameterTypeFix(correspondingParameter, typeToInsert)) + } + if (correspondingParameterDescriptor != null + && correspondingParameterDescriptor.varargElementType != null + && KotlinBuiltIns.isArray(valueArgumentType) + && !expressionType.arguments.isNullOrEmpty() + && expressionType.arguments[0].type.constructor == expectedType.constructor) { + actions.add(ChangeToUseSpreadOperatorFix(diagnosticElement)) + } } } } diff --git a/idea/testData/quickfix/changeToUseSpreadOperator/differentTypeParameter.kt b/idea/testData/quickfix/changeToUseSpreadOperator/differentTypeParameter.kt new file mode 100644 index 00000000000..60d9aae0220 --- /dev/null +++ b/idea/testData/quickfix/changeToUseSpreadOperator/differentTypeParameter.kt @@ -0,0 +1,10 @@ +// "Change 'y' to '*y'" "false" +// ACTION: Add 'toString()' call +// ACTION: Change parameter 'x' type of function 'foo' to 'Array' +// ACTION: Convert to block body +// ACTION: Create function 'foo' +// DISABLE-ERRORS + +fun foo(vararg x: String) {} + +fun bar(y: Array) = foo(y) diff --git a/idea/testData/quickfix/changeToUseSpreadOperator/mapOf.kt b/idea/testData/quickfix/changeToUseSpreadOperator/mapOf.kt new file mode 100644 index 00000000000..69f315d1750 --- /dev/null +++ b/idea/testData/quickfix/changeToUseSpreadOperator/mapOf.kt @@ -0,0 +1,8 @@ +// "Change 'pairs' to '*pairs'" "true" +// WITH_RUNTIME + +fun yourMapOf(vararg pairs: Pair) {} + +fun myMapOf(vararg pairs: Pair) { + val myMap = yourMapOf(pairs) +} \ No newline at end of file diff --git a/idea/testData/quickfix/changeToUseSpreadOperator/mapOf.kt.after b/idea/testData/quickfix/changeToUseSpreadOperator/mapOf.kt.after new file mode 100644 index 00000000000..bf4a3ae5261 --- /dev/null +++ b/idea/testData/quickfix/changeToUseSpreadOperator/mapOf.kt.after @@ -0,0 +1,8 @@ +// "Change 'pairs' to '*pairs'" "true" +// WITH_RUNTIME + +fun yourMapOf(vararg pairs: Pair) {} + +fun myMapOf(vararg pairs: Pair) { + val myMap = yourMapOf(*pairs) +} \ No newline at end of file diff --git a/idea/testData/quickfix/changeToUseSpreadOperator/mapOfBug.kt b/idea/testData/quickfix/changeToUseSpreadOperator/mapOfBug.kt new file mode 100644 index 00000000000..af15d6b0b29 --- /dev/null +++ b/idea/testData/quickfix/changeToUseSpreadOperator/mapOfBug.kt @@ -0,0 +1,11 @@ +// "Change 'pairs' to '*pairs'" "false" +// WITH_RUNTIME +// ACTION: Create function 'mapOf' +// ERROR: Type inference failed: fun mapOf(pair: Pair): Map
cannot be applied to
(Array>)
+// ERROR: Type mismatch: inferred type is Array> but Pair was expected + + +fun myMapOf(vararg pairs: Pair) { + // Does not work due to KT-15593 + val myMap = mapOf(pairs) +} \ No newline at end of file diff --git a/idea/testData/quickfix/changeToUseSpreadOperator/multipleParams.kt b/idea/testData/quickfix/changeToUseSpreadOperator/multipleParams.kt new file mode 100644 index 00000000000..60f9becc6c5 --- /dev/null +++ b/idea/testData/quickfix/changeToUseSpreadOperator/multipleParams.kt @@ -0,0 +1,5 @@ +// "Change 'array' to '*array'" "true" + +fun foo(a: String, vararg x: String, b: Int) {} + +fun bar(array: Array) = foo("aaa", array, b = 1) diff --git a/idea/testData/quickfix/changeToUseSpreadOperator/multipleParams.kt.after b/idea/testData/quickfix/changeToUseSpreadOperator/multipleParams.kt.after new file mode 100644 index 00000000000..9146f4824e0 --- /dev/null +++ b/idea/testData/quickfix/changeToUseSpreadOperator/multipleParams.kt.after @@ -0,0 +1,5 @@ +// "Change 'array' to '*array'" "true" + +fun foo(a: String, vararg x: String, b: Int) {} + +fun bar(array: Array) = foo("aaa", *array, b = 1) diff --git a/idea/testData/quickfix/changeToUseSpreadOperator/nonArray.kt b/idea/testData/quickfix/changeToUseSpreadOperator/nonArray.kt new file mode 100644 index 00000000000..225912dc2dc --- /dev/null +++ b/idea/testData/quickfix/changeToUseSpreadOperator/nonArray.kt @@ -0,0 +1,12 @@ +// "Change 'y' to '*y'" "false" +// ACTION: Add 'toString()' call +// ACTION: Change parameter 'x' type of function 'foo' to 'List' +// ACTION: Convert to block body +// ACTION: Create function 'foo' +// DISABLE-ERRORS + +// Fix this test case if https://youtrack.jetbrains.com/issue/KT-12663 is implemented + +fun foo(vararg x: String) {} + +fun bar(y: List) = foo(y) diff --git a/idea/testData/quickfix/changeToUseSpreadOperator/nonVarArg.kt b/idea/testData/quickfix/changeToUseSpreadOperator/nonVarArg.kt new file mode 100644 index 00000000000..965efaf1e91 --- /dev/null +++ b/idea/testData/quickfix/changeToUseSpreadOperator/nonVarArg.kt @@ -0,0 +1,10 @@ +// "Change 'y' to '*y'" "false" +// ACTION: Add 'toString()' call +// ACTION: Change parameter 'x' type of function 'foo' to 'Array' +// ACTION: Convert to block body +// ACTION: Create function 'foo' +// ERROR: Type mismatch: inferred type is Array but String was expected + +fun foo(x: String) {} + +fun bar(y: Array) = foo(y) diff --git a/idea/testData/quickfix/changeToUseSpreadOperator/normal.kt b/idea/testData/quickfix/changeToUseSpreadOperator/normal.kt new file mode 100644 index 00000000000..82794f26e0a --- /dev/null +++ b/idea/testData/quickfix/changeToUseSpreadOperator/normal.kt @@ -0,0 +1,5 @@ +// "Change 'y' to '*y'" "true" + +fun foo(vararg x: String) {} + +fun bar(y: Array) = foo(y) diff --git a/idea/testData/quickfix/changeToUseSpreadOperator/normal.kt.after b/idea/testData/quickfix/changeToUseSpreadOperator/normal.kt.after new file mode 100644 index 00000000000..eaa2b90b2c4 --- /dev/null +++ b/idea/testData/quickfix/changeToUseSpreadOperator/normal.kt.after @@ -0,0 +1,5 @@ +// "Change 'y' to '*y'" "true" + +fun foo(vararg x: String) {} + +fun bar(y: Array) = foo(*y) diff --git a/idea/testData/quickfix/changeToUseSpreadOperator/vararg.kt b/idea/testData/quickfix/changeToUseSpreadOperator/vararg.kt new file mode 100644 index 00000000000..e9ace620840 --- /dev/null +++ b/idea/testData/quickfix/changeToUseSpreadOperator/vararg.kt @@ -0,0 +1,5 @@ +// "Change 'y' to '*y'" "true" + +fun foo(vararg x: String) {} + +fun bar(vararg y: String) = foo(y) diff --git a/idea/testData/quickfix/changeToUseSpreadOperator/vararg.kt.after b/idea/testData/quickfix/changeToUseSpreadOperator/vararg.kt.after new file mode 100644 index 00000000000..41074db8aa1 --- /dev/null +++ b/idea/testData/quickfix/changeToUseSpreadOperator/vararg.kt.after @@ -0,0 +1,5 @@ +// "Change 'y' to '*y'" "true" + +fun foo(vararg x: String) {} + +fun bar(vararg y: String) = foo(*y) diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index f171ebfe9ee..126a036ec93 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -1058,6 +1058,63 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { } + @TestMetadata("idea/testData/quickfix/changeToUseSpreadOperator") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ChangeToUseSpreadOperator extends AbstractQuickFixTest { + public void testAllFilesPresentInChangeToUseSpreadOperator() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/changeToUseSpreadOperator"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("differentTypeParameter.kt") + public void testDifferentTypeParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/changeToUseSpreadOperator/differentTypeParameter.kt"); + doTest(fileName); + } + + @TestMetadata("mapOf.kt") + public void testMapOf() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/changeToUseSpreadOperator/mapOf.kt"); + doTest(fileName); + } + + @TestMetadata("mapOfBug.kt") + public void testMapOfBug() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/changeToUseSpreadOperator/mapOfBug.kt"); + doTest(fileName); + } + + @TestMetadata("multipleParams.kt") + public void testMultipleParams() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/changeToUseSpreadOperator/multipleParams.kt"); + doTest(fileName); + } + + @TestMetadata("nonArray.kt") + public void testNonArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/changeToUseSpreadOperator/nonArray.kt"); + doTest(fileName); + } + + @TestMetadata("nonVarArg.kt") + public void testNonVarArg() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/changeToUseSpreadOperator/nonVarArg.kt"); + doTest(fileName); + } + + @TestMetadata("normal.kt") + public void testNormal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/changeToUseSpreadOperator/normal.kt"); + doTest(fileName); + } + + @TestMetadata("vararg.kt") + public void testVararg() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/changeToUseSpreadOperator/vararg.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/quickfix/checkArguments") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)