From 4c88b318789a5e55e731287b5f3215ba5c4801b6 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Thu, 14 May 2015 14:45:26 +0300 Subject: [PATCH] KT-7525 Don't suggest to replace 'get' call with index operator for static method calls (inspection & intention) KT-5322 super.get issues a "Replace 'get' call with index operator" inspection #KT-7525 Fixed #KT-5322 Fixed --- .../jetbrains/kotlin/idea/intentions/Utils.kt | 7 +++ .../ReplaceCallWithBinaryOperatorIntention.kt | 9 +-- .../ReplaceCallWithUnaryOperatorIntention.kt | 5 ++ .../ReplaceContainsIntention.kt | 9 +-- .../ReplaceGetIntention.kt | 5 ++ .../qualifier.kt | 9 +++ .../qualifier.kt.after | 9 +++ .../replaceCallWithBinaryOperator/super.kt | 11 ++++ .../replaceCallWithUnaryOperator/qualifier.kt | 9 +++ .../qualifier.kt.after | 9 +++ .../replaceCallWithUnaryOperator/super.kt | 11 ++++ .../replaceContains/qualifier.kt | 9 +++ .../replaceContains/qualifier.kt.after | 9 +++ .../replaceContains/super.kt | 11 ++++ .../replaceGet/inspectionData/expected.xml | 11 +++- .../replaceGet/qualifier.kt | 9 +++ .../replaceGet/qualifier.kt.after | 9 +++ .../replaceGet/staticMethod.1.java | 5 ++ .../replaceGet/staticMethod.kt | 4 ++ .../conventionNameCalls/replaceGet/super.kt | 11 ++++ .../replaceGet/topLevelFun.kt | 8 +++ .../intentions/IntentionTestGenerated.java | 60 +++++++++++++++++++ 22 files changed, 230 insertions(+), 9 deletions(-) create mode 100644 idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/qualifier.kt create mode 100644 idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/qualifier.kt.after create mode 100644 idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/super.kt create mode 100644 idea/testData/intentions/conventionNameCalls/replaceCallWithUnaryOperator/qualifier.kt create mode 100644 idea/testData/intentions/conventionNameCalls/replaceCallWithUnaryOperator/qualifier.kt.after create mode 100644 idea/testData/intentions/conventionNameCalls/replaceCallWithUnaryOperator/super.kt create mode 100644 idea/testData/intentions/conventionNameCalls/replaceContains/qualifier.kt create mode 100644 idea/testData/intentions/conventionNameCalls/replaceContains/qualifier.kt.after create mode 100644 idea/testData/intentions/conventionNameCalls/replaceContains/super.kt create mode 100644 idea/testData/intentions/conventionNameCalls/replaceGet/qualifier.kt create mode 100644 idea/testData/intentions/conventionNameCalls/replaceGet/qualifier.kt.after create mode 100644 idea/testData/intentions/conventionNameCalls/replaceGet/staticMethod.1.java create mode 100644 idea/testData/intentions/conventionNameCalls/replaceGet/staticMethod.kt create mode 100644 idea/testData/intentions/conventionNameCalls/replaceGet/super.kt create mode 100644 idea/testData/intentions/conventionNameCalls/replaceGet/topLevelFun.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt index a93347bf804..49a34f25cfa 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt @@ -121,3 +121,10 @@ fun JetExpression.isExitStatement(): Boolean { } } +// returns false for call of super, static method or method from package +fun JetQualifiedExpression.isReceiverExpressionWithValue(): Boolean { + val receiver = getReceiverExpression() + if (receiver is JetSuperExpression) return false + return analyze().getType(receiver) != null +} + diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/conventionNameCalls/ReplaceCallWithBinaryOperatorIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/conventionNameCalls/ReplaceCallWithBinaryOperatorIntention.kt index cf39e87fedb..39e6fe53bcf 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/conventionNameCalls/ReplaceCallWithBinaryOperatorIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/conventionNameCalls/ReplaceCallWithBinaryOperatorIntention.kt @@ -18,10 +18,7 @@ package org.jetbrains.kotlin.idea.intentions.conventionNameCalls import com.intellij.openapi.editor.Editor import com.intellij.openapi.util.TextRange -import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingRangeIntention -import org.jetbrains.kotlin.idea.intentions.callExpression -import org.jetbrains.kotlin.idea.intentions.calleeName -import org.jetbrains.kotlin.idea.intentions.toResolvedCall +import org.jetbrains.kotlin.idea.intentions.* import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.JetDotQualifiedExpression import org.jetbrains.kotlin.psi.JetPsiFactory @@ -32,11 +29,15 @@ import org.jetbrains.kotlin.types.expressions.OperatorConventions public class ReplaceCallWithBinaryOperatorIntention : JetSelfTargetingRangeIntention(javaClass(), "Replace call with binary operator") { override fun applicabilityRange(element: JetDotQualifiedExpression): TextRange? { val operation = operation(element.calleeName) ?: return null + val resolvedCall = element.toResolvedCall() ?: return null if (!resolvedCall.getStatus().isSuccess()) return null if (resolvedCall.getCall().getTypeArgumentList() != null) return null val argument = resolvedCall.getCall().getValueArguments().singleOrNull() ?: return null if ((resolvedCall.getArgumentMapping(argument) as ArgumentMatch).valueParameter.getIndex() != 0) return null + + if (!element.isReceiverExpressionWithValue()) return null + setText("Replace with '$operation' operator") return element.callExpression!!.getCalleeExpression()!!.getTextRange() } diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/conventionNameCalls/ReplaceCallWithUnaryOperatorIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/conventionNameCalls/ReplaceCallWithUnaryOperatorIntention.kt index 36d7a2a8767..a62711cbb82 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/conventionNameCalls/ReplaceCallWithUnaryOperatorIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/conventionNameCalls/ReplaceCallWithUnaryOperatorIntention.kt @@ -21,6 +21,7 @@ import com.intellij.openapi.util.TextRange import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingRangeIntention import org.jetbrains.kotlin.idea.intentions.callExpression import org.jetbrains.kotlin.idea.intentions.calleeName +import org.jetbrains.kotlin.idea.intentions.isReceiverExpressionWithValue import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.JetDotQualifiedExpression import org.jetbrains.kotlin.psi.JetPsiFactory @@ -30,9 +31,13 @@ import org.jetbrains.kotlin.types.expressions.OperatorConventions public class ReplaceCallWithUnaryOperatorIntention : JetSelfTargetingRangeIntention(javaClass(), "Replace call with unary operator") { override fun applicabilityRange(element: JetDotQualifiedExpression): TextRange? { val operation = operation(element.calleeName) ?: return null + val call = element.callExpression ?: return null if (call.getTypeArgumentList() != null) return null if (!call.getValueArguments().isEmpty()) return null + + if (!element.isReceiverExpressionWithValue()) return null + setText("Replace with '$operation' operator") return call.getCalleeExpression()!!.getTextRange() } diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/conventionNameCalls/ReplaceContainsIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/conventionNameCalls/ReplaceContainsIntention.kt index 26071723314..cfabe9e22d2 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/conventionNameCalls/ReplaceContainsIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/conventionNameCalls/ReplaceContainsIntention.kt @@ -18,10 +18,7 @@ package org.jetbrains.kotlin.idea.intentions.conventionNameCalls import com.intellij.openapi.editor.Editor import com.intellij.openapi.util.TextRange -import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingRangeIntention -import org.jetbrains.kotlin.idea.intentions.callExpression -import org.jetbrains.kotlin.idea.intentions.calleeName -import org.jetbrains.kotlin.idea.intentions.toResolvedCall +import org.jetbrains.kotlin.idea.intentions.* import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch @@ -31,6 +28,7 @@ import org.jetbrains.kotlin.types.expressions.OperatorConventions public class ReplaceContainsIntention : JetSelfTargetingRangeIntention(javaClass(), "Replace 'contains' call with 'in' operator") { override fun applicabilityRange(element: JetDotQualifiedExpression): TextRange? { if (element.calleeName != OperatorConventions.CONTAINS.asString()) return null + val resolvedCall = element.toResolvedCall() ?: return null if (!resolvedCall.getStatus().isSuccess()) return null val argument = resolvedCall.getCall().getValueArguments().singleOrNull() ?: return null @@ -39,6 +37,9 @@ public class ReplaceContainsIntention : JetSelfTargetingRangeIntentionplus("x") +} diff --git a/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/qualifier.kt.after b/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/qualifier.kt.after new file mode 100644 index 00000000000..a741d1151ae --- /dev/null +++ b/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/qualifier.kt.after @@ -0,0 +1,9 @@ +class C { + companion object { + fun plus(s: String): C = C() + } +} + +fun foo() { + C + "x" +} diff --git a/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/super.kt b/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/super.kt new file mode 100644 index 00000000000..170125ea2eb --- /dev/null +++ b/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/super.kt @@ -0,0 +1,11 @@ +// IS_APPLICABLE: false + +open class Base { + open fun plus(s: String) = "" +} + +class C : Base() { + override fun plus(s: String): String { + return super.plus(s) + } +} diff --git a/idea/testData/intentions/conventionNameCalls/replaceCallWithUnaryOperator/qualifier.kt b/idea/testData/intentions/conventionNameCalls/replaceCallWithUnaryOperator/qualifier.kt new file mode 100644 index 00000000000..9ee010e0687 --- /dev/null +++ b/idea/testData/intentions/conventionNameCalls/replaceCallWithUnaryOperator/qualifier.kt @@ -0,0 +1,9 @@ +class C { + companion object { + fun minus(): C = C() + } +} + +fun foo() { + C.minus() +} diff --git a/idea/testData/intentions/conventionNameCalls/replaceCallWithUnaryOperator/qualifier.kt.after b/idea/testData/intentions/conventionNameCalls/replaceCallWithUnaryOperator/qualifier.kt.after new file mode 100644 index 00000000000..8eb489b730c --- /dev/null +++ b/idea/testData/intentions/conventionNameCalls/replaceCallWithUnaryOperator/qualifier.kt.after @@ -0,0 +1,9 @@ +class C { + companion object { + fun minus(): C = C() + } +} + +fun foo() { + -C +} diff --git a/idea/testData/intentions/conventionNameCalls/replaceCallWithUnaryOperator/super.kt b/idea/testData/intentions/conventionNameCalls/replaceCallWithUnaryOperator/super.kt new file mode 100644 index 00000000000..2b0476ccd54 --- /dev/null +++ b/idea/testData/intentions/conventionNameCalls/replaceCallWithUnaryOperator/super.kt @@ -0,0 +1,11 @@ +// IS_APPLICABLE: false + +open class Base { + open fun minus() = this +} + +class C : Base() { + override fun minus(): Base { + return super.minus() + } +} diff --git a/idea/testData/intentions/conventionNameCalls/replaceContains/qualifier.kt b/idea/testData/intentions/conventionNameCalls/replaceContains/qualifier.kt new file mode 100644 index 00000000000..d19737fd5c0 --- /dev/null +++ b/idea/testData/intentions/conventionNameCalls/replaceContains/qualifier.kt @@ -0,0 +1,9 @@ +class C { + companion object { + fun contains(s: String) = true + } +} + +fun foo() { + C.contains("x") +} diff --git a/idea/testData/intentions/conventionNameCalls/replaceContains/qualifier.kt.after b/idea/testData/intentions/conventionNameCalls/replaceContains/qualifier.kt.after new file mode 100644 index 00000000000..493a653eeaf --- /dev/null +++ b/idea/testData/intentions/conventionNameCalls/replaceContains/qualifier.kt.after @@ -0,0 +1,9 @@ +class C { + companion object { + fun contains(s: String) = true + } +} + +fun foo() { + "x" in C +} diff --git a/idea/testData/intentions/conventionNameCalls/replaceContains/super.kt b/idea/testData/intentions/conventionNameCalls/replaceContains/super.kt new file mode 100644 index 00000000000..b51f8450aa8 --- /dev/null +++ b/idea/testData/intentions/conventionNameCalls/replaceContains/super.kt @@ -0,0 +1,11 @@ +// IS_APPLICABLE: false + +open class Base { + open fun contains(s: String) = true +} + +class C : Base() { + override fun contains(s: String): Boolean { + return super.contains(s) + } +} diff --git a/idea/testData/intentions/conventionNameCalls/replaceGet/inspectionData/expected.xml b/idea/testData/intentions/conventionNameCalls/replaceGet/inspectionData/expected.xml index ae7438c43c1..874febad96d 100644 --- a/idea/testData/intentions/conventionNameCalls/replaceGet/inspectionData/expected.xml +++ b/idea/testData/intentions/conventionNameCalls/replaceGet/inspectionData/expected.xml @@ -52,4 +52,13 @@ Explicit 'get' Replace 'get' call with index operator - \ No newline at end of file + + + qualifier.kt + 8 + light_idea_test_case + + Explicit 'get' + Replace 'get' call with index operator + + diff --git a/idea/testData/intentions/conventionNameCalls/replaceGet/qualifier.kt b/idea/testData/intentions/conventionNameCalls/replaceGet/qualifier.kt new file mode 100644 index 00000000000..06155e771a3 --- /dev/null +++ b/idea/testData/intentions/conventionNameCalls/replaceGet/qualifier.kt @@ -0,0 +1,9 @@ +class C { + companion object { + fun get(s: String): C = C() + } +} + +fun foo() { + C.get("x") +} diff --git a/idea/testData/intentions/conventionNameCalls/replaceGet/qualifier.kt.after b/idea/testData/intentions/conventionNameCalls/replaceGet/qualifier.kt.after new file mode 100644 index 00000000000..c2e99062d7d --- /dev/null +++ b/idea/testData/intentions/conventionNameCalls/replaceGet/qualifier.kt.after @@ -0,0 +1,9 @@ +class C { + companion object { + fun get(s: String): C = C() + } +} + +fun foo() { + C["x"] +} diff --git a/idea/testData/intentions/conventionNameCalls/replaceGet/staticMethod.1.java b/idea/testData/intentions/conventionNameCalls/replaceGet/staticMethod.1.java new file mode 100644 index 00000000000..6b730e05483 --- /dev/null +++ b/idea/testData/intentions/conventionNameCalls/replaceGet/staticMethod.1.java @@ -0,0 +1,5 @@ +class JavaClass { + public static String get(String s) { + return s; + } +} \ No newline at end of file diff --git a/idea/testData/intentions/conventionNameCalls/replaceGet/staticMethod.kt b/idea/testData/intentions/conventionNameCalls/replaceGet/staticMethod.kt new file mode 100644 index 00000000000..cdb65fc9bed --- /dev/null +++ b/idea/testData/intentions/conventionNameCalls/replaceGet/staticMethod.kt @@ -0,0 +1,4 @@ +// IS_APPLICABLE: false +fun foo() { + JavaClass.get("x") +} diff --git a/idea/testData/intentions/conventionNameCalls/replaceGet/super.kt b/idea/testData/intentions/conventionNameCalls/replaceGet/super.kt new file mode 100644 index 00000000000..671bba995e5 --- /dev/null +++ b/idea/testData/intentions/conventionNameCalls/replaceGet/super.kt @@ -0,0 +1,11 @@ +// IS_APPLICABLE: false + +open class Base { + open fun get(s: String) = "" +} + +class C : Base() { + override fun get(s: String): String { + return super.get(s) + } +} diff --git a/idea/testData/intentions/conventionNameCalls/replaceGet/topLevelFun.kt b/idea/testData/intentions/conventionNameCalls/replaceGet/topLevelFun.kt new file mode 100644 index 00000000000..78136c9185b --- /dev/null +++ b/idea/testData/intentions/conventionNameCalls/replaceGet/topLevelFun.kt @@ -0,0 +1,8 @@ +// IS_APPLICABLE: false +package p + +fun get(s: String) = s + +fun foo() { + p.get("x") +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 7f65145f52e..a5bab717592 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -2237,12 +2237,24 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("qualifier.kt") + public void testQualifier() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/qualifier.kt"); + doTest(fileName); + } + @TestMetadata("rangeToSanityTest.kt") public void testRangeToSanityTest() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/rangeToSanityTest.kt"); doTest(fileName); } + @TestMetadata("super.kt") + public void testSuper() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/super.kt"); + doTest(fileName); + } + @TestMetadata("timesSanityTest.kt") public void testTimesSanityTest() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/timesSanityTest.kt"); @@ -2342,6 +2354,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("qualifier.kt") + public void testQualifier() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithUnaryOperator/qualifier.kt"); + doTest(fileName); + } + + @TestMetadata("super.kt") + public void testSuper() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithUnaryOperator/super.kt"); + doTest(fileName); + } + @TestMetadata("typeArguments.kt") public void testTypeArguments() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithUnaryOperator/typeArguments.kt"); @@ -2453,12 +2477,24 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("qualifier.kt") + public void testQualifier() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceContains/qualifier.kt"); + doTest(fileName); + } + @TestMetadata("simpleArgument.kt") public void testSimpleArgument() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceContains/simpleArgument.kt"); doTest(fileName); } + @TestMetadata("super.kt") + public void testSuper() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceContains/super.kt"); + doTest(fileName); + } + @TestMetadata("typeArguments.kt") public void testTypeArguments() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceContains/typeArguments.kt"); @@ -2546,6 +2582,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("qualifier.kt") + public void testQualifier() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceGet/qualifier.kt"); + doTest(fileName); + } + @TestMetadata("sanityCheck.kt") public void testSanityCheck() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceGet/sanityCheck.kt"); @@ -2558,6 +2600,24 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("staticMethod.kt") + public void testStaticMethod() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceGet/staticMethod.kt"); + doTest(fileName); + } + + @TestMetadata("super.kt") + public void testSuper() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceGet/super.kt"); + doTest(fileName); + } + + @TestMetadata("topLevelFun.kt") + public void testTopLevelFun() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceGet/topLevelFun.kt"); + doTest(fileName); + } + @TestMetadata("unacceptableVararg.kt") public void testUnacceptableVararg() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceGet/unacceptableVararg.kt");