Quick fix for applying spread operator where vararg is expected #KT-6824 Fixed

This commit is contained in:
shiraji
2016-12-27 01:35:18 +09:00
committed by Mikhail Glukhikh
parent 6ae4b42164
commit 44e69d8adc
15 changed files with 201 additions and 7 deletions
@@ -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<KtExpression>(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)
}
}
@@ -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))
}
}
}
}
@@ -0,0 +1,10 @@
// "Change 'y' to '*y'" "false"
// ACTION: Add 'toString()' call
// ACTION: Change parameter 'x' type of function 'foo' to 'Array<Int>'
// ACTION: Convert to block body
// ACTION: Create function 'foo'
// DISABLE-ERRORS
fun foo(vararg x: String) {}
fun bar(y: Array<Int>) = foo(y<caret>)
@@ -0,0 +1,8 @@
// "Change 'pairs' to '*pairs'" "true"
// WITH_RUNTIME
fun <K, V> yourMapOf(vararg pairs: Pair<K, V>) {}
fun myMapOf(vararg pairs: Pair<String,String>) {
val myMap = yourMapOf(<caret>pairs)
}
@@ -0,0 +1,8 @@
// "Change 'pairs' to '*pairs'" "true"
// WITH_RUNTIME
fun <K, V> yourMapOf(vararg pairs: Pair<K, V>) {}
fun myMapOf(vararg pairs: Pair<String,String>) {
val myMap = yourMapOf(*pairs)
}
@@ -0,0 +1,11 @@
// "Change 'pairs' to '*pairs'" "false"
// WITH_RUNTIME
// ACTION: Create function 'mapOf'
// ERROR: Type inference failed: fun <K, V> mapOf(pair: Pair<K, V>): Map<K, V><br>cannot be applied to<br>(Array<out Pair<String, String>>)<br>
// ERROR: Type mismatch: inferred type is Array<out Pair<String, String>> but Pair<???, ???> was expected
fun myMapOf(vararg pairs: Pair<String,String>) {
// Does not work due to KT-15593
val myMap = mapOf(<caret>pairs)
}
@@ -0,0 +1,5 @@
// "Change 'array' to '*array'" "true"
fun foo(a: String, vararg x: String, b: Int) {}
fun bar(array: Array<String>) = foo("aaa", array<caret>, b = 1)
@@ -0,0 +1,5 @@
// "Change 'array' to '*array'" "true"
fun foo(a: String, vararg x: String, b: Int) {}
fun bar(array: Array<String>) = foo("aaa", *array, b = 1)
@@ -0,0 +1,12 @@
// "Change 'y' to '*y'" "false"
// ACTION: Add 'toString()' call
// ACTION: Change parameter 'x' type of function 'foo' to 'List<String>'
// 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<String>) = foo(y<caret>)
@@ -0,0 +1,10 @@
// "Change 'y' to '*y'" "false"
// ACTION: Add 'toString()' call
// ACTION: Change parameter 'x' type of function 'foo' to 'Array<String>'
// ACTION: Convert to block body
// ACTION: Create function 'foo'
// ERROR: Type mismatch: inferred type is Array<String> but String was expected
fun foo(x: String) {}
fun bar(y: Array<String>) = foo(y<caret>)
@@ -0,0 +1,5 @@
// "Change 'y' to '*y'" "true"
fun foo(vararg x: String) {}
fun bar(y: Array<String>) = foo(y<caret>)
@@ -0,0 +1,5 @@
// "Change 'y' to '*y'" "true"
fun foo(vararg x: String) {}
fun bar(y: Array<String>) = foo(*y)
@@ -0,0 +1,5 @@
// "Change 'y' to '*y'" "true"
fun foo(vararg x: String) {}
fun bar(vararg y: String) = foo(y<caret>)
@@ -0,0 +1,5 @@
// "Change 'y' to '*y'" "true"
fun foo(vararg x: String) {}
fun bar(vararg y: String) = foo(*y)
@@ -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)