diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/ResolutionContext.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/ResolutionContext.java index c05bab8076c..8bae292700f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/ResolutionContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/ResolutionContext.java @@ -193,7 +193,9 @@ public abstract class ResolutionContext T getContextParentOfType(@NotNull KtExpression expression, @NotNull Class... classes) { - PsiElement current = expression.getParent(); + KtExpression context = expressionContextProvider.invoke(expression); + PsiElement current = context != null ? context : expression.getParent(); + while (current != null) { for (Class klass : classes) { if (klass.isInstance(current)) { @@ -205,7 +207,7 @@ public abstract class ResolutionContext() - val resolutionResults = callResolver.resolveFunctionCall( - BindingTraceContext(), scope, untypedCall, expectedType, dataFlow, false) - if (!resolutionResults.isSingleResult) { - return false - } + val (contextExpression, expectedType) = findContextToAnalyze(callExpression, bindingContext) + val resolutionScope = contextExpression.getResolutionScope(bindingContext, resolutionFacade) + + val key = Key("RemoveExplicitTypeArgumentsIntention") + callExpression.putCopyableUserData(key, Unit) + val expressionToAnalyze = contextExpression.copied() + callExpression.putCopyableUserData(key, null) + + val newCallExpression = expressionToAnalyze.findDescendantOfType { it.getCopyableUserData(key) != null }!! + newCallExpression.typeArgumentList!!.delete() + + val newBindingContext = expressionToAnalyze.analyzeInContext( + resolutionScope, + contextExpression, + trace = DelegatingBindingTrace(bindingContext, "Temporary trace"), + dataFlowInfo = bindingContext.getDataFlowInfoBefore(contextExpression), + expectedType = expectedType ?: TypeUtils.NO_EXPECTED_TYPE, + isStatement = expectedType == null + ) + + val newCall = newCallExpression.getResolvedCall(newBindingContext) ?: return false val args = originalCall.typeArguments - val newArgs = resolutionResults.resultingCall.typeArguments + val newArgs = newCall.typeArguments fun equalTypes(type1: KotlinType, type2: KotlinType): Boolean { return if (approximateFlexible) { @@ -110,17 +121,61 @@ class RemoveExplicitTypeArgumentsIntention : SelfTargetingOffsetIndependentInten equalTypes(argType, newArgType) } } + + private fun findContextToAnalyze(expression: KtExpression, bindingContext: BindingContext): Pair { + for (element in expression.parentsWithSelf) { + if (element !is KtExpression) continue + + if (element.getQualifiedExpressionForSelector() != null) continue + if (!element.isUsedAsExpression(bindingContext)) return element to null + + val parent = element.parent + when (parent) { + is KtNamedFunction -> { + val expectedType = if (element == parent.bodyExpression && !parent.hasBlockBody() && parent.hasDeclaredReturnType()) + (bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, parent] as? FunctionDescriptor)?.returnType + else + null + return element to expectedType + } + + is KtVariableDeclaration -> { + val expectedType = if (element == parent.initializer && parent.typeReference != null) + (bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, parent] as? ValueDescriptor)?.type + else + null + return element to expectedType + } + + is KtParameter -> { + val expectedType = if (element == parent.defaultValue) + (bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, parent] as? ValueDescriptor)?.type + else + null + return element to expectedType + } + + is KtPropertyAccessor -> { + val property = parent.parent as KtProperty + val expectedType = when { + element != parent.bodyExpression || parent.hasBlockBody() -> null + parent.isSetter -> parent.builtIns.unitType + property.typeReference == null -> null + else -> (bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, parent] as? FunctionDescriptor)?.returnType + } + return element to expectedType + } + } + } + + return expression to null + } } override fun isApplicableTo(element: KtTypeArgumentList): Boolean { return isApplicableTo(element, approximateFlexible = false) } - private class CallWithoutTypeArgs(call: Call) : DelegatingCall(call) { - override fun getTypeArguments() = emptyList() - override fun getTypeArgumentList() = null - } - override fun applyTo(element: KtTypeArgumentList, editor: Editor?) { element.delete() } diff --git a/idea/testData/intentions/removeExplicitTypeArguments/blockValue.kt b/idea/testData/intentions/removeExplicitTypeArguments/blockValue.kt new file mode 100644 index 00000000000..d5b08bfcd15 --- /dev/null +++ b/idea/testData/intentions/removeExplicitTypeArguments/blockValue.kt @@ -0,0 +1,12 @@ +// IS_APPLICABLE: true +// WITH_RUNTIME + +fun f(p: Int): List? { + return if (p > 0) { + print("a") + listOf() + } + else { + null + } +} \ No newline at end of file diff --git a/idea/testData/intentions/removeExplicitTypeArguments/blockValue.kt.after b/idea/testData/intentions/removeExplicitTypeArguments/blockValue.kt.after new file mode 100644 index 00000000000..6e76365ffaf --- /dev/null +++ b/idea/testData/intentions/removeExplicitTypeArguments/blockValue.kt.after @@ -0,0 +1,12 @@ +// IS_APPLICABLE: true +// WITH_RUNTIME + +fun f(p: Int): List? { + return if (p > 0) { + print("a") + listOf() + } + else { + null + } +} \ No newline at end of file diff --git a/idea/testData/intentions/removeExplicitTypeArguments/defaultParamValue.kt b/idea/testData/intentions/removeExplicitTypeArguments/defaultParamValue.kt new file mode 100644 index 00000000000..5f15c2ca8ab --- /dev/null +++ b/idea/testData/intentions/removeExplicitTypeArguments/defaultParamValue.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: true +// WITH_RUNTIME + +fun foo(p: List = listOf()) { +} diff --git a/idea/testData/intentions/removeExplicitTypeArguments/defaultParamValue.kt.after b/idea/testData/intentions/removeExplicitTypeArguments/defaultParamValue.kt.after new file mode 100644 index 00000000000..92ae84a1c15 --- /dev/null +++ b/idea/testData/intentions/removeExplicitTypeArguments/defaultParamValue.kt.after @@ -0,0 +1,5 @@ +// IS_APPLICABLE: true +// WITH_RUNTIME + +fun foo(p: List = listOf()) { +} diff --git a/idea/testData/intentions/removeExplicitTypeArguments/getterBody.kt b/idea/testData/intentions/removeExplicitTypeArguments/getterBody.kt new file mode 100644 index 00000000000..43075ad51ff --- /dev/null +++ b/idea/testData/intentions/removeExplicitTypeArguments/getterBody.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: true +// WITH_RUNTIME + +val x: List + get() = listOf() \ No newline at end of file diff --git a/idea/testData/intentions/removeExplicitTypeArguments/getterBody.kt.after b/idea/testData/intentions/removeExplicitTypeArguments/getterBody.kt.after new file mode 100644 index 00000000000..75e36d3f1da --- /dev/null +++ b/idea/testData/intentions/removeExplicitTypeArguments/getterBody.kt.after @@ -0,0 +1,5 @@ +// IS_APPLICABLE: true +// WITH_RUNTIME + +val x: List + get() = 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 9c75b6cae60..fd195dbbb0e 100644 --- a/idea/testData/intentions/removeExplicitTypeArguments/inspectionData/expected.xml +++ b/idea/testData/intentions/removeExplicitTypeArguments/inspectionData/expected.xml @@ -138,7 +138,7 @@ insideOtherCall.kt 5 light_idea_test_case - + Unnecessary type argument Remove explicit type arguments @@ -147,7 +147,7 @@ insideDeepOtherCall.kt 8 light_idea_test_case - + Unnecessary type argument Remove explicit type arguments @@ -156,9 +156,62 @@ insideDeepOtherCall.kt 8 light_idea_test_case - + Unnecessary type argument Remove explicit type arguments + + twoArguments.kt + 8 + light_idea_test_case + + Unnecessary type argument + Remove explicit type arguments + + + + twoArguments.kt + 8 + light_idea_test_case + + Unnecessary type argument + Remove explicit type arguments + + + + qualified.kt + 6 + light_idea_test_case + + Unnecessary type argument + Remove explicit type arguments + + + + getterBody.kt + 5 + light_idea_test_case + + Unnecessary type argument + Remove explicit type arguments + + + + defaultParamValue.kt + 4 + light_idea_test_case + + Unnecessary type argument + Remove explicit type arguments + + + + blockValue.kt + 7 + light_idea_test_case + + Unnecessary type argument + Remove explicit type arguments + \ No newline at end of file diff --git a/idea/testData/intentions/removeExplicitTypeArguments/qualified.kt b/idea/testData/intentions/removeExplicitTypeArguments/qualified.kt new file mode 100644 index 00000000000..a9cc2876a16 --- /dev/null +++ b/idea/testData/intentions/removeExplicitTypeArguments/qualified.kt @@ -0,0 +1,7 @@ +interface I + +fun Int.foo(p: I){} + +fun bar(p: I) { + 1.foo(p) +} \ No newline at end of file diff --git a/idea/testData/intentions/removeExplicitTypeArguments/qualified.kt.after b/idea/testData/intentions/removeExplicitTypeArguments/qualified.kt.after new file mode 100644 index 00000000000..fe21053d831 --- /dev/null +++ b/idea/testData/intentions/removeExplicitTypeArguments/qualified.kt.after @@ -0,0 +1,7 @@ +interface I + +fun Int.foo(p: I){} + +fun bar(p: I) { + 1.foo(p) +} \ No newline at end of file diff --git a/idea/testData/intentions/removeExplicitTypeArguments/twoArguments.kt b/idea/testData/intentions/removeExplicitTypeArguments/twoArguments.kt new file mode 100644 index 00000000000..a2ded285beb --- /dev/null +++ b/idea/testData/intentions/removeExplicitTypeArguments/twoArguments.kt @@ -0,0 +1,9 @@ +// IS_APPLICABLE: true +// WITH_RUNTIME + +fun foo(p1: List, p2: List) { +} + +fun bar() { + foo(listOf(), listOf()) +} diff --git a/idea/testData/intentions/removeExplicitTypeArguments/twoArguments.kt.after b/idea/testData/intentions/removeExplicitTypeArguments/twoArguments.kt.after new file mode 100644 index 00000000000..8a42870aae3 --- /dev/null +++ b/idea/testData/intentions/removeExplicitTypeArguments/twoArguments.kt.after @@ -0,0 +1,9 @@ +// IS_APPLICABLE: true +// WITH_RUNTIME + +fun foo(p1: List, p2: List) { +} + +fun bar() { + foo(listOf(), listOf()) +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index f900a5f63a7..cbc1a04759e 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -12896,6 +12896,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/removeExplicitTypeArguments"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("blockValue.kt") + public void testBlockValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitTypeArguments/blockValue.kt"); + doTest(fileName); + } + + @TestMetadata("defaultParamValue.kt") + public void testDefaultParamValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitTypeArguments/defaultParamValue.kt"); + doTest(fileName); + } + @TestMetadata("fourLiterals.kt") public void testFourLiterals() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitTypeArguments/fourLiterals.kt"); @@ -12908,6 +12920,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("getterBody.kt") + public void testGetterBody() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitTypeArguments/getterBody.kt"); + doTest(fileName); + } + @TestMetadata("inapplicableTypeThatIsAFunItCannotBeInferred.kt") public void testInapplicableTypeThatIsAFunItCannotBeInferred() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitTypeArguments/inapplicableTypeThatIsAFunItCannotBeInferred.kt"); @@ -13010,12 +13028,24 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("qualified.kt") + public void testQualified() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitTypeArguments/qualified.kt"); + doTest(fileName); + } + @TestMetadata("returnCallWithUnnecessaryTypeArgs.kt") public void testReturnCallWithUnnecessaryTypeArgs() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitTypeArguments/returnCallWithUnnecessaryTypeArgs.kt"); doTest(fileName); } + @TestMetadata("twoArguments.kt") + public void testTwoArguments() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitTypeArguments/twoArguments.kt"); + doTest(fileName); + } + @TestMetadata("twoLiteralValues.kt") public void testTwoLiteralValues() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitTypeArguments/twoLiteralValues.kt"); diff --git a/j2k/testData/fileOrElement/field/specifyType.kt b/j2k/testData/fileOrElement/field/specifyType.kt index 9f35d9038ad..4457aa031c3 100644 --- a/j2k/testData/fileOrElement/field/specifyType.kt +++ b/j2k/testData/fileOrElement/field/specifyType.kt @@ -20,7 +20,7 @@ internal class A { } fun bar() { - field5 = ArrayList() + field5 = ArrayList() field7++ field8++ field9 = null