J2K: do not drop formatting for expressions with operator

This commit is contained in:
Natalia Ukhorskaya
2015-11-11 18:05:00 +03:00
parent 293b8eeda8
commit 68101eeffa
16 changed files with 180 additions and 72 deletions
@@ -116,7 +116,7 @@ class CodeConverter(
}
else if (expression is PsiPrefixExpression && expression.isLiteralWithSign()) {
val operandConverted = convertExpression(expression.getOperand(), expectedType)
convertedExpression = PrefixExpression(expression.getOperationSign().getText(), operandConverted)
convertedExpression = PrefixExpression(Operator(expression.getOperationSign().getTokenType()).assignPrototype(expression.getOperationSign()), operandConverted)
}
else {
val conversion = PRIMITIVE_TYPE_CONVERSIONS[expectedTypeStr]
@@ -379,7 +379,7 @@ class Converter private constructor(
val superExpression = SuperExpression(Identifier.Empty).assignNoPrototype()
val superAccess = QualifiedExpression(superExpression, propertyInfo.identifier).assignNoPrototype()
val valueIdentifier = Identifier("value", false).assignNoPrototype()
val assignment = AssignmentExpression(superAccess, valueIdentifier, "=").assignNoPrototype()
val assignment = AssignmentExpression(superAccess, valueIdentifier, Operator.EQ).assignNoPrototype()
val body = Block(listOf(assignment), LBrace().assignNoPrototype(), RBrace().assignNoPrototype()).assignNoPrototype()
val parameter = FunctionParameter(valueIdentifier, propertyType, FunctionParameter.VarValModifier.None, Annotations.Empty, Modifiers.Empty).assignNoPrototype()
val parameterList = ParameterList(listOf(parameter)).assignNoPrototype()
@@ -20,7 +20,6 @@ package org.jetbrains.kotlin.j2k
import com.intellij.openapi.project.Project
import com.intellij.psi.*
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.tree.IElementType
import com.intellij.psi.util.MethodSignature
import com.intellij.psi.util.MethodSignatureUtil
import com.intellij.psi.util.PsiTreeUtil
@@ -28,6 +27,7 @@ import org.jetbrains.kotlin.asJava.KtLightField
import org.jetbrains.kotlin.asJava.KtLightMethod
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.j2k.ast.*
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
@@ -85,23 +85,23 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
override fun visitAssignmentExpression(expression: PsiAssignmentExpression) {
val tokenType = expression.getOperationSign().getTokenType()
val secondOp = when(tokenType) {
JavaTokenType.GTGTEQ -> "shr"
JavaTokenType.LTLTEQ -> "shl"
JavaTokenType.XOREQ -> "xor"
JavaTokenType.ANDEQ -> "and"
JavaTokenType.OREQ -> "or"
JavaTokenType.GTGTGTEQ -> "ushr"
else -> ""
}
val lhs = codeConverter.convertExpression(expression.getLExpression())
val rhs = codeConverter.convertExpression(expression.getRExpression()!!, expression.getLExpression().getType())
if (!secondOp.isEmpty()) {
result = AssignmentExpression(lhs, BinaryExpression(lhs, rhs, secondOp).assignNoPrototype(), " = ")
val secondOp = when(tokenType) {
JavaTokenType.GTGTEQ, JavaTokenType.LTLTEQ, JavaTokenType.GTGTGTEQ,
JavaTokenType.XOREQ, JavaTokenType.OREQ,
JavaTokenType.ANDEQ -> true
else -> false
}
val operator = Operator(tokenType).assignPrototype(expression.operationSign)
if (secondOp) {
result = AssignmentExpression(lhs, BinaryExpression(lhs, rhs, operator).assignNoPrototype(), Operator.EQ)
}
else {
result = AssignmentExpression(lhs, rhs, expression.getOperationSign().getText()!!)
result = AssignmentExpression(lhs, rhs, operator)
}
}
@@ -121,13 +121,13 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
result = MethodCallExpression.buildNotNull(leftConverted, "ushr", listOf(rightConverted))
}
else {
var operatorString = getOperatorString(operationTokenType)
var operator = Operator(operationTokenType)
if (operationTokenType == JavaTokenType.EQEQ || operationTokenType == JavaTokenType.NE) {
if (!canKeepEqEq(left, right)) {
operatorString += "="
operator = if (operationTokenType == JavaTokenType.EQEQ) Operator(KtTokens.EQEQEQ) else Operator(KtTokens.EXCLEQEQEQ)
}
}
result = BinaryExpression(leftConverted, rightConverted, operatorString)
result = BinaryExpression(leftConverted, rightConverted, operator.assignPrototype(expression.operationSign))
}
}
@@ -283,7 +283,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
1 /* setter */ -> {
val argument = codeConverter.convertExpression(arguments[if (isExtension) 1 else 0])
result = AssignmentExpression(propertyAccess, argument, "=")
result = AssignmentExpression(propertyAccess, argument, Operator.EQ)
return
}
}
@@ -383,7 +383,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
}
override fun visitPostfixExpression(expression: PsiPostfixExpression) {
result = PostfixExpression(getOperatorString(expression.getOperationSign().getTokenType()),
result = PostfixExpression(Operator(expression.getOperationSign().getTokenType()).assignPrototype(expression.operationSign),
codeConverter.convertExpression(expression.getOperand()))
}
@@ -393,11 +393,11 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
if (token == JavaTokenType.TILDE) {
result = MethodCallExpression.buildNotNull(operand, "inv")
}
else if (token == JavaTokenType.EXCL && operand is BinaryExpression && operand.op == "==") { // happens when equals is converted to ==
result = BinaryExpression(operand.left, operand.right, "!=")
else if (token == JavaTokenType.EXCL && operand is BinaryExpression && operand.op.asString() == "==") { // happens when equals is converted to ==
result = BinaryExpression(operand.left, operand.right, Operator(JavaTokenType.NE).assignPrototype(expression.operand))
}
else {
result = PrefixExpression(getOperatorString(token), operand)
result = PrefixExpression(Operator(token).assignPrototype(expression.operand), operand)
}
}
@@ -499,8 +499,17 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
}
override fun visitPolyadicExpression(expression: PsiPolyadicExpression) {
val args = expression.getOperands().map { codeConverter.convertExpression(it, expression.getType()) }
result = PolyadicExpression(args, getOperatorString(expression.getOperationTokenType()))
val commentsAndSpacesInheritance = CommentsAndSpacesInheritance.LINE_BREAKS
val args = expression.getOperands().map {
codeConverter.convertExpression(it, expression.getType()).assignPrototype(it, commentsAndSpacesInheritance)
}
val operators = expression.getOperands().map {
expression.getTokenBeforeOperand(it)?.let {
Operator(it.tokenType).assignPrototype(it, commentsAndSpacesInheritance)
}
}.filterNotNull()
result = PolyadicExpression(args, operators).assignPrototype(expression)
}
private fun convertArguments(expression: PsiCallExpression, isExtension: Boolean = false): List<Expression> {
@@ -530,34 +539,6 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
}
}
private fun getOperatorString(tokenType: IElementType): String {
return when(tokenType) {
JavaTokenType.EQEQ -> "=="
JavaTokenType.NE -> "!="
JavaTokenType.ANDAND -> "&&"
JavaTokenType.OROR -> "||"
JavaTokenType.GT -> ">"
JavaTokenType.LT -> "<"
JavaTokenType.GE -> ">="
JavaTokenType.LE -> "<="
JavaTokenType.EXCL -> "!"
JavaTokenType.PLUS -> "+"
JavaTokenType.MINUS -> "-"
JavaTokenType.ASTERISK -> "*"
JavaTokenType.DIV -> "/"
JavaTokenType.PERC -> "%"
JavaTokenType.GTGT -> "shr"
JavaTokenType.LTLT -> "shl"
JavaTokenType.XOR -> "xor"
JavaTokenType.AND -> "and"
JavaTokenType.OR -> "or"
JavaTokenType.GTGTGT -> "ushr"
JavaTokenType.PLUSPLUS -> "++"
JavaTokenType.MINUSMINUS -> "--"
else -> "" //System.out.println("UNSUPPORTED TOKEN TYPE: " + tokenType?.toString())
}
}
override fun visitLambdaExpression(expression: PsiLambdaExpression) {
val parameters = expression.parameterList
val convertedParameters = ParameterList(parameters.parameters.map {
@@ -270,8 +270,8 @@ class ForConverter(
}
val converted = codeConverter.convertExpression(bound)
val sign = if (correction > 0) "+" else "-"
return BinaryExpression(converted, LiteralExpression(Math.abs(correction).toString()).assignNoPrototype(), sign).assignNoPrototype()
val sign = if (correction > 0) JavaTokenType.PLUS else JavaTokenType.MINUS
return BinaryExpression(converted, LiteralExpression(Math.abs(correction).toString()).assignNoPrototype(), Operator(sign).assignPrototype(bound)).assignNoPrototype()
}
private fun PsiStatement.toContinuedLoop(): PsiLoopStatement? {
@@ -135,7 +135,7 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String
override fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, codeConverter: CodeConverter): Expression? {
if (qualifier == null || qualifier is PsiSuperExpression) return null
return BinaryExpression(codeConverter.convertExpression(qualifier), codeConverter.convertExpression(arguments.single()), "==")
return BinaryExpression(codeConverter.convertExpression(qualifier), codeConverter.convertExpression(arguments.single()), Operator.EQEQ)
}
},
@@ -148,7 +148,7 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String
OBJECTS_EQUALS("java.util.Objects", "equals", 2) {
override fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, codeConverter: CodeConverter)
= BinaryExpression(codeConverter.convertExpression(arguments[0]), codeConverter.convertExpression(arguments[1]), "==")
= BinaryExpression(codeConverter.convertExpression(arguments[0]), codeConverter.convertExpression(arguments[1]), Operator.EQEQ)
},
COLLECTIONS_EMPTY_LIST(Collections::class.java.name, "emptyList", 0) {
@@ -178,7 +178,7 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String
STRING_TRIM(JAVA_LANG_STRING, "trim", 0) {
override fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, codeConverter: CodeConverter): Expression? {
val comparison = BinaryExpression(Identifier("it", isNullable = false).assignNoPrototype(), LiteralExpression("' '").assignNoPrototype(), "<=").assignNoPrototype()
val comparison = BinaryExpression(Identifier("it", isNullable = false).assignNoPrototype(), LiteralExpression("' '").assignNoPrototype(), Operator(JavaTokenType.LE).assignNoPrototype()).assignNoPrototype()
return MethodCallExpression.buildNotNull(
codeConverter.convertExpression(qualifier), "trim",
listOf(LambdaExpression(null, Block.of(comparison).assignNoPrototype())), emptyList())
@@ -423,7 +423,7 @@ private fun addIgnoreCaseArgument(
ignoreCaseArgument: PsiExpression? = null
): Expression {
val ignoreCaseExpression = ignoreCaseArgument?.let { codeConverter.convertExpression(it) } ?: LiteralExpression("true").assignNoPrototype()
val ignoreCaseArgumentExpression = AssignmentExpression(Identifier("ignoreCase").assignNoPrototype(), ignoreCaseExpression, "=").assignNoPrototype()
val ignoreCaseArgumentExpression = AssignmentExpression(Identifier("ignoreCase").assignNoPrototype(), ignoreCaseExpression, Operator.EQ).assignNoPrototype()
return MethodCallExpression.build(codeConverter.convertExpression(qualifier), methodName,
codeConverter.convertExpressions(arguments) + ignoreCaseArgumentExpression,
typeArgumentsConverted, false)
@@ -16,8 +16,11 @@
package org.jetbrains.kotlin.j2k.ast
import com.intellij.psi.JavaTokenType
import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.j2k.CodeBuilder
import org.jetbrains.kotlin.j2k.append
import org.jetbrains.kotlin.lexer.KtTokens
class ArrayAccessExpression(val expression: Expression, val index: Expression, val lvalue: Boolean) : Expression() {
override fun generateCode(builder: CodeBuilder) {
@@ -27,7 +30,7 @@ class ArrayAccessExpression(val expression: Expression, val index: Expression, v
}
}
class AssignmentExpression(val left: Expression, val right: Expression, val op: String) : Expression() {
class AssignmentExpression(val left: Expression, val right: Expression, val op: Operator) : Expression() {
override fun generateCode(builder: CodeBuilder) {
builder.appendOperand(this, left).append(" ").append(op).append(" ").appendOperand(this, right)
}
@@ -48,7 +51,7 @@ class BangBangExpression(val expr: Expression) : Expression() {
}
}
class BinaryExpression(val left: Expression, val right: Expression, val op: String) : Expression() {
class BinaryExpression(val left: Expression, val right: Expression, val op: Operator) : Expression() {
override fun generateCode(builder: CodeBuilder) {
builder.appendOperand(this, left, false).append(" ").append(op).append(" ").appendOperand(this, right, true)
}
@@ -78,7 +81,7 @@ class ParenthesizedExpression(val expression: Expression) : Expression() {
}
}
class PrefixExpression(val op: String, val expression: Expression) : Expression() {
class PrefixExpression(val op: Operator, val expression: Expression) : Expression() {
override fun generateCode(builder: CodeBuilder){
builder.append(op).appendOperand(this, expression)
}
@@ -87,7 +90,7 @@ class PrefixExpression(val op: String, val expression: Expression) : Expression(
get() = expression.isNullable
}
class PostfixExpression(val op: String, val expression: Expression) : Expression() {
class PostfixExpression(val op: Operator, val expression: Expression) : Expression() {
override fun generateCode(builder: CodeBuilder) {
builder.appendOperand(this, expression) append op
}
@@ -118,9 +121,72 @@ class QualifiedExpression(val qualifier: Expression, val identifier: Expression)
}
}
class PolyadicExpression(val expressions: List<Expression>, val token: String) : Expression() {
class PolyadicExpression(val expressions: List<Expression>, val operators: List<Operator>) : Expression() {
override fun generateCode(builder: CodeBuilder) {
builder.append(expressions, " " + token + " ")
assert(expressions.size == operators.size + 1)
for ((i, expression) in expressions.withIndex()) {
builder.append(expression)
if (i < operators.size) {
builder.append(" ")
builder.append(operators[i])
builder.append(" ")
}
}
}
}
open class Operator(val operatorType: IElementType): Expression() {
override fun generateCode(builder: CodeBuilder) {
builder.append(asString(operatorType))
}
fun asString() = asString(operatorType)
private fun asString(tokenType: IElementType): String {
return when(tokenType) {
JavaTokenType.EQ -> "="
JavaTokenType.EQEQ -> "=="
JavaTokenType.NE -> "!="
JavaTokenType.ANDAND -> "&&"
JavaTokenType.OROR -> "||"
JavaTokenType.GT -> ">"
JavaTokenType.LT -> "<"
JavaTokenType.GE -> ">="
JavaTokenType.LE -> "<="
JavaTokenType.EXCL -> "!"
JavaTokenType.PLUS -> "+"
JavaTokenType.MINUS -> "-"
JavaTokenType.ASTERISK -> "*"
JavaTokenType.DIV -> "/"
JavaTokenType.PERC -> "%"
JavaTokenType.PLUSEQ -> "+="
JavaTokenType.MINUSEQ -> "-="
JavaTokenType.ASTERISKEQ -> "*="
JavaTokenType.DIVEQ -> "/="
JavaTokenType.PERCEQ -> "%="
JavaTokenType.GTGT -> "shr"
JavaTokenType.LTLT -> "shl"
JavaTokenType.XOR -> "xor"
JavaTokenType.AND -> "and"
JavaTokenType.OR -> "or"
JavaTokenType.GTGTGT -> "ushr"
JavaTokenType.GTGTEQ -> "shr"
JavaTokenType.LTLTEQ -> "shl"
JavaTokenType.XOREQ -> "xor"
JavaTokenType.ANDEQ -> "and"
JavaTokenType.OREQ -> "or"
JavaTokenType.GTGTGTEQ -> "ushr"
JavaTokenType.PLUSPLUS -> "++"
JavaTokenType.MINUSMINUS -> "--"
KtTokens.EQEQEQ -> "==="
KtTokens.EXCLEQEQEQ -> "!=="
else -> "" //System.out.println("UNSUPPORTED TOKEN TYPE: " + tokenType?.toString())
}
}
companion object {
val EQEQ = Operator(JavaTokenType.EQEQ).assignNoPrototype()
val EQ = Operator(JavaTokenType.EQ).assignNoPrototype()
}
}
+2 -2
View File
@@ -16,7 +16,7 @@
package org.jetbrains.kotlin.j2k.ast
import org.jetbrains.kotlin.j2k.*
import org.jetbrains.kotlin.j2k.CodeBuilder
fun String.withSuffix(suffix: String): String = if (isEmpty()) "" else this + suffix
fun String.withPrefix(prefix: String): String = if (isEmpty()) "" else prefix + this
@@ -43,7 +43,7 @@ private fun Expression.precedence(): Int? {
is TypeCastExpression -> 2
is BinaryExpression -> when(op) {
is BinaryExpression -> when(op.asString()) {
"*", "/", "%" -> 3
"+", "-" -> 4
"?:" -> 7
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.j2k.usageProcessing
import com.intellij.psi.*
import org.jetbrains.kotlin.j2k.AccessorKind
import org.jetbrains.kotlin.j2k.CodeConverter
import org.jetbrains.kotlin.j2k.ResolverForConverter
import org.jetbrains.kotlin.j2k.ast.*
import org.jetbrains.kotlin.psi.*
@@ -43,7 +42,7 @@ class AccessorToPropertyProcessing(val accessorMethod: PsiMethod, val accessorKi
else {
if (arguments.size() != 1) return null // incorrect call
val argument = codeConverter.convertExpression(arguments[0])
return AssignmentExpression(propertyAccess, argument, "=")
return AssignmentExpression(propertyAccess, argument, Operator.EQ)
}
}
}
+2 -1
View File
@@ -2,6 +2,7 @@ internal enum class Color : Runnable {
WHITE, BLACK, RED, YELLOW, BLUE;
override fun run() {
println("name()=" + name + ", toString()=" + toString())
println("name()=" + name +
", toString()=" + toString())
}
}
@@ -1,6 +1,7 @@
//file
import kotlinApi.*;
//TODO: Formatter works incorrectly
class A {
int foo(KotlinClass c) {
return c.getNullableProperty().length()
@@ -1,7 +1,15 @@
import kotlinApi.*
//TODO: Formatter works incorrectly
internal class A {
fun foo(c: KotlinClass): Int {
return c.nullableProperty!!.length + c.property.length + KotlinClass.nullableStaticVar!! + KotlinClass.staticVar + KotlinClass.nullableStaticFun(1)!! + KotlinClass.staticFun(1) + nullableGlobalFunction("")!!.length + globalFunction("").length
return c.nullableProperty!!.length
+c.property.length
+KotlinClass.nullableStaticVar!!
+KotlinClass.staticVar
+KotlinClass.nullableStaticFun(1)!!
+KotlinClass.staticFun(1)
+nullableGlobalFunction("")!!.length
+globalFunction("").length
}
}
@@ -4,6 +4,8 @@ internal class C {
fun foo(): Int {
KotlinObject.property1 = 1
KotlinObject.property2 = 2
return KotlinObject.foo() + KotlinObject.property1 + KotlinObject.property2
return KotlinObject.foo() +
KotlinObject.property1 +
KotlinObject.property2
}
}
+10
View File
@@ -0,0 +1,10 @@
public class A {
public static final String TEXT1 = "text1.\n" +
"text2\n" +
"text3";
public static final String TEXT2 = "text1\n"
+ "text2\n"
+ "text3";
}
+10
View File
@@ -0,0 +1,10 @@
object A {
val TEXT1 = "text1.\n" +
"text2\n" +
"text3"
val TEXT2 = "text1\n"
+ "text2\n"
+ "text3"
}
@@ -3941,6 +3941,21 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
}
}
@TestMetadata("j2k/testData/fileOrElement/strings")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Strings extends AbstractJavaToKotlinConverterForWebDemoTest {
public void testAllFilesPresentInStrings() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("j2k/testData/fileOrElement/strings"), Pattern.compile("^(.+)\\.java$"), true);
}
@TestMetadata("formattedString.java")
public void testFormattedString() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/strings/formattedString.java");
doTest(fileName);
}
}
@TestMetadata("j2k/testData/fileOrElement/superExpression")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -3941,6 +3941,21 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
}
}
@TestMetadata("j2k/testData/fileOrElement/strings")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Strings extends AbstractJavaToKotlinConverterSingleFileTest {
public void testAllFilesPresentInStrings() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("j2k/testData/fileOrElement/strings"), Pattern.compile("^(.+)\\.java$"), true);
}
@TestMetadata("formattedString.java")
public void testFormattedString() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/strings/formattedString.java");
doTest(fileName);
}
}
@TestMetadata("j2k/testData/fileOrElement/superExpression")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)