From 38d6c597a35375dbb12a39e5c6d018238f383588 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Fri, 26 May 2017 14:54:20 +0300 Subject: [PATCH] Handle expected type more precisely in RemoveExplicitTypeArguments So #KT-17623 Fixed --- .../RemoveExplicitTypeArgumentsIntention.kt | 39 +++++++++++++------ .../insideDeepOtherCall.kt | 9 +++++ .../insideDeepOtherCall.kt.after | 9 +++++ .../insideGenericCall.kt | 8 ++++ .../insideOtherCall.kt | 5 +++ .../insideOtherCall.kt.after | 5 +++ .../inspectionData/expected.xml | 28 +++++++++++++ .../fromIntellij/Substitution.kt.after | 2 +- .../explicateTypeArgument/ExplicateForSome.kt | 2 +- .../ExplicateForSome.kt.after | 2 +- .../intentions/IntentionTestGenerated.java | 18 +++++++++ 11 files changed, 113 insertions(+), 14 deletions(-) create mode 100644 idea/testData/intentions/removeExplicitTypeArguments/insideDeepOtherCall.kt create mode 100644 idea/testData/intentions/removeExplicitTypeArguments/insideDeepOtherCall.kt.after create mode 100644 idea/testData/intentions/removeExplicitTypeArguments/insideGenericCall.kt create mode 100644 idea/testData/intentions/removeExplicitTypeArguments/insideOtherCall.kt create mode 100644 idea/testData/intentions/removeExplicitTypeArguments/insideOtherCall.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/RemoveExplicitTypeArgumentsIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/RemoveExplicitTypeArgumentsIntention.kt index 0c344fe09a1..88f2f9f37f8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/RemoveExplicitTypeArgumentsIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/RemoveExplicitTypeArgumentsIntention.kt @@ -41,6 +41,33 @@ class RemoveExplicitTypeArgumentsInspection : IntentionBasedInspection(KtTypeArgumentList::class.java, "Remove explicit type arguments") { companion object { + + private fun KtCallExpression.argumentTypesDeducedFromReturnType(context: BindingContext): Boolean { + val resolvedCall = getResolvedCall(context) ?: return false + val typeParameters = resolvedCall.candidateDescriptor.typeParameters + if (typeParameters.isEmpty()) return true + val returnType = resolvedCall.candidateDescriptor.returnType ?: return false + return returnType.arguments.map { it.type }.containsAll(typeParameters.map { it.defaultType }) + } + + private fun KtCallExpression.hasExplicitExpectedType(context: BindingContext): Boolean { + // todo Check with expected type for other expressions + // If always use expected type from trace there is a problem with nested calls: + // the expression type for them can depend on their explicit type arguments (via outer call), + // therefore we should resolve outer call with erased type arguments for inner call + val parent = parent + return when (parent) { + is KtProperty -> parent.initializer == this && parent.typeReference != null + is KtDeclarationWithBody -> parent.bodyExpression == this + is KtReturnExpression -> true + is KtValueArgument -> (parent.parent.parent as? KtCallExpression)?.let { + it.typeArgumentList != null || + it.hasExplicitExpectedType(context) && it.argumentTypesDeducedFromReturnType(context) + }?: false + else -> false + } + } + fun isApplicableTo(element: KtTypeArgumentList, approximateFlexible: Boolean): Boolean { val callExpression = element.parent as? KtCallExpression ?: return false if (callExpression.typeArguments.isEmpty()) return false @@ -52,17 +79,7 @@ class RemoveExplicitTypeArgumentsIntention : SelfTargetingOffsetIndependentInten val originalCall = callExpression.getResolvedCall(context) ?: return false val untypedCall = CallWithoutTypeArgs(originalCall.call) - // todo Check with expected type for other expressions - // If always use expected type from trace there is a problem with nested calls: - // the expression type for them can depend on their explicit type arguments (via outer call), - // therefore we should resolve outer call with erased type arguments for inner call - val parent = callExpression.parent - val expectedTypeIsExplicitInCode = when (parent) { - is KtProperty -> parent.initializer == callExpression && parent.typeReference != null - is KtDeclarationWithBody -> parent.bodyExpression == callExpression - is KtReturnExpression -> true - else -> false - } + val expectedTypeIsExplicitInCode = callExpression.hasExplicitExpectedType(context) val expectedType = if (expectedTypeIsExplicitInCode) { context[BindingContext.EXPECTED_EXPRESSION_TYPE, callExpression] ?: TypeUtils.NO_EXPECTED_TYPE } diff --git a/idea/testData/intentions/removeExplicitTypeArguments/insideDeepOtherCall.kt b/idea/testData/intentions/removeExplicitTypeArguments/insideDeepOtherCall.kt new file mode 100644 index 00000000000..1a1ce8598cd --- /dev/null +++ b/idea/testData/intentions/removeExplicitTypeArguments/insideDeepOtherCall.kt @@ -0,0 +1,9 @@ +// WITH_RUNTIME + +fun foo(f: ListWrapper) {} + +class ListWrapper(val x: List) + +fun f() { + foo(ListWrapper(listOf())) +} diff --git a/idea/testData/intentions/removeExplicitTypeArguments/insideDeepOtherCall.kt.after b/idea/testData/intentions/removeExplicitTypeArguments/insideDeepOtherCall.kt.after new file mode 100644 index 00000000000..bd7e56cb940 --- /dev/null +++ b/idea/testData/intentions/removeExplicitTypeArguments/insideDeepOtherCall.kt.after @@ -0,0 +1,9 @@ +// WITH_RUNTIME + +fun foo(f: ListWrapper) {} + +class ListWrapper(val x: List) + +fun f() { + foo(ListWrapper(listOf())) +} diff --git a/idea/testData/intentions/removeExplicitTypeArguments/insideGenericCall.kt b/idea/testData/intentions/removeExplicitTypeArguments/insideGenericCall.kt new file mode 100644 index 00000000000..ab154414f1d --- /dev/null +++ b/idea/testData/intentions/removeExplicitTypeArguments/insideGenericCall.kt @@ -0,0 +1,8 @@ +// IS_APPLICABLE: false +// WITH_RUNTIME + +fun foo(list: List): Int = 0 + +fun bar(): Int { + return foo(listOf()) +} \ No newline at end of file diff --git a/idea/testData/intentions/removeExplicitTypeArguments/insideOtherCall.kt b/idea/testData/intentions/removeExplicitTypeArguments/insideOtherCall.kt new file mode 100644 index 00000000000..38505744f51 --- /dev/null +++ b/idea/testData/intentions/removeExplicitTypeArguments/insideOtherCall.kt @@ -0,0 +1,5 @@ +// WITH_RUNTIME + +class ListWrapper(val x: List) + +val list: ListWrapper = ListWrapper(listOf()) \ No newline at end of file diff --git a/idea/testData/intentions/removeExplicitTypeArguments/insideOtherCall.kt.after b/idea/testData/intentions/removeExplicitTypeArguments/insideOtherCall.kt.after new file mode 100644 index 00000000000..1faf152414f --- /dev/null +++ b/idea/testData/intentions/removeExplicitTypeArguments/insideOtherCall.kt.after @@ -0,0 +1,5 @@ +// WITH_RUNTIME + +class ListWrapper(val x: List) + +val list: ListWrapper = ListWrapper(listOf()) \ No newline at end of file diff --git a/idea/testData/intentions/removeExplicitTypeArguments/inspectionData/expected.xml b/idea/testData/intentions/removeExplicitTypeArguments/inspectionData/expected.xml index 7e7b130bde5..9c75b6cae60 100644 --- a/idea/testData/intentions/removeExplicitTypeArguments/inspectionData/expected.xml +++ b/idea/testData/intentions/removeExplicitTypeArguments/inspectionData/expected.xml @@ -133,4 +133,32 @@ Type arguments are unnecessary Remove explicit type arguments + + + insideOtherCall.kt + 5 + light_idea_test_case + + Unnecessary type argument + Remove explicit type arguments + + + + insideDeepOtherCall.kt + 8 + light_idea_test_case + + Unnecessary type argument + Remove explicit type arguments + + + + insideDeepOtherCall.kt + 8 + light_idea_test_case + + Unnecessary type argument + Remove explicit type arguments + + \ No newline at end of file diff --git a/idea/testData/refactoring/inline/function/fromIntellij/Substitution.kt.after b/idea/testData/refactoring/inline/function/fromIntellij/Substitution.kt.after index 05186b22430..1a17aa72b99 100644 --- a/idea/testData/refactoring/inline/function/fromIntellij/Substitution.kt.after +++ b/idea/testData/refactoring/inline/function/fromIntellij/Substitution.kt.after @@ -3,7 +3,7 @@ class Foo { fun method(b: Boolean, elementComputable: Computable, processingContext: Any): WeighingComparable { - return WeighingComparable(elementComputable, ProximityLocation(), Array>(0) { null!! }) + return WeighingComparable(elementComputable, ProximityLocation(), Array(0) { null!! }) } companion object { diff --git a/idea/testData/refactoring/inline/inlineVariableOrProperty/explicateTypeArgument/ExplicateForSome.kt b/idea/testData/refactoring/inline/inlineVariableOrProperty/explicateTypeArgument/ExplicateForSome.kt index 49f9c9c85bb..8268c6d7033 100644 --- a/idea/testData/refactoring/inline/inlineVariableOrProperty/explicateTypeArgument/ExplicateForSome.kt +++ b/idea/testData/refactoring/inline/inlineVariableOrProperty/explicateTypeArgument/ExplicateForSome.kt @@ -3,5 +3,5 @@ import java.util.ArrayList fun f() { val v : List = listOf() val copy1: List = ArrayList(v) - val copy2: = ArrayList(v) + val copy2 = ArrayList(v) } diff --git a/idea/testData/refactoring/inline/inlineVariableOrProperty/explicateTypeArgument/ExplicateForSome.kt.after b/idea/testData/refactoring/inline/inlineVariableOrProperty/explicateTypeArgument/ExplicateForSome.kt.after index 4c9e0e1cc96..a40d02a5d5e 100644 --- a/idea/testData/refactoring/inline/inlineVariableOrProperty/explicateTypeArgument/ExplicateForSome.kt.after +++ b/idea/testData/refactoring/inline/inlineVariableOrProperty/explicateTypeArgument/ExplicateForSome.kt.after @@ -2,5 +2,5 @@ import java.util.ArrayList fun f() { val copy1: List = ArrayList(listOf()) - val copy2: = ArrayList(listOf()) + val copy2 = ArrayList(listOf()) } diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 9c79180e0de..ce0d565504d 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -12896,6 +12896,24 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("insideDeepOtherCall.kt") + public void testInsideDeepOtherCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitTypeArguments/insideDeepOtherCall.kt"); + doTest(fileName); + } + + @TestMetadata("insideGenericCall.kt") + public void testInsideGenericCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitTypeArguments/insideGenericCall.kt"); + doTest(fileName); + } + + @TestMetadata("insideOtherCall.kt") + public void testInsideOtherCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitTypeArguments/insideOtherCall.kt"); + doTest(fileName); + } + @TestMetadata("lambdaType.kt") public void testLambdaType() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitTypeArguments/lambdaType.kt");