Handle expected type more precisely in RemoveExplicitTypeArguments

So #KT-17623 Fixed
This commit is contained in:
Mikhail Glukhikh
2017-05-26 14:54:20 +03:00
parent c67f8c07d2
commit 38d6c597a3
11 changed files with 113 additions and 14 deletions
@@ -41,6 +41,33 @@ class RemoveExplicitTypeArgumentsInspection : IntentionBasedInspection<KtTypeArg
class RemoveExplicitTypeArgumentsIntention : SelfTargetingOffsetIndependentIntention<KtTypeArgumentList>(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
}
@@ -0,0 +1,9 @@
// WITH_RUNTIME
fun foo(f: ListWrapper<Int>) {}
class ListWrapper<T>(val x: List<T>)
fun f() {
foo(ListWrapper<Int>(listOf<caret><Int>()))
}
@@ -0,0 +1,9 @@
// WITH_RUNTIME
fun foo(f: ListWrapper<Int>) {}
class ListWrapper<T>(val x: List<T>)
fun f() {
foo(ListWrapper<Int>(listOf()))
}
@@ -0,0 +1,8 @@
// IS_APPLICABLE: false
// WITH_RUNTIME
fun <T> foo(list: List<T>): Int = 0
fun bar(): Int {
return foo(listOf<caret><String>())
}
@@ -0,0 +1,5 @@
// WITH_RUNTIME
class ListWrapper<T>(val x: List<T>)
val list: ListWrapper<Int> = ListWrapper(listOf<caret><Int>())
@@ -0,0 +1,5 @@
// WITH_RUNTIME
class ListWrapper<T>(val x: List<T>)
val list: ListWrapper<Int> = ListWrapper(listOf())
@@ -133,4 +133,32 @@
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Type arguments are unnecessary</problem_class>
<description>Remove explicit type arguments</description>
</problem>
<problem>
<file>insideOtherCall.kt</file>
<line>5</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/insideOtherCall.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Unnecessary type argument</problem_class>
<description>Remove explicit type arguments</description>
</problem>
<problem>
<file>insideDeepOtherCall.kt</file>
<line>8</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/insideDeepOtherCall.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Unnecessary type argument</problem_class>
<description>Remove explicit type arguments</description>
</problem>
<problem>
<file>insideDeepOtherCall.kt</file>
<line>8</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/insideDeepOtherCall.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Unnecessary type argument</problem_class>
<description>Remove explicit type arguments</description>
</problem>
</problems>
@@ -3,7 +3,7 @@ class Foo {
fun method(b: Boolean,
elementComputable: Computable<String>,
processingContext: Any): WeighingComparable<String, ProximityLocation> {
return WeighingComparable(elementComputable, ProximityLocation(), Array<Weigher<*, *>>(0) { null!! })
return WeighingComparable(elementComputable, ProximityLocation(), Array(0) { null!! })
}
companion object {
@@ -3,5 +3,5 @@ import java.util.ArrayList
fun f() {
val v : List<Int> = listOf()
val copy1: List<Int> = ArrayList(<caret>v)
val copy2: = ArrayList(v)
val copy2 = ArrayList(v)
}
@@ -2,5 +2,5 @@ import java.util.ArrayList
fun f() {
val copy1: List<Int> = ArrayList(listOf())
val copy2: = ArrayList(listOf<Int>())
val copy2 = ArrayList(listOf<Int>())
}
@@ -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");