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
This commit is contained in:
Valentin Kipyatkov
2015-05-14 14:45:26 +03:00
parent 0fe334fd55
commit 4c88b31878
22 changed files with 230 additions and 9 deletions
@@ -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
}
@@ -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<JetDotQualifiedExpression>(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()
}
@@ -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<JetDotQualifiedExpression>(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()
}
@@ -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<JetDotQualifiedExpression>(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 : JetSelfTargetingRangeIntention<JetDotQua
val target = resolvedCall.getResultingDescriptor()
val returnType = target.getReturnType() ?: return null
if (!target.builtIns.isBooleanOrSubtype(returnType)) return null
if (!element.isReceiverExpressionWithValue()) return null
return element.callExpression!!.getCalleeExpression()!!.getTextRange()
}
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
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.psi.JetDotQualifiedExpression
import org.jetbrains.kotlin.psi.JetPsiFactory
import org.jetbrains.kotlin.psi.buildExpression
@@ -33,9 +34,13 @@ public class ReplaceGetIntention : JetSelfTargetingRangeIntention<JetDotQualifie
if (element.calleeName != "get") return null
val call = element.callExpression ?: return null
if (call.getTypeArgumentList() != null) return null
val arguments = call.getValueArguments()
if (arguments.isEmpty()) return null
if (arguments.any { it.isNamed() }) return null
if (!element.isReceiverExpressionWithValue()) return null
return call.getCalleeExpression()!!.getTextRange()
}
@@ -0,0 +1,9 @@
class C {
companion object {
fun plus(s: String): C = C()
}
}
fun foo() {
C.<caret>plus("x")
}
@@ -0,0 +1,9 @@
class C {
companion object {
fun plus(s: String): C = C()
}
}
fun foo() {
C + "x"
}
@@ -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.<caret>plus(s)
}
}
@@ -0,0 +1,9 @@
class C {
companion object {
fun minus(): C = C()
}
}
fun foo() {
C.<caret>minus()
}
@@ -0,0 +1,9 @@
class C {
companion object {
fun minus(): C = C()
}
}
fun foo() {
-C
}
@@ -0,0 +1,11 @@
// IS_APPLICABLE: false
open class Base {
open fun minus() = this
}
class C : Base() {
override fun minus(): Base {
return super.<caret>minus()
}
}
@@ -0,0 +1,9 @@
class C {
companion object {
fun contains(s: String) = true
}
}
fun foo() {
C.<caret>contains("x")
}
@@ -0,0 +1,9 @@
class C {
companion object {
fun contains(s: String) = true
}
}
fun foo() {
"x" in C
}
@@ -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.<caret>contains(s)
}
}
@@ -52,4 +52,13 @@
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Explicit 'get'</problem_class>
<description>Replace 'get' call with index operator</description>
</problem>
</problems>
<problem>
<file>qualifier.kt</file>
<line>8</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/qualifier.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Explicit 'get'</problem_class>
<description>Replace 'get' call with index operator</description>
</problem>
</problems>
@@ -0,0 +1,9 @@
class C {
companion object {
fun get(s: String): C = C()
}
}
fun foo() {
C.<caret>get("x")
}
@@ -0,0 +1,9 @@
class C {
companion object {
fun get(s: String): C = C()
}
}
fun foo() {
C["x"]
}
@@ -0,0 +1,5 @@
class JavaClass {
public static String get(String s) {
return s;
}
}
@@ -0,0 +1,4 @@
// IS_APPLICABLE: false
fun foo() {
JavaClass.<caret>get("x")
}
@@ -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.<caret>get(s)
}
}
@@ -0,0 +1,8 @@
// IS_APPLICABLE: false
package p
fun get(s: String) = s
fun foo() {
p.<caret>get("x")
}
@@ -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");