KT-13628 New line is not preserved when converting java to Kotlin

#KT-13628 Fixed
This commit is contained in:
Valentin Kipyatkov
2016-10-01 00:25:55 +03:00
parent 8fe57c8e84
commit badbcd4989
16 changed files with 91 additions and 23 deletions
@@ -132,7 +132,8 @@ class AnnotationConverter(private val converter: Converter) {
val name = it.name val name = it.name
null to converter.deferredElement<Expression> { null to converter.deferredElement<Expression> {
QualifiedExpression(Identifier.withNoPrototype("AnnotationTarget", isNullable = false), QualifiedExpression(Identifier.withNoPrototype("AnnotationTarget", isNullable = false),
Identifier.withNoPrototype(name, isNullable = false)) Identifier.withNoPrototype(name, isNullable = false),
null)
} }
} }
return Identifier.withNoPrototype("Target") to deferredExpressionList return Identifier.withNoPrototype("Target") to deferredExpressionList
@@ -359,7 +359,7 @@ class Converter private constructor(
} }
else if (propertyInfo.modifiers.contains(Modifier.OVERRIDE) && !(propertyInfo.superInfo?.isAbstract() ?: false)) { else if (propertyInfo.modifiers.contains(Modifier.OVERRIDE) && !(propertyInfo.superInfo?.isAbstract() ?: false)) {
val superExpression = SuperExpression(Identifier.Empty).assignNoPrototype() val superExpression = SuperExpression(Identifier.Empty).assignNoPrototype()
val superAccess = QualifiedExpression(superExpression, propertyInfo.identifier).assignNoPrototype() val superAccess = QualifiedExpression(superExpression, propertyInfo.identifier, null).assignNoPrototype()
val returnStatement = ReturnStatement(superAccess).assignNoPrototype() val returnStatement = ReturnStatement(superAccess).assignNoPrototype()
val body = Block.of(returnStatement).assignNoPrototype() val body = Block.of(returnStatement).assignNoPrototype()
val parameterList = ParameterList.withNoPrototype(emptyList()) val parameterList = ParameterList.withNoPrototype(emptyList())
@@ -396,7 +396,7 @@ class Converter private constructor(
} }
else if (propertyInfo.modifiers.contains(Modifier.OVERRIDE) && !(propertyInfo.superInfo?.isAbstract() ?: false)) { else if (propertyInfo.modifiers.contains(Modifier.OVERRIDE) && !(propertyInfo.superInfo?.isAbstract() ?: false)) {
val superExpression = SuperExpression(Identifier.Empty).assignNoPrototype() val superExpression = SuperExpression(Identifier.Empty).assignNoPrototype()
val superAccess = QualifiedExpression(superExpression, propertyInfo.identifier).assignNoPrototype() val superAccess = QualifiedExpression(superExpression, propertyInfo.identifier, null).assignNoPrototype()
val valueIdentifier = Identifier.withNoPrototype("value", isNullable = false) val valueIdentifier = Identifier.withNoPrototype("value", isNullable = false)
val assignment = AssignmentExpression(superAccess, valueIdentifier, Operator.EQ).assignNoPrototype() val assignment = AssignmentExpression(superAccess, valueIdentifier, Operator.EQ).assignNoPrototype()
val body = Block.of(assignment).assignNoPrototype() val body = Block.of(assignment).assignNoPrototype()
@@ -242,7 +242,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
} }
else { else {
val type = converter.convertTypeElement(operand, Nullability.NotNull) val type = converter.convertTypeElement(operand, Nullability.NotNull)
result = QualifiedExpression(ClassLiteralExpression(type).assignNoPrototype(), Identifier.withNoPrototype("java")) result = QualifiedExpression(ClassLiteralExpression(type).assignNoPrototype(), Identifier.withNoPrototype("java"), null)
return return
} }
@@ -253,7 +253,8 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
else else
wrapperTypeName.shortName().asString() wrapperTypeName.shortName().asString()
result = QualifiedExpression(Identifier(classNameToUse, false).assignPrototype(operand), result = QualifiedExpression(Identifier(classNameToUse, false).assignPrototype(operand),
Identifier.withNoPrototype("TYPE", isNullable = false)) Identifier.withNoPrototype("TYPE", isNullable = false),
null)
} }
override fun visitConditionalExpression(expression: PsiConditionalExpression) { override fun visitConditionalExpression(expression: PsiConditionalExpression) {
@@ -322,6 +323,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
override fun visitMethodCallExpression(expression: PsiMethodCallExpression) { override fun visitMethodCallExpression(expression: PsiMethodCallExpression) {
val methodExpr = expression.methodExpression val methodExpr = expression.methodExpression
val arguments = expression.argumentList.expressions val arguments = expression.argumentList.expressions
val qualifier = methodExpr.qualifierExpression
val target = methodExpr.resolve() val target = methodExpr.resolve()
val isNullable = if (target is PsiMethod) typeConverter.methodNullability(target).isNullable(codeConverter.settings) else false val isNullable = if (target is PsiMethod) typeConverter.methodNullability(target).isNullable(codeConverter.settings) else false
val typeArguments = convertTypeArguments(expression) val typeArguments = convertTypeArguments(expression)
@@ -340,12 +342,15 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
val isExtension = property.isExtensionDeclaration() val isExtension = property.isExtensionDeclaration()
val propertyAccess = if (isTopLevel) { val propertyAccess = if (isTopLevel) {
if (isExtension) if (isExtension)
QualifiedExpression(codeConverter.convertExpression(arguments.firstOrNull(), true), propertyName).assignNoPrototype() QualifiedExpression(codeConverter.convertExpression(arguments.firstOrNull(), true), propertyName, null).assignNoPrototype()
else else
propertyName propertyName
} }
else if (qualifier != null) {
QualifiedExpression(codeConverter.convertExpression(qualifier), propertyName, methodExpr.dot()).assignNoPrototype()
}
else { else {
QualifiedExpression(codeConverter.convertExpression(methodExpr.qualifierExpression), propertyName).assignNoPrototype() propertyName
} }
when(if (isExtension) parameterCount - 1 else parameterCount) { when(if (isExtension) parameterCount - 1 else parameterCount) {
@@ -408,7 +413,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
if (target is PsiMethod) { if (target is PsiMethod) {
val specialMethod = SpecialMethod.match(target, arguments.size, converter.services) val specialMethod = SpecialMethod.match(target, arguments.size, converter.services)
if (specialMethod != null) { if (specialMethod != null) {
val converted = specialMethod.convertCall(methodExpr.qualifierExpression, arguments, typeArguments, codeConverter) val converted = specialMethod.convertCall(qualifier, arguments, typeArguments, codeConverter)
if (converted != null) { if (converted != null) {
result = converted result = converted
return return
@@ -542,7 +547,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
expression.getContainingClass()?.getParentOfType<PsiVariable>(false)?.let { expression.getContainingClass()?.getParentOfType<PsiVariable>(false)?.let {
if (it == expression.qualifierExpression?.reference?.resolve()) { if (it == expression.qualifierExpression?.reference?.resolve()) {
result = QualifiedExpression(ThisExpression(Identifier.Empty).assignNoPrototype(), identifier) result = QualifiedExpression(ThisExpression(Identifier.Empty).assignNoPrototype(), identifier, null)
return return
} }
} }
@@ -583,7 +588,10 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
} }
} }
result = if (qualifier != null) QualifiedExpression(codeConverter.convertExpression(qualifier), identifier) else identifier result = if (qualifier != null)
QualifiedExpression(codeConverter.convertExpression(qualifier), identifier, expression.dot())
else
identifier
} }
private fun isNullable(target: PsiVariable): Boolean { private fun isNullable(target: PsiVariable): Boolean {
@@ -240,7 +240,7 @@ class ForConverter(
val collectionType = PsiElementFactory.SERVICE.getInstance(project).createTypeByFQClassName(CommonClassNames.JAVA_UTIL_COLLECTION) val collectionType = PsiElementFactory.SERVICE.getInstance(project).createTypeByFQClassName(CommonClassNames.JAVA_UTIL_COLLECTION)
val qualifierType = qualifier.type val qualifierType = qualifier.type
if (qualifierType != null && collectionType.isAssignableFrom(qualifierType)) { if (qualifierType != null && collectionType.isAssignableFrom(qualifierType)) {
indices = QualifiedExpression(codeConverter.convertExpression(qualifier), Identifier.withNoPrototype("indices", isNullable = false)) indices = QualifiedExpression(codeConverter.convertExpression(qualifier), Identifier.withNoPrototype("indices", isNullable = false), null)
} }
} }
} }
@@ -250,7 +250,7 @@ class ForConverter(
&& collectionSize.referenceName == "length") { && collectionSize.referenceName == "length") {
val qualifier = collectionSize.qualifierExpression val qualifier = collectionSize.qualifierExpression
if (qualifier is PsiReferenceExpression && qualifier.type is PsiArrayType) { if (qualifier is PsiReferenceExpression && qualifier.type is PsiArrayType) {
indices = QualifiedExpression(codeConverter.convertExpression(qualifier), Identifier.withNoPrototype("indices", isNullable = false)) indices = QualifiedExpression(codeConverter.convertExpression(qualifier), Identifier.withNoPrototype("indices", isNullable = false), null)
} }
} }
@@ -197,7 +197,10 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String
OBJECT_GET_CLASS(JAVA_LANG_OBJECT, "getClass", 0) { OBJECT_GET_CLASS(JAVA_LANG_OBJECT, "getClass", 0) {
override fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, codeConverter: CodeConverter): Expression { override fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, codeConverter: CodeConverter): Expression {
val identifier = Identifier.withNoPrototype("javaClass", isNullable = false) val identifier = Identifier.withNoPrototype("javaClass", isNullable = false)
return if (qualifier != null) QualifiedExpression(codeConverter.convertExpression(qualifier), identifier) else identifier return if (qualifier != null)
QualifiedExpression(codeConverter.convertExpression(qualifier), identifier, null)
else
identifier
} }
}, },
@@ -436,7 +439,10 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String
protected fun convertMethodCallToPropertyUse(codeConverter: CodeConverter, qualifier: PsiExpression?, propertyName: String = methodName): Expression { protected fun convertMethodCallToPropertyUse(codeConverter: CodeConverter, qualifier: PsiExpression?, propertyName: String = methodName): Expression {
val identifier = Identifier.withNoPrototype(propertyName, isNullable = false) val identifier = Identifier.withNoPrototype(propertyName, isNullable = false)
return if (qualifier != null) QualifiedExpression(codeConverter.convertExpression(qualifier), identifier) else identifier return if (qualifier != null)
QualifiedExpression(codeConverter.convertExpression(qualifier), identifier, null)
else
identifier
} }
protected fun Array<PsiExpression>.notNull() = map { it to Nullability.NotNull } protected fun Array<PsiExpression>.notNull() = map { it to Nullability.NotNull }
@@ -21,6 +21,7 @@ import com.intellij.psi.util.PsiMethodUtil
import org.jetbrains.kotlin.asJava.classes.KtLightClass import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.j2k.ast.* import org.jetbrains.kotlin.j2k.ast.*
import org.jetbrains.kotlin.psi.psiUtil.allChildren
import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.types.expressions.OperatorConventions
fun quoteKeywords(packageName: String): String = packageName.split('.').map { Identifier.toKotlin(it) }.joinToString(".") fun quoteKeywords(packageName: String): String = packageName.split('.').map { Identifier.toKotlin(it) }.joinToString(".")
@@ -110,6 +111,8 @@ fun PsiModifierListOwner.accessModifier(): String = when {
fun PsiMethod.isMainMethod(): Boolean = PsiMethodUtil.isMainMethod(this) fun PsiMethod.isMainMethod(): Boolean = PsiMethodUtil.isMainMethod(this)
fun PsiReferenceExpression.dot(): PsiElement? = allChildren.firstOrNull { it.node.elementType == JavaTokenType.DOT }
fun PsiMember.isImported(file: PsiJavaFile): Boolean { fun PsiMember.isImported(file: PsiJavaFile): Boolean {
if (this is PsiClass) { if (this is PsiClass) {
val fqName = qualifiedName val fqName = qualifiedName
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.j2k.ast package org.jetbrains.kotlin.j2k.ast
import com.intellij.psi.JavaTokenType import com.intellij.psi.JavaTokenType
import com.intellij.psi.PsiElement
import com.intellij.psi.tree.IElementType import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.j2k.CodeBuilder import org.jetbrains.kotlin.j2k.CodeBuilder
import org.jetbrains.kotlin.j2k.append import org.jetbrains.kotlin.j2k.append
@@ -111,17 +112,26 @@ class SuperExpression(val identifier: Identifier) : Expression() {
} }
} }
class QualifiedExpression(val qualifier: Expression, val identifier: Expression) : Expression() { class QualifiedExpression(val qualifier: Expression, val identifier: Expression, dotPrototype: PsiElement?) : Expression() {
private val dot = Dot().assignPrototype(dotPrototype, CommentsAndSpacesInheritance.LINE_BREAKS)
override val isNullable: Boolean override val isNullable: Boolean
get() = identifier.isNullable get() = identifier.isNullable
override fun generateCode(builder: CodeBuilder) { override fun generateCode(builder: CodeBuilder) {
if (!qualifier.isEmpty) { if (!qualifier.isEmpty) {
builder.appendOperand(this, qualifier).append(if (qualifier.isNullable) "!!." else ".") builder.appendOperand(this, qualifier).append(if (qualifier.isNullable) "!!" else "")
builder.append(dot)
} }
builder.append(identifier) builder.append(identifier)
} }
private class Dot : Element() {
override fun generateCode(builder: CodeBuilder) {
builder.append(".")
}
}
} }
class PolyadicExpression(val expressions: List<Expression>, val operators: List<Operator>) : Expression() { class PolyadicExpression(val expressions: List<Expression>, val operators: List<Operator>) : Expression() {
@@ -54,7 +54,7 @@ class MethodCallExpression(
isNullable: Boolean isNullable: Boolean
): MethodCallExpression { ): MethodCallExpression {
val identifier = Identifier.withNoPrototype(methodName, isNullable = false) val identifier = Identifier.withNoPrototype(methodName, isNullable = false)
return MethodCallExpression(if (receiver != null) QualifiedExpression(receiver, identifier).assignNoPrototype() else identifier, return MethodCallExpression(if (receiver != null) QualifiedExpression(receiver, identifier, null).assignNoPrototype() else identifier,
argumentList, argumentList,
typeArguments, typeArguments,
isNullable) isNullable)
@@ -20,6 +20,7 @@ import com.intellij.psi.*
import org.jetbrains.kotlin.j2k.AccessorKind import org.jetbrains.kotlin.j2k.AccessorKind
import org.jetbrains.kotlin.j2k.CodeConverter import org.jetbrains.kotlin.j2k.CodeConverter
import org.jetbrains.kotlin.j2k.ast.* import org.jetbrains.kotlin.j2k.ast.*
import org.jetbrains.kotlin.j2k.dot
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.utils.addToStdlib.singletonList import org.jetbrains.kotlin.utils.addToStdlib.singletonList
@@ -34,7 +35,11 @@ class AccessorToPropertyProcessing(val accessorMethod: PsiMethod, val accessorKi
val arguments = methodCall.argumentList.expressions val arguments = methodCall.argumentList.expressions
val propertyName = Identifier.withNoPrototype(propertyName, isNullable) val propertyName = Identifier.withNoPrototype(propertyName, isNullable)
val propertyAccess = QualifiedExpression(codeConverter.convertExpression(methodExpr.qualifierExpression), propertyName).assignNoPrototype() val qualifier = methodExpr.qualifierExpression
val propertyAccess = if (qualifier != null)
QualifiedExpression(codeConverter.convertExpression(qualifier), propertyName, methodExpr.dot()).assignNoPrototype()
else
propertyName
if (accessorKind == AccessorKind.GETTER) { if (accessorKind == AccessorKind.GETTER) {
if (arguments.size != 0) return null // incorrect call if (arguments.size != 0) return null // incorrect call
@@ -22,6 +22,7 @@ import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.j2k.AccessorKind import org.jetbrains.kotlin.j2k.AccessorKind
import org.jetbrains.kotlin.j2k.CodeConverter import org.jetbrains.kotlin.j2k.CodeConverter
import org.jetbrains.kotlin.j2k.ast.* import org.jetbrains.kotlin.j2k.ast.*
import org.jetbrains.kotlin.j2k.dot
import org.jetbrains.kotlin.utils.addToStdlib.singletonList import org.jetbrains.kotlin.utils.addToStdlib.singletonList
class FieldToPropertyProcessing( class FieldToPropertyProcessing(
@@ -60,7 +61,7 @@ class FieldToPropertyProcessing(
val qualifier = expression.qualifierExpression val qualifier = expression.qualifierExpression
if (qualifier != null && !useFieldReference) { if (qualifier != null && !useFieldReference) {
return QualifiedExpression(codeConverter.convertExpression(qualifier), identifier) return QualifiedExpression(codeConverter.convertExpression(qualifier), identifier, expression.dot())
} }
else { else {
// check if field name is shadowed // check if field name is shadowed
@@ -74,7 +75,7 @@ class FieldToPropertyProcessing(
return if (refExpr.resolve() == null) return if (refExpr.resolve() == null)
identifier identifier
else else
QualifiedExpression(ThisExpression(Identifier.Empty).assignNoPrototype(), identifier) //TODO: this is not correct in case of nested/anonymous classes QualifiedExpression(ThisExpression(Identifier.Empty).assignNoPrototype(), identifier, null) //TODO: this is not correct in case of nested/anonymous classes
} }
} }
} }
+5 -2
View File
@@ -4,8 +4,11 @@ object Test {
@JvmStatic fun main(args: Array<String>) { @JvmStatic fun main(args: Array<String>) {
println()// Comment println()// Comment
Test.foo()// Comment1 Test
.indexOf("s")// Comment2 // Comment1
.foo()
// Comment2
.indexOf("s")
} }
fun foo(): String { fun foo(): String {
+4 -1
View File
@@ -35,7 +35,10 @@ internal class CustomerBuilder {
object User { object User {
fun main() { fun main() {
val customer = CustomerBuilder().WithFirstName("Homer").WithLastName("Simpson").Build() val customer = CustomerBuilder()
.WithFirstName("Homer")
.WithLastName("Simpson")
.Build()
println(customer.firstName) println(customer.firstName)
println(customer.lastName) println(customer.lastName)
} }
@@ -0,0 +1,8 @@
public class C {
void foo() {
StringBuilder builder = new StringBuilder();
builder.append(1)
.append(2).append(3)
.append(4);
}
}
+8
View File
@@ -0,0 +1,8 @@
class C {
internal fun foo() {
val builder = StringBuilder()
builder.append(1)
.append(2).append(3)
.append(4)
}
}
@@ -2317,6 +2317,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("j2k/testData/fileOrElement/formatting"), Pattern.compile("^(.+)\\.java$"), true); KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("j2k/testData/fileOrElement/formatting"), Pattern.compile("^(.+)\\.java$"), true);
} }
@TestMetadata("chainedCall.java")
public void testChainedCall() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/formatting/chainedCall.java");
doTest(fileName);
}
@TestMetadata("lineBreaksBetweenArguments.java") @TestMetadata("lineBreaksBetweenArguments.java")
public void testLineBreaksBetweenArguments() throws Exception { public void testLineBreaksBetweenArguments() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/formatting/lineBreaksBetweenArguments.java"); String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/formatting/lineBreaksBetweenArguments.java");
@@ -2317,6 +2317,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("j2k/testData/fileOrElement/formatting"), Pattern.compile("^(.+)\\.java$"), true); KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("j2k/testData/fileOrElement/formatting"), Pattern.compile("^(.+)\\.java$"), true);
} }
@TestMetadata("chainedCall.java")
public void testChainedCall() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/formatting/chainedCall.java");
doTest(fileName);
}
@TestMetadata("lineBreaksBetweenArguments.java") @TestMetadata("lineBreaksBetweenArguments.java")
public void testLineBreaksBetweenArguments() throws Exception { public void testLineBreaksBetweenArguments() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/formatting/lineBreaksBetweenArguments.java"); String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/formatting/lineBreaksBetweenArguments.java");