diff --git a/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt b/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt index faae5f1ed6d..ff6f88539f5 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt @@ -349,8 +349,7 @@ class Converter private( if (specifyAlways) return convertType() val initializer = variable.getInitializer() - if (initializer == null) return convertType() - if (initializer is PsiLiteralExpression && initializer.getType() == PsiType.NULL) return convertType() + if (initializer == null || initializer.isNullLiteral()) return convertType() if (canChangeType) return null diff --git a/j2k/src/org/jetbrains/kotlin/j2k/ExpressionConverter.kt b/j2k/src/org/jetbrains/kotlin/j2k/ExpressionConverter.kt index f695b4e8c93..123bce71e6b 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/ExpressionConverter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/ExpressionConverter.kt @@ -16,12 +16,11 @@ package org.jetbrains.kotlin.j2k +import com.intellij.codeInsight.generation.GenerateEqualsHelper import com.intellij.psi.* -import com.intellij.psi.CommonClassNames.JAVA_LANG_DOUBLE -import com.intellij.psi.CommonClassNames.JAVA_LANG_FLOAT -import com.intellij.psi.CommonClassNames.JAVA_LANG_INTEGER -import com.intellij.psi.CommonClassNames.JAVA_LANG_LONG +import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.tree.IElementType +import com.intellij.psi.util.MethodSignatureUtil import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.asJava.KotlinLightField import org.jetbrains.kotlin.asJava.KotlinLightMethod @@ -32,7 +31,6 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.isExtensionDeclaration import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType -import org.jetbrains.kotlin.types.expressions.OperatorConventions trait ExpressionConverter { fun convertExpression(expression: PsiExpression, codeConverter: CodeConverter): Expression @@ -105,21 +103,53 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter { } override fun visitBinaryExpression(expression: PsiBinaryExpression) { - var lhs = codeConverter.convertExpression(expression.getLOperand(), null) - var rhs = codeConverter.convertExpression(expression.getROperand(), null) + val left = expression.getLOperand() + val right = expression.getROperand() + var leftConverted = codeConverter.convertExpression(left, null) + var rightConverted = codeConverter.convertExpression(right, null) - if (expression.getOperationTokenType() in NON_NULL_OPERAND_OPS) { - lhs = BangBangExpression.surroundIfNullable(lhs) - rhs = BangBangExpression.surroundIfNullable(rhs) + val operationTokenType = expression.getOperationTokenType() + if (operationTokenType in NON_NULL_OPERAND_OPS) { + leftConverted = BangBangExpression.surroundIfNullable(leftConverted) + rightConverted = BangBangExpression.surroundIfNullable(rightConverted) } - if (expression.getOperationTokenType() == JavaTokenType.GTGTGT) { - result = MethodCallExpression.buildNotNull(lhs, "ushr", listOf(rhs)) + + if (operationTokenType == JavaTokenType.GTGTGT) { + result = MethodCallExpression.buildNotNull(leftConverted, "ushr", listOf(rightConverted)) } else { - result = BinaryExpression(lhs, rhs, getOperatorString(expression.getOperationSign().getTokenType())) + var operatorString = getOperatorString(operationTokenType) + if (operationTokenType == JavaTokenType.EQEQ || operationTokenType == JavaTokenType.NE) { + if (!canKeepEqEq(left, right)) { + operatorString += "=" + } + } + result = BinaryExpression(leftConverted, rightConverted, operatorString) } } + private fun canKeepEqEq(left: PsiExpression, right: PsiExpression?): Boolean { + if (left.isNullLiteral() || (right?.isNullLiteral() ?: false)) return true + val type = left.getType() + when (type) { + is PsiPrimitiveType, is PsiArrayType -> return true + + is PsiClassType -> { + val psiClass = type.resolve() ?: return false + if (!psiClass.hasModifierProperty(PsiModifier.FINAL)) return false + + val equalsSignature = GenerateEqualsHelper.getEqualsSignature(converter.project, GlobalSearchScope.allScope(converter.project)) + val equalsMethod = MethodSignatureUtil.findMethodBySignature(psiClass, equalsSignature, true) + if (equalsMethod != null && equalsMethod.getContainingClass()?.getQualifiedName() != CommonClassNames.JAVA_LANG_OBJECT) return false + + return true + } + + else -> return false + } + + } + private val NON_NULL_OPERAND_OPS = setOf( JavaTokenType.ANDAND, JavaTokenType.OROR, diff --git a/j2k/src/org/jetbrains/kotlin/j2k/TypeConverter.kt b/j2k/src/org/jetbrains/kotlin/j2k/TypeConverter.kt index fff171d31f9..e2f12cf2b70 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/TypeConverter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/TypeConverter.kt @@ -291,7 +291,7 @@ class TypeConverter(val converter: Converter) { val operationType = parent.getOperationTokenType() if (operationType == JavaTokenType.EQEQ || operationType == JavaTokenType.NE) { val otherOperand = if (usage == parent.getLOperand()) parent.getROperand() else parent.getLOperand() - return otherOperand is PsiLiteralExpression && otherOperand.getType() == PsiType.NULL + return otherOperand?.isNullLiteral() ?: false } } else if (parent is PsiVariable && usage == parent.getInitializer() && parent.isEffectivelyFinal()) { diff --git a/j2k/src/org/jetbrains/kotlin/j2k/Utils.kt b/j2k/src/org/jetbrains/kotlin/j2k/Utils.kt index c7bf2db8f5b..339e0a7beb2 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/Utils.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/Utils.kt @@ -141,3 +141,4 @@ fun PsiMember.isImported(file: PsiJavaFile): Boolean { } } +fun PsiExpression.isNullLiteral() = this is PsiLiteralExpression && getType() == PsiType.NULL diff --git a/j2k/testData/fileOrElement/equals/EqOperator.java b/j2k/testData/fileOrElement/equals/EqOperator.java new file mode 100644 index 00000000000..b531b011f7f --- /dev/null +++ b/j2k/testData/fileOrElement/equals/EqOperator.java @@ -0,0 +1,39 @@ +interface I{} + +final class C{} + +class O{} + +final class E { + @Override + public boolean equals(Object o) { + return super.equals(o); + } +} + +class B { + @Override + public boolean equals(Object o) { + return super.equals(o); + } +} + +final class BB extends B {} + +class X { + void foo(I i1, I i2, String s1, String s2, C c1, C c2, int i, O o1, O o2, E e1, E e2, BB bb1, BB bb2, int[] arr1, int[] arr2) { + if (i1 == i2) return; + if (s1 == s2) return; + if (c1 == c2) return; + if (i1 == null) return; + if (null == i2) return; + if (i == 0) return; + if (o1 == o2) return; + if (e1 == e2) return; + if (bb1 == bb2) return; + if (arr1 == arr2) return; + + if (s1 != s2) return; + if (c1 != c2) return; + } +} diff --git a/j2k/testData/fileOrElement/equals/EqOperator.kt b/j2k/testData/fileOrElement/equals/EqOperator.kt new file mode 100644 index 00000000000..a4c061231cd --- /dev/null +++ b/j2k/testData/fileOrElement/equals/EqOperator.kt @@ -0,0 +1,37 @@ +interface I + +class C + +class O + +class E { + override fun equals(o: Any?): Boolean { + return super.equals(o) + } +} + +open class B { + override fun equals(o: Any?): Boolean { + return super.equals(o) + } +} + +class BB : B() + +class X { + fun foo(i1: I?, i2: I?, s1: String, s2: String, c1: C, c2: C, i: Int, o1: O, o2: O, e1: E, e2: E, bb1: BB, bb2: BB, arr1: IntArray, arr2: IntArray) { + if (i1 === i2) return + if (s1 === s2) return + if (c1 == c2) return + if (i1 == null) return + if (null == i2) return + if (i == 0) return + if (o1 === o2) return + if (e1 === e2) return + if (bb1 === bb2) return + if (arr1 == arr2) return + + if (s1 !== s2) return + if (c1 != c2) return + } +} diff --git a/j2k/testData/fileOrElement/labelStatement/complicatedExampleFromJavaTutorial.kt b/j2k/testData/fileOrElement/labelStatement/complicatedExampleFromJavaTutorial.kt index b702f29e43c..17d8857b5ff 100644 --- a/j2k/testData/fileOrElement/labelStatement/complicatedExampleFromJavaTutorial.kt +++ b/j2k/testData/fileOrElement/labelStatement/complicatedExampleFromJavaTutorial.kt @@ -3,7 +3,7 @@ test@ for (i in 0..max) { var j = i var k = 0 while (n-- != 0) { - if (searchMe.charAt(j++) != substring.charAt(k++)) { + if (searchMe.charAt(j++) !== substring.charAt(k++)) { continue@test } } diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java index 40193f89d91..aaa0fa2214b 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java +++ b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java @@ -1729,6 +1729,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("j2k/testData/fileOrElement/equals"), Pattern.compile("^(.+)\\.java$"), true); } + @TestMetadata("EqOperator.java") + public void testEqOperator() throws Exception { + String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/equals/EqOperator.java"); + doTest(fileName); + } + @TestMetadata("Equals1.java") public void testEquals1() throws Exception { String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/equals/Equals1.java"); diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java index 5cc160c0b9c..536b309d158 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java +++ b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java @@ -1729,6 +1729,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("j2k/testData/fileOrElement/equals"), Pattern.compile("^(.+)\\.java$"), true); } + @TestMetadata("EqOperator.java") + public void testEqOperator() throws Exception { + String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/equals/EqOperator.java"); + doTest(fileName); + } + @TestMetadata("Equals1.java") public void testEquals1() throws Exception { String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/equals/Equals1.java");