Do not highlight replacement of equals involving floating-point types

Two tests with smart casts do not work yet
Part of KT-25933
This commit is contained in:
kenji tomita
2018-09-27 02:48:42 +03:00
committed by Mikhail Glukhikh
parent 8c6337f3f6
commit 1aa9d5673b
12 changed files with 99 additions and 8 deletions
@@ -21,20 +21,25 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.codegen.kotlinType
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.inspections.AbstractApplicabilityBasedInspection
import org.jetbrains.kotlin.idea.intentions.callExpression
import org.jetbrains.kotlin.idea.intentions.*
import org.jetbrains.kotlin.idea.intentions.conventionNameCalls.isAnyEquals
import org.jetbrains.kotlin.idea.intentions.isReceiverExpressionWithValue
import org.jetbrains.kotlin.idea.intentions.toResolvedCall
import org.jetbrains.kotlin.lexer.KtSingleValueToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getLastParentOfTypeInRow
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.resolve.calls.callUtil.getFirstArgumentExpression
import org.jetbrains.kotlin.resolve.calls.callUtil.getReceiverExpression
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.types.typeUtil.*
import org.jetbrains.kotlin.util.OperatorNameConventions
class ReplaceCallWithBinaryOperatorInspection : AbstractApplicabilityBasedInspection<KtDotQualifiedExpression>(
@@ -69,10 +74,13 @@ class ReplaceCallWithBinaryOperatorInspection : AbstractApplicabilityBasedInspec
override fun inspectionHighlightType(element: KtDotQualifiedExpression): ProblemHighlightType {
val calleeExpression = element.callExpression?.calleeExpression as? KtSimpleNameExpression
val identifier = calleeExpression?.getReferencedNameAsName()
return if (identifier == OperatorNameConventions.EQUALS || identifier == OperatorNameConventions.COMPARE_TO) {
val isFloatingPointNumberEquals = calleeExpression?.isFloatingPointNumberEquals() ?: false
return if (isFloatingPointNumberEquals) {
ProblemHighlightType.INFORMATION
} else if (identifier == OperatorNameConventions.EQUALS || identifier == OperatorNameConventions.COMPARE_TO) {
ProblemHighlightType.GENERIC_ERROR_OR_WARNING
}
else {
} else {
ProblemHighlightType.INFORMATION
}
}
@@ -84,6 +92,10 @@ class ReplaceCallWithBinaryOperatorInspection : AbstractApplicabilityBasedInspec
override fun fixText(element: KtDotQualifiedExpression): String {
val calleeExpression = element.callExpression?.calleeExpression as? KtSimpleNameExpression ?: return defaultFixText
if (calleeExpression.isFloatingPointNumberEquals()) {
return "Replace total order equality with IEEE 754 equality"
}
val operation = operation(calleeExpression) ?: return defaultFixText
return "Replace with '${operation.value}'"
}
@@ -123,7 +135,6 @@ class ReplaceCallWithBinaryOperatorInspection : AbstractApplicabilityBasedInspec
return when (identifier) {
OperatorNameConventions.EQUALS -> {
if (!dotQualified.isAnyEquals()) return null
val prefixExpression = dotQualified.getWrappingPrefixExpressionIfAny()
if (prefixExpression != null && prefixExpression.operationToken == KtTokens.EXCL) KtTokens.EXCLEQ
else KtTokens.EQEQ
@@ -148,4 +159,25 @@ class ReplaceCallWithBinaryOperatorInspection : AbstractApplicabilityBasedInspec
else -> OperatorConventions.BINARY_OPERATION_NAMES.inverse()[identifier]
}
}
}
private fun KtDotQualifiedExpression.isFloatingPointNumberEquals(): Boolean {
val resolvedCall = resolveToCall() ?: return false
val context = analyze(BodyResolveMode.PARTIAL)
val receiverType = resolvedCall.getReceiverExpression().kotlinType(context) ?: return false
val argumentType = resolvedCall.getFirstArgumentExpression().kotlinType(context) ?: return false
return (receiverType.isFpType() && argumentType.isNumericType()) || (argumentType.isFpType() && receiverType.isNumericType())
}
private fun KtSimpleNameExpression.isFloatingPointNumberEquals(): Boolean {
val dotQualified = parent.parent as? KtDotQualifiedExpression ?: return false
return dotQualified.isFloatingPointNumberEquals()
}
private fun KotlinType.isFpType(): Boolean {
return isFloat() || isDouble()
}
private fun KotlinType.isNumericType(): Boolean {
return isFpType() || isByte() || isShort() || isInt() || isLong()
}
}
@@ -0,0 +1,4 @@
// FIX: Replace total order equality with IEEE 754 equality
fun test(a: Any, b: Any) =
a is Double && b is Double && a.<caret>equals(b)
@@ -0,0 +1,4 @@
// FIX: Replace total order equality with IEEE 754 equality
fun test(a: Any, b: Any) =
a is Double && b is Double && a == b
@@ -0,0 +1,4 @@
// FIX: Replace total order equality with IEEE 754 equality
fun test(a: Any, b: Any) =
a is Float && b is Float && a.<caret>equals(b)
@@ -0,0 +1,4 @@
// FIX: Replace total order equality with IEEE 754 equality
fun test(a: Any, b: Any) =
a is Float && b is Float && a == b
@@ -0,0 +1,3 @@
// FIX: Replace total order equality with IEEE 754 equality
fun checkPi(x: Double) = x.<caret>equals(3.14)
@@ -0,0 +1,3 @@
// FIX: Replace total order equality with IEEE 754 equality
fun checkPi(x: Double) = x == 3.14
@@ -0,0 +1,3 @@
// FIX: Replace total order equality with IEEE 754 equality
fun checkPi(x: Double) = 3.14.<caret>equals(x)
@@ -0,0 +1,3 @@
// FIX: Replace total order equality with IEEE 754 equality
fun checkPi(x: Double) = 3.14 == x
@@ -0,0 +1,3 @@
// FIX: Replace total order equality with IEEE 754 equality
fun test(a: Double, b: Double) = a.<caret>equals(b)
@@ -0,0 +1,3 @@
// FIX: Replace total order equality with IEEE 754 equality
fun test(a: Double, b: Double) = a == b
@@ -1526,11 +1526,36 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
runTest("idea/testData/inspectionsLocal/conventionNameCalls/replaceCallWithBinaryOperator/equalsCompareTo.kt");
}
@TestMetadata("equalsDoubleSmartCast.kt")
public void testEqualsDoubleSmartCast() throws Exception {
runTest("idea/testData/inspectionsLocal/conventionNameCalls/replaceCallWithBinaryOperator/equalsDoubleSmartCast.kt");
}
@TestMetadata("equalsExtensionFunction.kt")
public void testEqualsExtensionFunction() throws Exception {
runTest("idea/testData/inspectionsLocal/conventionNameCalls/replaceCallWithBinaryOperator/equalsExtensionFunction.kt");
}
@TestMetadata("equalsFloatSmartCast.kt")
public void testEqualsFloatSmartCast() throws Exception {
runTest("idea/testData/inspectionsLocal/conventionNameCalls/replaceCallWithBinaryOperator/equalsFloatSmartCast.kt");
}
@TestMetadata("equalsFloatingPointLiteral1.kt")
public void testEqualsFloatingPointLiteral1() throws Exception {
runTest("idea/testData/inspectionsLocal/conventionNameCalls/replaceCallWithBinaryOperator/equalsFloatingPointLiteral1.kt");
}
@TestMetadata("equalsFloatingPointLiteral2.kt")
public void testEqualsFloatingPointLiteral2() throws Exception {
runTest("idea/testData/inspectionsLocal/conventionNameCalls/replaceCallWithBinaryOperator/equalsFloatingPointLiteral2.kt");
}
@TestMetadata("equalsFloatingPointType.kt")
public void testEqualsFloatingPointType() throws Exception {
runTest("idea/testData/inspectionsLocal/conventionNameCalls/replaceCallWithBinaryOperator/equalsFloatingPointType.kt");
}
@TestMetadata("extensionFunction.kt")
public void testExtensionFunction() throws Exception {
runTest("idea/testData/inspectionsLocal/conventionNameCalls/replaceCallWithBinaryOperator/extensionFunction.kt");