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.codeInsight.intention.IntentionAction
|
||||||
import com.intellij.openapi.diagnostic.Logger
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.intellij.psi.util.PsiTreeUtil
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
|
import com.intellij.util.containers.isNullOrEmpty
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
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.psi.*
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getTargetFunction
|
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.getParentResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getValueArgumentForExpression
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getValueArgumentForExpression
|
||||||
@@ -189,16 +191,27 @@ class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() {
|
|||||||
val argumentExpression = parentIf ?: diagnosticElement
|
val argumentExpression = parentIf ?: diagnosticElement
|
||||||
val valueArgument = resolvedCall.call.getValueArgumentForExpression(argumentExpression)
|
val valueArgument = resolvedCall.call.getValueArgumentForExpression(argumentExpression)
|
||||||
if (valueArgument != null) {
|
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 expressionFromArgument = valueArgument.getArgumentExpression()
|
||||||
val valueArgumentType = if (diagnostic.factory === Errors.NULL_FOR_NONNULL_TYPE)
|
val valueArgumentType = if (diagnostic.factory === Errors.NULL_FOR_NONNULL_TYPE)
|
||||||
expressionType
|
expressionType
|
||||||
else if (expressionFromArgument != null) context.getType(expressionFromArgument) else null
|
else
|
||||||
if (correspondingParameter != null && valueArgumentType != null) {
|
expressionFromArgument?.let { context.getType(it) }
|
||||||
val callable = PsiTreeUtil.getParentOfType(correspondingParameter, KtCallableDeclaration::class.java, true)
|
if (valueArgumentType != null) {
|
||||||
val scope = callable?.getResolutionScope(context, callable.getResolutionFacade())
|
if (correspondingParameter != null) {
|
||||||
val typeToInsert = valueArgumentType.approximateWithResolvableType(scope, true)
|
val callable = PsiTreeUtil.getParentOfType(correspondingParameter, KtCallableDeclaration::class.java, true)
|
||||||
actions.add(ChangeParameterTypeFix(correspondingParameter, typeToInsert))
|
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")
|
@TestMetadata("idea/testData/quickfix/checkArguments")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user