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

View File

@@ -132,7 +132,8 @@ class AnnotationConverter(private val converter: Converter) {
val name = it.name
null to converter.deferredElement<Expression> {
QualifiedExpression(Identifier.withNoPrototype("AnnotationTarget", isNullable = false),
Identifier.withNoPrototype(name, isNullable = false))
Identifier.withNoPrototype(name, isNullable = false),
null)
}
}
return Identifier.withNoPrototype("Target") to deferredExpressionList

View File

@@ -359,7 +359,7 @@ class Converter private constructor(
}
else if (propertyInfo.modifiers.contains(Modifier.OVERRIDE) && !(propertyInfo.superInfo?.isAbstract() ?: false)) {
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 body = Block.of(returnStatement).assignNoPrototype()
val parameterList = ParameterList.withNoPrototype(emptyList())
@@ -396,7 +396,7 @@ class Converter private constructor(
}
else if (propertyInfo.modifiers.contains(Modifier.OVERRIDE) && !(propertyInfo.superInfo?.isAbstract() ?: false)) {
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 assignment = AssignmentExpression(superAccess, valueIdentifier, Operator.EQ).assignNoPrototype()
val body = Block.of(assignment).assignNoPrototype()

View File

@@ -242,7 +242,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
}
else {
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
}
@@ -253,7 +253,8 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
else
wrapperTypeName.shortName().asString()
result = QualifiedExpression(Identifier(classNameToUse, false).assignPrototype(operand),
Identifier.withNoPrototype("TYPE", isNullable = false))
Identifier.withNoPrototype("TYPE", isNullable = false),
null)
}
override fun visitConditionalExpression(expression: PsiConditionalExpression) {
@@ -322,6 +323,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
override fun visitMethodCallExpression(expression: PsiMethodCallExpression) {
val methodExpr = expression.methodExpression
val arguments = expression.argumentList.expressions
val qualifier = methodExpr.qualifierExpression
val target = methodExpr.resolve()
val isNullable = if (target is PsiMethod) typeConverter.methodNullability(target).isNullable(codeConverter.settings) else false
val typeArguments = convertTypeArguments(expression)
@@ -340,12 +342,15 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
val isExtension = property.isExtensionDeclaration()
val propertyAccess = if (isTopLevel) {
if (isExtension)
QualifiedExpression(codeConverter.convertExpression(arguments.firstOrNull(), true), propertyName).assignNoPrototype()
QualifiedExpression(codeConverter.convertExpression(arguments.firstOrNull(), true), propertyName, null).assignNoPrototype()
else
propertyName
}
else if (qualifier != null) {
QualifiedExpression(codeConverter.convertExpression(qualifier), propertyName, methodExpr.dot()).assignNoPrototype()
}
else {
QualifiedExpression(codeConverter.convertExpression(methodExpr.qualifierExpression), propertyName).assignNoPrototype()
propertyName
}
when(if (isExtension) parameterCount - 1 else parameterCount) {
@@ -408,7 +413,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
if (target is PsiMethod) {
val specialMethod = SpecialMethod.match(target, arguments.size, converter.services)
if (specialMethod != null) {
val converted = specialMethod.convertCall(methodExpr.qualifierExpression, arguments, typeArguments, codeConverter)
val converted = specialMethod.convertCall(qualifier, arguments, typeArguments, codeConverter)
if (converted != null) {
result = converted
return
@@ -542,7 +547,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
expression.getContainingClass()?.getParentOfType<PsiVariable>(false)?.let {
if (it == expression.qualifierExpression?.reference?.resolve()) {
result = QualifiedExpression(ThisExpression(Identifier.Empty).assignNoPrototype(), identifier)
result = QualifiedExpression(ThisExpression(Identifier.Empty).assignNoPrototype(), identifier, null)
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 {

View File

@@ -240,7 +240,7 @@ class ForConverter(
val collectionType = PsiElementFactory.SERVICE.getInstance(project).createTypeByFQClassName(CommonClassNames.JAVA_UTIL_COLLECTION)
val qualifierType = qualifier.type
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") {
val qualifier = collectionSize.qualifierExpression
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)
}
}

View File

@@ -197,7 +197,10 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String
OBJECT_GET_CLASS(JAVA_LANG_OBJECT, "getClass", 0) {
override fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, codeConverter: CodeConverter): Expression {
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 {
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 }

View File

@@ -21,6 +21,7 @@ import com.intellij.psi.util.PsiMethodUtil
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.j2k.ast.*
import org.jetbrains.kotlin.psi.psiUtil.allChildren
import org.jetbrains.kotlin.types.expressions.OperatorConventions
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 PsiReferenceExpression.dot(): PsiElement? = allChildren.firstOrNull { it.node.elementType == JavaTokenType.DOT }
fun PsiMember.isImported(file: PsiJavaFile): Boolean {
if (this is PsiClass) {
val fqName = qualifiedName

View File

@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.j2k.ast
import com.intellij.psi.JavaTokenType
import com.intellij.psi.PsiElement
import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.j2k.CodeBuilder
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
get() = identifier.isNullable
override fun generateCode(builder: CodeBuilder) {
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)
}
private class Dot : Element() {
override fun generateCode(builder: CodeBuilder) {
builder.append(".")
}
}
}
class PolyadicExpression(val expressions: List<Expression>, val operators: List<Operator>) : Expression() {

View File

@@ -54,7 +54,7 @@ class MethodCallExpression(
isNullable: Boolean
): MethodCallExpression {
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,
typeArguments,
isNullable)

View File

@@ -20,6 +20,7 @@ import com.intellij.psi.*
import org.jetbrains.kotlin.j2k.AccessorKind
import org.jetbrains.kotlin.j2k.CodeConverter
import org.jetbrains.kotlin.j2k.ast.*
import org.jetbrains.kotlin.j2k.dot
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
@@ -34,7 +35,11 @@ class AccessorToPropertyProcessing(val accessorMethod: PsiMethod, val accessorKi
val arguments = methodCall.argumentList.expressions
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 (arguments.size != 0) return null // incorrect call

View File

@@ -22,6 +22,7 @@ import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.j2k.AccessorKind
import org.jetbrains.kotlin.j2k.CodeConverter
import org.jetbrains.kotlin.j2k.ast.*
import org.jetbrains.kotlin.j2k.dot
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
class FieldToPropertyProcessing(
@@ -60,7 +61,7 @@ class FieldToPropertyProcessing(
val qualifier = expression.qualifierExpression
if (qualifier != null && !useFieldReference) {
return QualifiedExpression(codeConverter.convertExpression(qualifier), identifier)
return QualifiedExpression(codeConverter.convertExpression(qualifier), identifier, expression.dot())
}
else {
// check if field name is shadowed
@@ -74,7 +75,7 @@ class FieldToPropertyProcessing(
return if (refExpr.resolve() == null)
identifier
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
}
}
}

View File

@@ -4,8 +4,11 @@ object Test {
@JvmStatic fun main(args: Array<String>) {
println()// Comment
Test.foo()// Comment1
.indexOf("s")// Comment2
Test
// Comment1
.foo()
// Comment2
.indexOf("s")
}
fun foo(): String {

View File

@@ -35,7 +35,10 @@ internal class CustomerBuilder {
object User {
fun main() {
val customer = CustomerBuilder().WithFirstName("Homer").WithLastName("Simpson").Build()
val customer = CustomerBuilder()
.WithFirstName("Homer")
.WithLastName("Simpson")
.Build()
println(customer.firstName)
println(customer.lastName)
}

View File

@@ -0,0 +1,8 @@
public class C {
void foo() {
StringBuilder builder = new StringBuilder();
builder.append(1)
.append(2).append(3)
.append(4);
}
}

View File

@@ -0,0 +1,8 @@
class C {
internal fun foo() {
val builder = StringBuilder()
builder.append(1)
.append(2).append(3)
.append(4)
}
}

View File

@@ -2317,6 +2317,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
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")
public void testLineBreaksBetweenArguments() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/formatting/lineBreaksBetweenArguments.java");

View File

@@ -2317,6 +2317,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
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")
public void testLineBreaksBetweenArguments() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/formatting/lineBreaksBetweenArguments.java");