diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/RemoveExplicitTypeArgumentsIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/RemoveExplicitTypeArgumentsIntention.kt index 7b1d98fce75..db0e7265607 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/RemoveExplicitTypeArgumentsIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/RemoveExplicitTypeArgumentsIntention.kt @@ -31,7 +31,9 @@ import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.util.DelegatingCall import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.checker.JetTypeChecker public class RemoveExplicitTypeArgumentsInspection : IntentionBasedInspection(RemoveExplicitTypeArgumentsIntention()) { override val problemHighlightType: ProblemHighlightType @@ -39,45 +41,60 @@ public class RemoveExplicitTypeArgumentsInspection : IntentionBasedInspection(javaClass(), "Remove explicit type arguments") { + companion object { + public fun isApplicableTo(element: JetTypeArgumentList, approximateFlexible: Boolean): Boolean { + val callExpression = element.getParent() as? JetCallExpression ?: return false + if (callExpression.getTypeArguments().isEmpty()) return false + + val context = callExpression.analyze(BodyResolveMode.PARTIAL) + val calleeExpression = callExpression.getCalleeExpression() ?: return false + val scope = context[BindingContext.RESOLUTION_SCOPE, calleeExpression/*TODO: discuss it*/] ?: return false + val originalCall = callExpression.getResolvedCall(context) ?: return false + val untypedCall = CallWithoutTypeArgs(originalCall.getCall()) + + // 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.getParent() + val expectedTypeIsExplicitInCode = when (parent) { + is JetProperty -> parent.getInitializer() == callExpression && parent.getTypeReference() != null + is JetDeclarationWithBody -> parent.getBodyExpression() == callExpression + is JetReturnExpression -> true + else -> false + } + val expectedType = if (expectedTypeIsExplicitInCode) { + context[BindingContext.EXPECTED_EXPRESSION_TYPE, callExpression] ?: TypeUtils.NO_EXPECTED_TYPE + } + else { + TypeUtils.NO_EXPECTED_TYPE + } + val dataFlow = context.getDataFlowInfo(callExpression) + val injector = InjectorForMacros(callExpression.getProject(), callExpression.findModuleDescriptor()) + val resolutionResults = injector.getCallResolver().resolveFunctionCall( + BindingTraceContext(), scope, untypedCall, expectedType, dataFlow, false) + assert (resolutionResults.isSingleResult()) { + "Removing type arguments changed resolve for: ${callExpression.getTextWithLocation()} to ${resolutionResults.getResultCode()}" + } + + val args = originalCall.getTypeArguments() + val newArgs = resolutionResults.getResultingCall().getTypeArguments() + + fun equalTypes(type1: JetType, type2: JetType): Boolean { + return if (approximateFlexible) { + JetTypeChecker.DEFAULT.equalTypes(type1, type2) + } + else { + type1 == type2 + } + } + + return args.size() == newArgs.size() && args.values().zip(newArgs.values()).all { pair -> equalTypes(pair.first, pair.second) } + } + } + override fun isApplicableTo(element: JetTypeArgumentList): Boolean { - val callExpression = element.getParent() as? JetCallExpression ?: return false - if (callExpression.getTypeArguments().isEmpty()) return false - - val context = callExpression.analyze(BodyResolveMode.PARTIAL) - val calleeExpression = callExpression.getCalleeExpression() ?: return false - val scope = context[BindingContext.RESOLUTION_SCOPE, calleeExpression/*TODO: discuss it*/] ?: return false - val originalCall = callExpression.getResolvedCall(context) ?: return false - val untypedCall = CallWithoutTypeArgs(originalCall.getCall()) - - // 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.getParent() - val expectedTypeIsExplicitInCode = when (parent) { - is JetProperty -> parent.getInitializer() == callExpression && parent.getTypeReference() != null - is JetDeclarationWithBody -> parent.getBodyExpression() == callExpression - is JetReturnExpression -> true - else -> false - } - val expectedType = if (expectedTypeIsExplicitInCode) { - context[BindingContext.EXPECTED_EXPRESSION_TYPE, callExpression] ?: TypeUtils.NO_EXPECTED_TYPE - } - else { - TypeUtils.NO_EXPECTED_TYPE - } - val dataFlow = context.getDataFlowInfo(callExpression) - val injector = InjectorForMacros(callExpression.getProject(), callExpression.findModuleDescriptor()) - val resolutionResults = injector.getCallResolver().resolveFunctionCall( - BindingTraceContext(), scope, untypedCall, expectedType, dataFlow, false) - assert (resolutionResults.isSingleResult()) { - "Removing type arguments changed resolve for: ${callExpression.getTextWithLocation()} to ${resolutionResults.getResultCode()}" - } - - val args = originalCall.getTypeArguments() - val newArgs = resolutionResults.getResultingCall().getTypeArguments() - - return args == newArgs.mapValues { approximateFlexibleTypes(it.getValue(), false) } + return isApplicableTo(element, approximateFlexible = false) } private class CallWithoutTypeArgs(call: Call) : DelegatingCall(call) { diff --git a/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessor.kt index dcf174f3953..ece5ac89163 100644 --- a/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessor.kt @@ -111,7 +111,7 @@ public class J2kPostProcessor(private val formatCode: Boolean) : PostProcessor { } override fun visitTypeArgumentList(typeArgumentList: JetTypeArgumentList) { - if (rangeFilter(typeArgumentList) == RangeFilterResult.PROCESS && RemoveExplicitTypeArgumentsIntention().isApplicableTo(typeArgumentList)) { + if (rangeFilter(typeArgumentList) == RangeFilterResult.PROCESS && RemoveExplicitTypeArgumentsIntention.isApplicableTo(typeArgumentList, approximateFlexible = true)) { redundantTypeArgs.add(typeArgumentList) return } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFixBase.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFixBase.kt index 78070e6420c..3e0fd5d9500 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFixBase.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFixBase.kt @@ -541,9 +541,9 @@ public abstract class DeprecatedSymbolUsageFixBase( } private fun removeExplicitTypeArguments(result: JetExpression) { - val intention = RemoveExplicitTypeArgumentsIntention() - result.collectDescendantsOfType(canGoInside = { !it[USER_CODE_KEY] }) { intention.isApplicableTo(it) } - .forEach { it.delete() } + result.collectDescendantsOfType(canGoInside = { !it[USER_CODE_KEY] }) { + RemoveExplicitTypeArgumentsIntention.isApplicableTo(it, approximateFlexible = true) + }.forEach { it.delete() } } private fun simplifySpreadArrayOfArguments(expression: JetExpression) { diff --git a/idea/testData/intentions/removeExplicitTypeArguments/inspectionData/expected.xml b/idea/testData/intentions/removeExplicitTypeArguments/inspectionData/expected.xml index 13bf5d12a17..cb120d91ca0 100644 --- a/idea/testData/intentions/removeExplicitTypeArguments/inspectionData/expected.xml +++ b/idea/testData/intentions/removeExplicitTypeArguments/inspectionData/expected.xml @@ -3,7 +3,7 @@ variableStringFartherScope.kt 5 light_idea_test_case - + Type arguments are unnecessary Remove explicit type arguments @@ -12,7 +12,7 @@ fourLiterals.kt 3 light_idea_test_case - + Type arguments are unnecessary Remove explicit type arguments @@ -21,7 +21,7 @@ literalStringWithClass.kt 3 light_idea_test_case - + Type arguments are unnecessary Remove explicit type arguments @@ -30,7 +30,7 @@ lambdaType.kt 3 light_idea_test_case - + Type arguments are unnecessary Remove explicit type arguments @@ -39,7 +39,7 @@ literalAny.kt 3 light_idea_test_case - + Type arguments are unnecessary Remove explicit type arguments @@ -48,7 +48,7 @@ literalString.kt 3 light_idea_test_case - + Type arguments are unnecessary Remove explicit type arguments @@ -57,7 +57,7 @@ variablesAndLiterals.kt 5 light_idea_test_case - + Type arguments are unnecessary Remove explicit type arguments @@ -66,7 +66,7 @@ literalsWhenTypeArgHasTypeArg.kt 3 light_idea_test_case - + Type arguments are unnecessary Remove explicit type arguments @@ -75,7 +75,7 @@ variableString.kt 4 light_idea_test_case - + Type arguments are unnecessary Remove explicit type arguments @@ -84,7 +84,7 @@ variableString2.kt 3 light_idea_test_case - + Type arguments are unnecessary Remove explicit type arguments @@ -93,7 +93,7 @@ twoLiteralValues.kt 3 light_idea_test_case - + Type arguments are unnecessary Remove explicit type arguments @@ -102,7 +102,7 @@ returnCallWithUnnecessaryTypeArgs.kt 4 light_idea_test_case - + Type arguments are unnecessary Remove explicit type arguments @@ -111,7 +111,7 @@ propertyInitializerIsCallWithUnnecessaryTypeArgs.kt 4 light_idea_test_case - + Type arguments are unnecessary Remove explicit type arguments @@ -120,7 +120,7 @@ functionBodyIsCallWithUnnecessaryTypeArgs.kt 3 light_idea_test_case - + Type arguments are unnecessary Remove explicit type arguments @@ -129,7 +129,26 @@ mapGet.kt 9 light_idea_test_case - + + Type arguments are unnecessary + Remove explicit type arguments + + + + platforrmType2.kt + 5 + light_idea_test_case + + Type arguments are unnecessary + Remove explicit type arguments + + + + + platforrmType1.kt + 5 + light_idea_test_case + Type arguments are unnecessary Remove explicit type arguments diff --git a/idea/testData/intentions/removeExplicitTypeArguments/platforrmType1.1.java b/idea/testData/intentions/removeExplicitTypeArguments/platforrmType1.1.java new file mode 100644 index 00000000000..7a4a6d2b4f7 --- /dev/null +++ b/idea/testData/intentions/removeExplicitTypeArguments/platforrmType1.1.java @@ -0,0 +1,7 @@ +import java.util.List; + +class JavaClass { + static List method(){ + return null; + } +} diff --git a/idea/testData/intentions/removeExplicitTypeArguments/platforrmType1.kt b/idea/testData/intentions/removeExplicitTypeArguments/platforrmType1.kt new file mode 100644 index 00000000000..cd41d18f955 --- /dev/null +++ b/idea/testData/intentions/removeExplicitTypeArguments/platforrmType1.kt @@ -0,0 +1,6 @@ +// IS_APPLICABLE: false +// WITH_RUNTIME + +fun foo() { + JavaClass.method().toTypedArray() +} \ No newline at end of file diff --git a/idea/testData/intentions/removeExplicitTypeArguments/platforrmType2.1.java b/idea/testData/intentions/removeExplicitTypeArguments/platforrmType2.1.java new file mode 100644 index 00000000000..7a4a6d2b4f7 --- /dev/null +++ b/idea/testData/intentions/removeExplicitTypeArguments/platforrmType2.1.java @@ -0,0 +1,7 @@ +import java.util.List; + +class JavaClass { + static List method(){ + return null; + } +} diff --git a/idea/testData/intentions/removeExplicitTypeArguments/platforrmType2.kt b/idea/testData/intentions/removeExplicitTypeArguments/platforrmType2.kt new file mode 100644 index 00000000000..475eeec6485 --- /dev/null +++ b/idea/testData/intentions/removeExplicitTypeArguments/platforrmType2.kt @@ -0,0 +1,6 @@ +// IS_APPLICABLE: false +// WITH_RUNTIME + +fun foo() { + JavaClass.method().toTypedArray() +} \ No newline at end of file diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/typeArguments/platformType.after.Dependency.java b/idea/testData/quickfix/deprecatedSymbolUsage/typeArguments/platformType.after.Dependency.java new file mode 100644 index 00000000000..f0719b18db4 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/typeArguments/platformType.after.Dependency.java @@ -0,0 +1,7 @@ +import java.util.List; + +class JavaClass { + static List list() { + return null; + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/typeArguments/platformType.after.kt b/idea/testData/quickfix/deprecatedSymbolUsage/typeArguments/platformType.after.kt new file mode 100644 index 00000000000..d86ee34890b --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/typeArguments/platformType.after.kt @@ -0,0 +1,10 @@ +// "Replace with 'newFun()'" "true" + +@deprecated("", ReplaceWith("newFun()")) +fun Collection.oldFun() {} + +fun Collection.newFun() {} + +fun foo() { + JavaClass.list().newFun() +} \ No newline at end of file diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/typeArguments/platformType.before.Dependency.java b/idea/testData/quickfix/deprecatedSymbolUsage/typeArguments/platformType.before.Dependency.java new file mode 100644 index 00000000000..f0719b18db4 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/typeArguments/platformType.before.Dependency.java @@ -0,0 +1,7 @@ +import java.util.List; + +class JavaClass { + static List list() { + return null; + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/typeArguments/platformType.before.Main.kt b/idea/testData/quickfix/deprecatedSymbolUsage/typeArguments/platformType.before.Main.kt new file mode 100644 index 00000000000..c14ca47293a --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/typeArguments/platformType.before.Main.kt @@ -0,0 +1,10 @@ +// "Replace with 'newFun()'" "true" + +@deprecated("", ReplaceWith("newFun()")) +fun Collection.oldFun() {} + +fun Collection.newFun() {} + +fun foo() { + JavaClass.list().oldFun() +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 13f07d420a7..f294348c02e 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -5813,6 +5813,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("platforrmType1.kt") + public void testPlatforrmType1() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitTypeArguments/platforrmType1.kt"); + doTest(fileName); + } + + @TestMetadata("platforrmType2.kt") + public void testPlatforrmType2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitTypeArguments/platforrmType2.kt"); + doTest(fileName); + } + @TestMetadata("propertyInitializerIsCallWithUnnecessaryTypeArgs.kt") public void testPropertyInitializerIsCallWithUnnecessaryTypeArgs() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitTypeArguments/propertyInitializerIsCallWithUnnecessaryTypeArgs.kt"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java index 04e5e38214f..a31b4ea21e3 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -903,6 +903,21 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes } } + @TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/typeArguments") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class TypeArguments extends AbstractQuickFixMultiFileTest { + public void testAllFilesPresentInTypeArguments() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/deprecatedSymbolUsage/typeArguments"), Pattern.compile("^(\\w+)\\.before\\.Main\\.kt$"), true); + } + + @TestMetadata("platformType.before.Main.kt") + public void testPlatformType() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/typeArguments/platformType.before.Main.kt"); + doTestWithExtraFile(fileName); + } + } + @TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/wholeProject") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/j2k/testData/fileOrElement/typeParameters/typeFromOtherFile.kt b/j2k/testData/fileOrElement/typeParameters/typeFromOtherFile.kt index 51ed748555e..9d2428deb2b 100644 --- a/j2k/testData/fileOrElement/typeParameters/typeFromOtherFile.kt +++ b/j2k/testData/fileOrElement/typeParameters/typeFromOtherFile.kt @@ -3,6 +3,6 @@ import javaApi.T class A { public fun foo(t: T): Any { - return Collections.nCopies>(1, t.set) + return Collections.nCopies(1, t.set) } } \ No newline at end of file