[kotlin] Port ChangeToUseSpreadOperatorFix to K2

^KTIJ-29614

GitOrigin-RevId: 12491bc9442c1df1cead4fe2f5932eaf7b310729
This commit is contained in:
Andrey Cherkasov
2024-06-12 23:53:12 +00:00
committed by intellij-monorepo-bot
parent 0ebe5ef4c9
commit a8adbe84e9
18 changed files with 192 additions and 11 deletions
@@ -0,0 +1,72 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.k2.codeinsight.fixes
import org.jetbrains.kotlin.analysis.api.KaSession
import org.jetbrains.kotlin.analysis.api.KaStarTypeProjection
import org.jetbrains.kotlin.analysis.api.calls.KaErrorCallInfo
import org.jetbrains.kotlin.analysis.api.calls.KaFunctionCall
import org.jetbrains.kotlin.analysis.api.calls.symbol
import org.jetbrains.kotlin.analysis.api.components.buildClassType
import org.jetbrains.kotlin.analysis.api.fir.diagnostics.KaFirDiagnostic
import org.jetbrains.kotlin.analysis.api.types.KaCapturedType
import org.jetbrains.kotlin.analysis.api.types.KaNonErrorClassType
import org.jetbrains.kotlin.analysis.api.types.KaType
import org.jetbrains.kotlin.analysis.api.types.KaTypeParameterType
import org.jetbrains.kotlin.idea.codeinsight.api.applicators.fixes.KotlinQuickFixFactory
import org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtReferenceExpression
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
internal object ChangeToUseSpreadOperatorFixFactory {
val changeToUseSpreadOperatorFixFactory = KotlinQuickFixFactory.ModCommandBased { diagnostic: KaFirDiagnostic.ArgumentTypeMismatch ->
val element = diagnostic.psi as? KtReferenceExpression ?: return@ModCommandBased emptyList()
val callExpression = element.getStrictParentOfType<KtCallExpression>() ?: return@ModCommandBased emptyList()
val arrayElementType = diagnostic.actualType.getArrayElementType()?.unwrap() ?: return@ModCommandBased emptyList()
val functionCall = (callExpression.resolveCall() as? KaErrorCallInfo)?.candidateCalls?.singleOrNull() as? KaFunctionCall<*>
?: return@ModCommandBased emptyList()
if (functionCall.argumentMapping[element]?.symbol?.isVararg != true &&
functionCall.symbol.callableId?.asSingleFqName() != FqName("kotlin.collections.mapOf")
) {
return@ModCommandBased emptyList()
}
val buildType = substituteTypeParameterTypesWithStarTypeProjections(diagnostic.expectedType) ?: return@ModCommandBased emptyList()
if (!arrayElementType.isSubTypeOf(buildType)) return@ModCommandBased emptyList()
listOf(
ChangeToUseSpreadOperatorFix(element)
)
}
}
private fun KaType.unwrap(): KaType {
return (this as? KaCapturedType)?.projection?.type ?: this
}
/**
* Substitute type parameter types in the given [type] with star type projections.
*
* For instance, given Pair<T, Pair<Int, U>>, the function returns Pair<*, Pair<Int, *>>.
*/
context(KaSession)
private fun substituteTypeParameterTypesWithStarTypeProjections(type: KaType): KaType? {
return when (type) {
is KaNonErrorClassType -> buildClassType(type.classSymbol) {
type.ownTypeArguments.mapNotNull { it.type }.forEach {
if (it is KaTypeParameterType) {
argument(KaStarTypeProjection(token))
} else {
substituteTypeParameterTypesWithStarTypeProjections(it)?.let { type ->
argument(type)
}
}
}
}
else -> null
}
}
@@ -113,6 +113,7 @@ class KotlinK2QuickFixRegistrar : KotlinQuickFixRegistrar() {
registerPsiQuickFixes(KaFirDiagnostic.InvalidIfAsExpression::class, AddIfElseBranchFix)
registerFactory(RemoveSupertypeFixFactory.removeSupertypeFixFactory)
registerFactory(NumberConversionFixFactory.numberConversionFixFactory)
registerFactory(ChangeToUseSpreadOperatorFixFactory.changeToUseSpreadOperatorFixFactory)
}
private val addAbstract = KtQuickFixesListBuilder.registerPsiQuickFix {
@@ -6093,7 +6093,69 @@ public abstract class HighLevelQuickFixTestGenerated extends AbstractHighLevelQu
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/quickfix/changeToUseSpreadOperator")
public static class ChangeToUseSpreadOperator extends AbstractHighLevelQuickFixTest {
@java.lang.Override
@org.jetbrains.annotations.NotNull
public final KotlinPluginMode getPluginMode() {
return KotlinPluginMode.K2;
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("differentTypeParameter.kt")
public void testDifferentTypeParameter() throws Exception {
runTest("../../../idea/tests/testData/quickfix/changeToUseSpreadOperator/differentTypeParameter.kt");
}
@TestMetadata("differentTypeParameterGeneric.kt")
public void testDifferentTypeParameterGeneric() throws Exception {
runTest("../../../idea/tests/testData/quickfix/changeToUseSpreadOperator/differentTypeParameterGeneric.kt");
}
@TestMetadata("mapOf.kt")
public void testMapOf() throws Exception {
runTest("../../../idea/tests/testData/quickfix/changeToUseSpreadOperator/mapOf.kt");
}
@TestMetadata("multipleParams.kt")
public void testMultipleParams() throws Exception {
runTest("../../../idea/tests/testData/quickfix/changeToUseSpreadOperator/multipleParams.kt");
}
@TestMetadata("nestedGeneric.kt")
public void testNestedGeneric() throws Exception {
runTest("../../../idea/tests/testData/quickfix/changeToUseSpreadOperator/nestedGeneric.kt");
}
@TestMetadata("nonArray.kt")
public void testNonArray() throws Exception {
runTest("../../../idea/tests/testData/quickfix/changeToUseSpreadOperator/nonArray.kt");
}
@TestMetadata("nonVarArg.kt")
public void testNonVarArg() throws Exception {
runTest("../../../idea/tests/testData/quickfix/changeToUseSpreadOperator/nonVarArg.kt");
}
@TestMetadata("normal.kt")
public void testNormal() throws Exception {
runTest("../../../idea/tests/testData/quickfix/changeToUseSpreadOperator/normal.kt");
}
@TestMetadata("stdlibMapOf.kt")
public void testStdlibMapOf() throws Exception {
runTest("../../../idea/tests/testData/quickfix/changeToUseSpreadOperator/stdlibMapOf.kt");
}
@TestMetadata("vararg.kt")
public void testVararg() throws Exception {
runTest("../../../idea/tests/testData/quickfix/changeToUseSpreadOperator/vararg.kt");
}
}
@@ -3738,6 +3738,11 @@ public abstract class K1QuickFixTestGenerated extends AbstractK1QuickFixTest {
runTest("testData/quickfix/changeToUseSpreadOperator/differentTypeParameter.kt");
}
@TestMetadata("differentTypeParameterGeneric.kt")
public void testDifferentTypeParameterGeneric() throws Exception {
runTest("testData/quickfix/changeToUseSpreadOperator/differentTypeParameterGeneric.kt");
}
@TestMetadata("mapOf.kt")
public void testMapOf() throws Exception {
runTest("testData/quickfix/changeToUseSpreadOperator/mapOf.kt");
@@ -3748,6 +3753,11 @@ public abstract class K1QuickFixTestGenerated extends AbstractK1QuickFixTest {
runTest("testData/quickfix/changeToUseSpreadOperator/multipleParams.kt");
}
@TestMetadata("nestedGeneric.kt")
public void testNestedGeneric() throws Exception {
runTest("testData/quickfix/changeToUseSpreadOperator/nestedGeneric.kt");
}
@TestMetadata("nonArray.kt")
public void testNonArray() throws Exception {
runTest("testData/quickfix/changeToUseSpreadOperator/nonArray.kt");
@@ -0,0 +1,8 @@
// "Change 'y' to '*y'" "false"
fun <T> foo(vararg x: Pair<List<String>, Pair<T, Number>>) {}
fun bar(y: Array<Pair<List<String>, Pair<Int, String>>>) = foo(<caret>y)
// IGNORE_K1
// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
@@ -6,4 +6,5 @@ fun <K, V> yourMapOf(vararg pairs: Pair<K, V>) {}
fun myMapOf(vararg pairs: Pair<String,String>) {
val myMap = yourMapOf(<caret>pairs)
}
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
@@ -6,4 +6,5 @@ fun <K, V> yourMapOf(vararg pairs: Pair<K, V>) {}
fun myMapOf(vararg pairs: Pair<String,String>) {
val myMap = yourMapOf(*pairs)
}
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
@@ -4,4 +4,5 @@ fun foo(a: String, vararg x: String, b: Int) {}
fun bar(array: Array<String>) = foo("aaa", array<caret>, b = 1)
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
@@ -4,4 +4,5 @@ fun foo(a: String, vararg x: String, b: Int) {}
fun bar(array: Array<String>) = foo("aaa", *array, b = 1)
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
@@ -0,0 +1,9 @@
// "Change 'y' to '*y'" "true"
// WITH_STDLIB
fun <T> foo(vararg x: Pair<List<String>, Pair<T, Number>>) {}
fun bar(y: Array<Pair<ArrayList<String>, Pair<Int, Int>>>) = foo(<caret>y)
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
@@ -0,0 +1,9 @@
// "Change 'y' to '*y'" "true"
// WITH_STDLIB
fun <T> foo(vararg x: Pair<List<String>, Pair<T, Number>>) {}
fun bar(y: Array<Pair<ArrayList<String>, Pair<Int, Int>>>) = foo(*y)
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
@@ -4,4 +4,5 @@ fun foo(vararg x: String) {}
fun bar(y: Array<String>) = foo(y<caret>)
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
@@ -4,4 +4,5 @@ fun foo(vararg x: String) {}
fun bar(y: Array<String>) = foo(*y)
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
@@ -4,4 +4,5 @@
fun myMapOf(vararg pairs: Pair<String,String>) {
val myMap = mapOf(<caret>pairs)
}
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
@@ -4,4 +4,5 @@
fun myMapOf(vararg pairs: Pair<String,String>) {
val myMap = mapOf(*pairs)
}
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
@@ -4,4 +4,5 @@ fun foo(vararg x: String) {}
fun bar(vararg y: String) = foo(y<caret>)
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
@@ -4,4 +4,5 @@ fun foo(vararg x: String) {}
fun bar(vararg y: String) = foo(*y)
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.quickfix.ChangeToUseSpreadOperatorFix
@@ -99,7 +99,7 @@ internal fun MutableTWorkspace.generateK2FixTests() {
model("$idea/quickfix/changeSuperTypeListEntryTypeArgument", pattern = pattern, isIgnored = true)
model("$idea/quickfix/changeToLabeledReturn", pattern = pattern)
model("$idea/quickfix/changeToMutableCollection", pattern = pattern, isIgnored = true)
model("$idea/quickfix/changeToUseSpreadOperator", pattern = pattern, isIgnored = true)
model("$idea/quickfix/changeToUseSpreadOperator", pattern = pattern)
model("$idea/quickfix/compilerError", pattern = pattern, isIgnored = true)
model("$idea/quickfix/convertCollectionLiteralToIntArrayOf", pattern = pattern)
model("$idea/quickfix/convertIllegalEscapeToUnicodeEscape", pattern = pattern, isIgnored = true)