KT-7989 ReplaceWith replacement adds redundant type arguments for platform types

Highlighting of redundant type arguments made more strict about platform types

 #KT-7989 Fixed
This commit is contained in:
Valentin Kipyatkov
2015-06-10 15:21:37 +03:00
parent 2203fb3231
commit 366a2be8a7
15 changed files with 181 additions and 58 deletions
@@ -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<JetTypeArgumentList>(RemoveExplicitTypeArgumentsIntention()) {
override val problemHighlightType: ProblemHighlightType
@@ -39,45 +41,60 @@ public class RemoveExplicitTypeArgumentsInspection : IntentionBasedInspection<Je
}
public class RemoveExplicitTypeArgumentsIntention : JetSelfTargetingOffsetIndependentIntention<JetTypeArgumentList>(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) {
@@ -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
}
@@ -541,9 +541,9 @@ public abstract class DeprecatedSymbolUsageFixBase(
}
private fun removeExplicitTypeArguments(result: JetExpression) {
val intention = RemoveExplicitTypeArgumentsIntention()
result.collectDescendantsOfType<JetTypeArgumentList>(canGoInside = { !it[USER_CODE_KEY] }) { intention.isApplicableTo(it) }
.forEach { it.delete() }
result.collectDescendantsOfType<JetTypeArgumentList>(canGoInside = { !it[USER_CODE_KEY] }) {
RemoveExplicitTypeArgumentsIntention.isApplicableTo(it, approximateFlexible = true)
}.forEach { it.delete() }
}
private fun simplifySpreadArrayOfArguments(expression: JetExpression) {
@@ -3,7 +3,7 @@
<file>variableStringFartherScope.kt</file>
<line>5</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/variableStringFartherScope.kt" />
<entry_point TYPE="file" FQNAME="variableStringFartherScope.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Type arguments are unnecessary</problem_class>
<description>Remove explicit type arguments</description>
</problem>
@@ -12,7 +12,7 @@
<file>fourLiterals.kt</file>
<line>3</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/fourLiterals.kt" />
<entry_point TYPE="file" FQNAME="fourLiterals.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Type arguments are unnecessary</problem_class>
<description>Remove explicit type arguments</description>
</problem>
@@ -21,7 +21,7 @@
<file>literalStringWithClass.kt</file>
<line>3</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/literalStringWithClass.kt" />
<entry_point TYPE="file" FQNAME="literalStringWithClass.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Type arguments are unnecessary</problem_class>
<description>Remove explicit type arguments</description>
</problem>
@@ -30,7 +30,7 @@
<file>lambdaType.kt</file>
<line>3</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/lambdaType.kt" />
<entry_point TYPE="file" FQNAME="lambdaType.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Type arguments are unnecessary</problem_class>
<description>Remove explicit type arguments</description>
</problem>
@@ -39,7 +39,7 @@
<file>literalAny.kt</file>
<line>3</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/literalAny.kt" />
<entry_point TYPE="file" FQNAME="literalAny.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Type arguments are unnecessary</problem_class>
<description>Remove explicit type arguments</description>
</problem>
@@ -48,7 +48,7 @@
<file>literalString.kt</file>
<line>3</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/literalString.kt" />
<entry_point TYPE="file" FQNAME="literalString.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Type arguments are unnecessary</problem_class>
<description>Remove explicit type arguments</description>
</problem>
@@ -57,7 +57,7 @@
<file>variablesAndLiterals.kt</file>
<line>5</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/variablesAndLiterals.kt" />
<entry_point TYPE="file" FQNAME="variablesAndLiterals.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Type arguments are unnecessary</problem_class>
<description>Remove explicit type arguments</description>
</problem>
@@ -66,7 +66,7 @@
<file>literalsWhenTypeArgHasTypeArg.kt</file>
<line>3</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/literalsWhenTypeArgHasTypeArg.kt" />
<entry_point TYPE="file" FQNAME="literalsWhenTypeArgHasTypeArg.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Type arguments are unnecessary</problem_class>
<description>Remove explicit type arguments</description>
</problem>
@@ -75,7 +75,7 @@
<file>variableString.kt</file>
<line>4</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/variableString.kt" />
<entry_point TYPE="file" FQNAME="variableString.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Type arguments are unnecessary</problem_class>
<description>Remove explicit type arguments</description>
</problem>
@@ -84,7 +84,7 @@
<file>variableString2.kt</file>
<line>3</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/variableString2.kt" />
<entry_point TYPE="file" FQNAME="variableString2.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Type arguments are unnecessary</problem_class>
<description>Remove explicit type arguments</description>
</problem>
@@ -93,7 +93,7 @@
<file>twoLiteralValues.kt</file>
<line>3</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/twoLiteralValues.kt" />
<entry_point TYPE="file" FQNAME="twoLiteralValues.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Type arguments are unnecessary</problem_class>
<description>Remove explicit type arguments</description>
</problem>
@@ -102,7 +102,7 @@
<file>returnCallWithUnnecessaryTypeArgs.kt</file>
<line>4</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/returnCallWithUnnecessaryTypeArgs.kt" />
<entry_point TYPE="file" FQNAME="returnCallWithUnnecessaryTypeArgs.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Type arguments are unnecessary</problem_class>
<description>Remove explicit type arguments</description>
</problem>
@@ -111,7 +111,7 @@
<file>propertyInitializerIsCallWithUnnecessaryTypeArgs.kt</file>
<line>4</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/propertyInitializerIsCallWithUnnecessaryTypeArgs.kt" />
<entry_point TYPE="file" FQNAME="propertyInitializerIsCallWithUnnecessaryTypeArgs.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Type arguments are unnecessary</problem_class>
<description>Remove explicit type arguments</description>
</problem>
@@ -120,7 +120,7 @@
<file>functionBodyIsCallWithUnnecessaryTypeArgs.kt</file>
<line>3</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/functionBodyIsCallWithUnnecessaryTypeArgs.kt" />
<entry_point TYPE="file" FQNAME="functionBodyIsCallWithUnnecessaryTypeArgs.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Type arguments are unnecessary</problem_class>
<description>Remove explicit type arguments</description>
</problem>
@@ -129,7 +129,26 @@
<file>mapGet.kt</file>
<line>9</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/mapGet.kt" />
<entry_point TYPE="file" FQNAME="mapGet.kt" />
<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>platforrmType2.kt</file>
<line>5</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="platforrmType2.kt" />
<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>platforrmType1.kt</file>
<line>5</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="platforrmType1.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Type arguments are unnecessary</problem_class>
<description>Remove explicit type arguments</description>
</problem>
@@ -0,0 +1,7 @@
import java.util.List;
class JavaClass {
static List<String> method(){
return null;
}
}
@@ -0,0 +1,6 @@
// IS_APPLICABLE: false
// WITH_RUNTIME
fun foo() {
JavaClass.method().toTypedArray<caret><String>()
}
@@ -0,0 +1,7 @@
import java.util.List;
class JavaClass {
static List<String> method(){
return null;
}
}
@@ -0,0 +1,6 @@
// IS_APPLICABLE: false
// WITH_RUNTIME
fun foo() {
JavaClass.method().toTypedArray<caret><String?>()
}
@@ -0,0 +1,7 @@
import java.util.List;
class JavaClass {
static List<String> list() {
return null;
}
}
@@ -0,0 +1,10 @@
// "Replace with 'newFun()'" "true"
@deprecated("", ReplaceWith("newFun()"))
fun <T> Collection<T>.oldFun() {}
fun <T> Collection<T>.newFun() {}
fun foo() {
JavaClass.list().<caret>newFun()
}
@@ -0,0 +1,7 @@
import java.util.List;
class JavaClass {
static List<String> list() {
return null;
}
}
@@ -0,0 +1,10 @@
// "Replace with 'newFun()'" "true"
@deprecated("", ReplaceWith("newFun()"))
fun <T> Collection<T>.oldFun() {}
fun <T> Collection<T>.newFun() {}
fun foo() {
JavaClass.list().<caret>oldFun()
}
@@ -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");
@@ -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)
@@ -3,6 +3,6 @@ import javaApi.T
class A {
public fun foo(t: T): Any {
return Collections.nCopies<Set<String>>(1, t.set)
return Collections.nCopies(1, t.set)
}
}