Quick fix for applying spread operator where vararg is expected #KT-6824 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
6ae4b42164
commit
44e69d8adc
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user