diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeToStarProjectionFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeToStarProjectionFix.kt index 60b9edc5d7e..fd64d13b8e7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeToStarProjectionFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeToStarProjectionFix.kt @@ -20,8 +20,14 @@ import com.intellij.codeInsight.intention.IntentionAction import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall +import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType +import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypes +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch +import org.jetbrains.kotlin.types.typeUtil.isTypeParameter class ChangeToStarProjectionFix(element: KtTypeElement) : KotlinQuickFixAction(element) { override fun getFamilyName() = "Change to star projection" @@ -36,14 +42,38 @@ class ChangeToStarProjectionFix(element: KtTypeElement) : KotlinQuickFixAction()?.right - ?: diagnostic.psiElement.getNonStrictParentOfType() + val binaryExpr = diagnostic.psiElement.getNonStrictParentOfType() + val typeReference = binaryExpr?.right ?: diagnostic.psiElement.getNonStrictParentOfType() val typeElement = typeReference?.typeElement ?: return null if (typeElement is KtFunctionType) return null + + if (binaryExpr?.operationReference?.isAsKeyword() == true) { + val parent = binaryExpr.getParentOfTypes(true, KtValueArgument::class.java, KtQualifiedExpression::class.java) + val type = when (parent) { + is KtValueArgument -> { + val callExpr = parent.getStrictParentOfType() + (callExpr?.resolveToCall()?.getArgumentMapping(parent) as? ArgumentMatch)?.valueParameter?.original?.type + } + is KtQualifiedExpression -> + if (KtPsiUtil.safeDeparenthesize(parent.receiverExpression) == binaryExpr) + parent.resolveToCall()?.resultingDescriptor?.extensionReceiverParameter?.value?.original?.type + else + null + else -> + null + } + if (type?.arguments?.any { !it.isStarProjection && !it.type.isTypeParameter() } == true) return null + } + if (typeElement.typeArgumentsAsTypes.isNotEmpty()) { return ChangeToStarProjectionFix(typeElement) } return null } + + private fun KtSimpleNameExpression.isAsKeyword(): Boolean { + val elementType = getReferencedNameElementType() + return elementType == KtTokens.AS_KEYWORD || elementType == KtTokens.AS_SAFE + } } } diff --git a/idea/testData/quickfix/addStarProjections/cast/extensionReceiver.kt b/idea/testData/quickfix/addStarProjections/cast/extensionReceiver.kt new file mode 100644 index 00000000000..275d956dcc3 --- /dev/null +++ b/idea/testData/quickfix/addStarProjections/cast/extensionReceiver.kt @@ -0,0 +1,8 @@ +// "Change type arguments to <*>" "false" +// ACTION: Convert to run +// ACTION: Convert to with +fun test(a: Any) { + (a as List).bar() +} + +fun List.bar() {} \ No newline at end of file diff --git a/idea/testData/quickfix/addStarProjections/cast/extensionReceiver2.kt b/idea/testData/quickfix/addStarProjections/cast/extensionReceiver2.kt new file mode 100644 index 00000000000..6e5c317f398 --- /dev/null +++ b/idea/testData/quickfix/addStarProjections/cast/extensionReceiver2.kt @@ -0,0 +1,8 @@ +// "Change type arguments to <*, *>" "false" +// ACTION: Convert to run +// ACTION: Convert to with +fun test(a: Any) { + (a as Map).bar() +} + +fun Map.bar() {} \ No newline at end of file diff --git a/idea/testData/quickfix/addStarProjections/cast/extensionReceiver3.kt b/idea/testData/quickfix/addStarProjections/cast/extensionReceiver3.kt new file mode 100644 index 00000000000..e2b22c7688a --- /dev/null +++ b/idea/testData/quickfix/addStarProjections/cast/extensionReceiver3.kt @@ -0,0 +1,6 @@ +// "Change type arguments to <*>" "true" +fun test(a: Any) { + (a as List).bar() +} + +fun List<*>.bar() {} \ No newline at end of file diff --git a/idea/testData/quickfix/addStarProjections/cast/extensionReceiver3.kt.after b/idea/testData/quickfix/addStarProjections/cast/extensionReceiver3.kt.after new file mode 100644 index 00000000000..187e762e835 --- /dev/null +++ b/idea/testData/quickfix/addStarProjections/cast/extensionReceiver3.kt.after @@ -0,0 +1,6 @@ +// "Change type arguments to <*>" "true" +fun test(a: Any) { + (a as List<*>).bar() +} + +fun List<*>.bar() {} \ No newline at end of file diff --git a/idea/testData/quickfix/addStarProjections/cast/extensionReceiver4.kt b/idea/testData/quickfix/addStarProjections/cast/extensionReceiver4.kt new file mode 100644 index 00000000000..4f11b7c6c0b --- /dev/null +++ b/idea/testData/quickfix/addStarProjections/cast/extensionReceiver4.kt @@ -0,0 +1,6 @@ +// "Change type arguments to <*>" "true" +fun test(a: Any) { + (a as List).bar() +} + +fun List.bar() {} \ No newline at end of file diff --git a/idea/testData/quickfix/addStarProjections/cast/extensionReceiver4.kt.after b/idea/testData/quickfix/addStarProjections/cast/extensionReceiver4.kt.after new file mode 100644 index 00000000000..59405fa9e41 --- /dev/null +++ b/idea/testData/quickfix/addStarProjections/cast/extensionReceiver4.kt.after @@ -0,0 +1,6 @@ +// "Change type arguments to <*>" "true" +fun test(a: Any) { + (a as List<*>).bar() +} + +fun List.bar() {} \ No newline at end of file diff --git a/idea/testData/quickfix/addStarProjections/cast/extensionReceiver5.kt b/idea/testData/quickfix/addStarProjections/cast/extensionReceiver5.kt new file mode 100644 index 00000000000..66468c5949d --- /dev/null +++ b/idea/testData/quickfix/addStarProjections/cast/extensionReceiver5.kt @@ -0,0 +1,6 @@ +// "Change type arguments to <*, *>" "true" +fun test(a: Any) { + (a as Map).bar() +} + +fun Map.bar() {} \ No newline at end of file diff --git a/idea/testData/quickfix/addStarProjections/cast/extensionReceiver5.kt.after b/idea/testData/quickfix/addStarProjections/cast/extensionReceiver5.kt.after new file mode 100644 index 00000000000..f390842b927 --- /dev/null +++ b/idea/testData/quickfix/addStarProjections/cast/extensionReceiver5.kt.after @@ -0,0 +1,6 @@ +// "Change type arguments to <*, *>" "true" +fun test(a: Any) { + (a as Map<*, *>).bar() +} + +fun Map.bar() {} \ No newline at end of file diff --git a/idea/testData/quickfix/addStarProjections/cast/valueArgument.kt b/idea/testData/quickfix/addStarProjections/cast/valueArgument.kt new file mode 100644 index 00000000000..b8abb1d7c9d --- /dev/null +++ b/idea/testData/quickfix/addStarProjections/cast/valueArgument.kt @@ -0,0 +1,7 @@ +// "Change type arguments to <*>" "false" +// ACTION: Add 'list =' to argument +fun test(a: Any) { + foo(a as List) +} + +fun foo(list: List) {} \ No newline at end of file diff --git a/idea/testData/quickfix/addStarProjections/cast/valueArgument2.kt b/idea/testData/quickfix/addStarProjections/cast/valueArgument2.kt new file mode 100644 index 00000000000..d0e32a35126 --- /dev/null +++ b/idea/testData/quickfix/addStarProjections/cast/valueArgument2.kt @@ -0,0 +1,7 @@ +// "Change type arguments to <*, *>" "false" +// ACTION: Add 'map =' to argument +fun test(a: Any) { + foo(a as Map) +} + +fun foo(map: Map) {} \ No newline at end of file diff --git a/idea/testData/quickfix/addStarProjections/cast/valueArgument3.kt b/idea/testData/quickfix/addStarProjections/cast/valueArgument3.kt new file mode 100644 index 00000000000..0b877e9a9f3 --- /dev/null +++ b/idea/testData/quickfix/addStarProjections/cast/valueArgument3.kt @@ -0,0 +1,6 @@ +// "Change type arguments to <*>" "true" +fun test(a: Any) { + foo(a as List) +} + +fun foo(list: List<*>) {} \ No newline at end of file diff --git a/idea/testData/quickfix/addStarProjections/cast/valueArgument3.kt.after b/idea/testData/quickfix/addStarProjections/cast/valueArgument3.kt.after new file mode 100644 index 00000000000..3f90b30da61 --- /dev/null +++ b/idea/testData/quickfix/addStarProjections/cast/valueArgument3.kt.after @@ -0,0 +1,6 @@ +// "Change type arguments to <*>" "true" +fun test(a: Any) { + foo(a as List<*>) +} + +fun foo(list: List<*>) {} \ No newline at end of file diff --git a/idea/testData/quickfix/addStarProjections/cast/valueArgument4.kt b/idea/testData/quickfix/addStarProjections/cast/valueArgument4.kt new file mode 100644 index 00000000000..a159ff0d1e3 --- /dev/null +++ b/idea/testData/quickfix/addStarProjections/cast/valueArgument4.kt @@ -0,0 +1,6 @@ +// "Change type arguments to <*>" "true" +fun test(a: Any) { + foo(a as List) +} + +fun foo(list: List) {} \ No newline at end of file diff --git a/idea/testData/quickfix/addStarProjections/cast/valueArgument4.kt.after b/idea/testData/quickfix/addStarProjections/cast/valueArgument4.kt.after new file mode 100644 index 00000000000..05eab31ffd3 --- /dev/null +++ b/idea/testData/quickfix/addStarProjections/cast/valueArgument4.kt.after @@ -0,0 +1,6 @@ +// "Change type arguments to <*>" "true" +fun test(a: Any) { + foo(a as List<*>) +} + +fun foo(list: List) {} \ No newline at end of file diff --git a/idea/testData/quickfix/addStarProjections/cast/valueArgument5.kt b/idea/testData/quickfix/addStarProjections/cast/valueArgument5.kt new file mode 100644 index 00000000000..86b4312a9eb --- /dev/null +++ b/idea/testData/quickfix/addStarProjections/cast/valueArgument5.kt @@ -0,0 +1,6 @@ +// "Change type arguments to <*, *>" "true" +fun test(a: Any) { + foo(a as Map) +} + +fun foo(map: Map) {} \ No newline at end of file diff --git a/idea/testData/quickfix/addStarProjections/cast/valueArgument5.kt.after b/idea/testData/quickfix/addStarProjections/cast/valueArgument5.kt.after new file mode 100644 index 00000000000..d7f7e21bf90 --- /dev/null +++ b/idea/testData/quickfix/addStarProjections/cast/valueArgument5.kt.after @@ -0,0 +1,6 @@ +// "Change type arguments to <*, *>" "true" +fun test(a: Any) { + foo(a as Map<*, *>) +} + +fun foo(map: Map) {} \ No newline at end of file diff --git a/idea/testData/quickfix/addStarProjections/cast/valueArgument6.kt b/idea/testData/quickfix/addStarProjections/cast/valueArgument6.kt new file mode 100644 index 00000000000..37440dab691 --- /dev/null +++ b/idea/testData/quickfix/addStarProjections/cast/valueArgument6.kt @@ -0,0 +1,7 @@ +// "Change type arguments to <*>" "false" +// ACTION: Add 'list =' to argument +fun test(a: Any) { + foo(a as? List) +} + +fun foo(list: List?) {} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index eb48157b148..98aab500f96 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -1075,10 +1075,65 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { runTest("idea/testData/quickfix/addStarProjections/cast/changeToStarProjectionNullable.kt"); } + @TestMetadata("extensionReceiver.kt") + public void testExtensionReceiver() throws Exception { + runTest("idea/testData/quickfix/addStarProjections/cast/extensionReceiver.kt"); + } + + @TestMetadata("extensionReceiver2.kt") + public void testExtensionReceiver2() throws Exception { + runTest("idea/testData/quickfix/addStarProjections/cast/extensionReceiver2.kt"); + } + + @TestMetadata("extensionReceiver3.kt") + public void testExtensionReceiver3() throws Exception { + runTest("idea/testData/quickfix/addStarProjections/cast/extensionReceiver3.kt"); + } + + @TestMetadata("extensionReceiver4.kt") + public void testExtensionReceiver4() throws Exception { + runTest("idea/testData/quickfix/addStarProjections/cast/extensionReceiver4.kt"); + } + + @TestMetadata("extensionReceiver5.kt") + public void testExtensionReceiver5() throws Exception { + runTest("idea/testData/quickfix/addStarProjections/cast/extensionReceiver5.kt"); + } + @TestMetadata("uncheckedCastOnTypeParameter.kt") public void testUncheckedCastOnTypeParameter() throws Exception { runTest("idea/testData/quickfix/addStarProjections/cast/uncheckedCastOnTypeParameter.kt"); } + + @TestMetadata("valueArgument.kt") + public void testValueArgument() throws Exception { + runTest("idea/testData/quickfix/addStarProjections/cast/valueArgument.kt"); + } + + @TestMetadata("valueArgument2.kt") + public void testValueArgument2() throws Exception { + runTest("idea/testData/quickfix/addStarProjections/cast/valueArgument2.kt"); + } + + @TestMetadata("valueArgument3.kt") + public void testValueArgument3() throws Exception { + runTest("idea/testData/quickfix/addStarProjections/cast/valueArgument3.kt"); + } + + @TestMetadata("valueArgument4.kt") + public void testValueArgument4() throws Exception { + runTest("idea/testData/quickfix/addStarProjections/cast/valueArgument4.kt"); + } + + @TestMetadata("valueArgument5.kt") + public void testValueArgument5() throws Exception { + runTest("idea/testData/quickfix/addStarProjections/cast/valueArgument5.kt"); + } + + @TestMetadata("valueArgument6.kt") + public void testValueArgument6() throws Exception { + runTest("idea/testData/quickfix/addStarProjections/cast/valueArgument6.kt"); + } } @TestMetadata("idea/testData/quickfix/addStarProjections/checkType")