Skip to content

Commit 39f94d7

Browse files
committed
KT-6044 J2K: Convert numeric float literals to 0.2f instead of 0.2.toFloat() + more fixes related to literals and type transformations
#KT-6044 Fixed
1 parent 5454f70 commit 39f94d7

22 files changed

+291
-135
lines changed

j2k/src/org/jetbrains/kotlin/j2k/CodeConverter.kt

Lines changed: 48 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -16,39 +16,9 @@
1616

1717
package org.jetbrains.kotlin.j2k
1818

19-
import com.intellij.psi.PsiType
20-
import com.intellij.psi.PsiCodeBlock
21-
import com.intellij.psi.PsiStatement
22-
import org.jetbrains.kotlin.j2k.ast.Block
23-
import org.jetbrains.kotlin.j2k.ast.LBrace
24-
import org.jetbrains.kotlin.j2k.ast.assignPrototype
25-
import org.jetbrains.kotlin.j2k.ast.RBrace
26-
import org.jetbrains.kotlin.j2k.ast.Statement
27-
import com.intellij.psi.PsiExpression
28-
import org.jetbrains.kotlin.j2k.ast.Expression
29-
import com.intellij.psi.PsiLocalVariable
30-
import org.jetbrains.kotlin.j2k.ast.LocalVariable
31-
import com.intellij.psi.PsiModifier
32-
import org.jetbrains.kotlin.j2k.ast.declarationIdentifier
33-
import org.jetbrains.kotlin.j2k.ast.Identifier
34-
import com.intellij.psi.PsiPrimitiveType
35-
import com.intellij.psi.PsiClassType
36-
import org.jetbrains.kotlin.j2k.ast.BangBangExpression
37-
import org.jetbrains.kotlin.j2k.ast.assignNoPrototype
38-
import org.jetbrains.kotlin.j2k.ast.LiteralExpression
39-
import org.jetbrains.kotlin.j2k.ast.MethodCallExpression
40-
import org.jetbrains.kotlin.j2k.ast.Type
41-
import org.jetbrains.kotlin.j2k.ast.ErrorType
42-
import org.jetbrains.kotlin.j2k.ast.Nullability
43-
import com.intellij.psi.CommonClassNames.JAVA_LANG_BYTE
44-
import com.intellij.psi.CommonClassNames.JAVA_LANG_SHORT
45-
import com.intellij.psi.CommonClassNames.JAVA_LANG_INTEGER
46-
import com.intellij.psi.CommonClassNames.JAVA_LANG_LONG
47-
import com.intellij.psi.CommonClassNames.JAVA_LANG_FLOAT
48-
import com.intellij.psi.CommonClassNames.JAVA_LANG_DOUBLE
49-
import com.intellij.psi.CommonClassNames.JAVA_LANG_CHARACTER
50-
import com.intellij.psi.PsiAnonymousClass
51-
import org.jetbrains.kotlin.j2k.ast.AnonymousClassBody
19+
import com.intellij.psi.*
20+
import com.intellij.psi.CommonClassNames.*
21+
import org.jetbrains.kotlin.j2k.ast.*
5222

5323
class CodeConverter(
5424
public val converter: Converter,
@@ -113,18 +83,40 @@ class CodeConverter(
11383
var convertedExpression = convertExpression(expression)
11484
if (expectedType == null || expectedType == PsiType.VOID) return convertedExpression
11585

116-
val actualType = expression.getType()
117-
if (actualType == null) return convertedExpression
86+
val actualType = expression.getType() ?: return convertedExpression
11887

119-
if (convertedExpression.isNullable &&
120-
(actualType is PsiPrimitiveType || actualType is PsiClassType && expectedType is PsiPrimitiveType)) {
121-
convertedExpression = BangBangExpression(convertedExpression).assignNoPrototype()
88+
if (actualType is PsiPrimitiveType || actualType is PsiClassType && expectedType is PsiPrimitiveType) {
89+
convertedExpression = BangBangExpression.surroundIfNullable(convertedExpression)
12290
}
12391

124-
if (needConversion(actualType, expectedType) && convertedExpression !is LiteralExpression) {
125-
val conversion = PRIMITIVE_TYPE_CONVERSIONS[expectedType.getCanonicalText()]
126-
if (conversion != null) {
127-
convertedExpression = MethodCallExpression.buildNotNull(convertedExpression, conversion)
92+
if (needConversion(actualType, expectedType)) {
93+
val expectedTypeStr = expectedType.getCanonicalText()
94+
if (expression is PsiLiteralExpression) {
95+
if (expectedTypeStr == "float" || expectedTypeStr == "double") {
96+
var text = convertedExpression.canonicalCode()
97+
if (text.last() in setOf('f', 'L')) {
98+
text = text.substring(0, text.length() - 1)
99+
}
100+
if (expectedTypeStr == "float") {
101+
text += "f"
102+
}
103+
else {
104+
if (text.indexOf('.') < 0) {
105+
text += ".0"
106+
}
107+
}
108+
convertedExpression = LiteralExpression(text)
109+
}
110+
}
111+
else if (expression is PsiPrefixExpression && expression.isLiteralWithSign()) {
112+
val operandConverted = convertExpression(expression.getOperand(), expectedType)
113+
convertedExpression = PrefixExpression(expression.getOperationSign().getText(), operandConverted)
114+
}
115+
else {
116+
val conversion = PRIMITIVE_TYPE_CONVERSIONS[expectedTypeStr]
117+
if (conversion != null) {
118+
convertedExpression = MethodCallExpression.buildNotNull(convertedExpression, conversion)
119+
}
128120
}
129121
}
130122

@@ -133,25 +125,34 @@ class CodeConverter(
133125

134126
public fun convertedExpressionType(expression: PsiExpression, expectedType: PsiType): Type {
135127
var convertedExpression = convertExpression(expression)
136-
val actualType = expression.getType()
137-
if (actualType == null) return ErrorType()
128+
val actualType = expression.getType() ?: return ErrorType()
138129
var resultType = typeConverter.convertType(actualType, if (convertedExpression.isNullable) Nullability.Nullable else Nullability.NotNull)
139130

140131
if (actualType is PsiPrimitiveType && resultType.isNullable ||
141132
expectedType is PsiPrimitiveType && actualType is PsiClassType) {
142133
resultType = resultType.toNotNullType()
143134
}
144135

145-
if (needConversion(actualType, expectedType) && convertedExpression !is LiteralExpression) {
146-
val conversion = PRIMITIVE_TYPE_CONVERSIONS[expectedType.getCanonicalText()]
147-
if (conversion != null) {
136+
if (needConversion(actualType, expectedType)) {
137+
val expectedTypeStr = expectedType.getCanonicalText()
138+
139+
val willConvert = if (convertedExpression is LiteralExpression
140+
|| expression is PsiPrefixExpression && expression.isLiteralWithSign() )
141+
expectedTypeStr == "float" || expectedTypeStr == "double"
142+
else
143+
PRIMITIVE_TYPE_CONVERSIONS[expectedTypeStr] != null
144+
145+
if (willConvert) {
148146
resultType = typeConverter.convertType(expectedType, Nullability.NotNull)
149147
}
150148
}
151149

152150
return resultType
153151
}
154152

153+
private fun PsiPrefixExpression.isLiteralWithSign()
154+
= getOperand() is PsiLiteralExpression && getOperationTokenType() in setOf(JavaTokenType.PLUS, JavaTokenType.MINUS)
155+
155156
public fun convertAnonymousClassBody(anonymousClass: PsiAnonymousClass): AnonymousClassBody {
156157
return AnonymousClassBody(ClassBodyConverter(anonymousClass, converter, isOpenClass = false, isObject = false).convertBody(),
157158
anonymousClass.getBaseClassType().resolve()?.isInterface() ?: false).assignPrototype(anonymousClass)

j2k/src/org/jetbrains/kotlin/j2k/ExpressionConverter.kt

Lines changed: 29 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,12 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
7474
}
7575

7676
override fun visitArrayInitializerExpression(expression: PsiArrayInitializerExpression) {
77-
val expressionType = typeConverter.convertType(expression.getType())
77+
val arrayType = expression.getType()
78+
val componentType = (arrayType as? PsiArrayType)?.getComponentType()
79+
val expressionType = typeConverter.convertType(arrayType)
7880
assert(expressionType is ArrayType) { "Array initializer must have array type: expressionType = $expressionType expression = $expression" }
7981
result = createArrayInitializerExpression(expressionType as ArrayType,
80-
codeConverter.convertExpressions(expression.getInitializers()),
81-
needExplicitType = true/*TODO: it's often redundant*/)
82+
expression.getInitializers().map { codeConverter.convertExpression(it, componentType) })
8283
}
8384

8485
override fun visitAssignmentExpression(expression: PsiAssignmentExpression) {
@@ -104,17 +105,13 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
104105
}
105106

106107
override fun visitBinaryExpression(expression: PsiBinaryExpression) {
107-
val operandsExpectedType = when (expression.getOperationTokenType()) {
108-
JavaTokenType.ANDAND, JavaTokenType.OROR -> PsiType.BOOLEAN
108+
var lhs = codeConverter.convertExpression(expression.getLOperand(), null)
109+
var rhs = codeConverter.convertExpression(expression.getROperand(), null)
109110

110-
JavaTokenType.PLUS, JavaTokenType.MINUS, JavaTokenType.ASTERISK,
111-
JavaTokenType.DIV, JavaTokenType.PERC, JavaTokenType.LTLT, JavaTokenType.GTGT,
112-
JavaTokenType.GTGTGT -> expression.getType()
113-
114-
else -> null
111+
if (expression.getOperationTokenType() in NON_NULL_OPERAND_OPS) {
112+
lhs = BangBangExpression.surroundIfNullable(lhs)
113+
rhs = BangBangExpression.surroundIfNullable(rhs)
115114
}
116-
val lhs = codeConverter.convertExpression(expression.getLOperand(), operandsExpectedType)
117-
val rhs = codeConverter.convertExpression(expression.getROperand(), operandsExpectedType)
118115
if (expression.getOperationTokenType() == JavaTokenType.GTGTGT) {
119116
result = MethodCallExpression.buildNotNull(lhs, "ushr", listOf(rhs))
120117
}
@@ -123,6 +120,18 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
123120
}
124121
}
125122

123+
private val NON_NULL_OPERAND_OPS = setOf(
124+
JavaTokenType.ANDAND,
125+
JavaTokenType.OROR,
126+
JavaTokenType.PLUS,
127+
JavaTokenType.MINUS,
128+
JavaTokenType.ASTERISK,
129+
JavaTokenType.DIV,
130+
JavaTokenType.PERC,
131+
JavaTokenType.LTLT,
132+
JavaTokenType.GTGT,
133+
JavaTokenType.GTGTGT)
134+
126135
override fun visitClassObjectAccessExpression(expression: PsiClassObjectAccessExpression) {
127136
val operand = expression.getOperand()
128137
val typeName = operand.getType().getCanonicalText()
@@ -173,24 +182,24 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
173182
var text = expression.getText()!!
174183
val type = expression.getType()
175184
if (type != null) {
176-
val canonicalTypeStr = type.getCanonicalText()
177-
if (canonicalTypeStr == "double" || canonicalTypeStr == JAVA_LANG_DOUBLE) {
185+
val typeStr = type.getCanonicalText()
186+
if (typeStr == "double") {
178187
text = text.replace("D", "").replace("d", "")
179188
if (!text.contains(".")) {
180189
text += ".0"
181190
}
182191

183192
}
184193

185-
if (canonicalTypeStr == "float" || canonicalTypeStr == JAVA_LANG_FLOAT) {
186-
text = text.replace("F", "").replace("f", "") + "." + OperatorConventions.FLOAT + "()"
194+
if (typeStr == "float") {
195+
text = text.replace("F", "f")
187196
}
188197

189-
if (canonicalTypeStr == "long" || canonicalTypeStr == JAVA_LANG_LONG) {
190-
text = text.replace("L", "").replace("l", "")
198+
if (typeStr == "long") {
199+
text = text.replace("l", "L")
191200
}
192201

193-
if (canonicalTypeStr == "int" || canonicalTypeStr == JAVA_LANG_INTEGER) {
202+
if (typeStr == "int") {
194203
text = if (value != null) value.toString() else text
195204
}
196205
}
@@ -341,7 +350,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
341350
result = BinaryExpression(operand.left, operand.right, "!=")
342351
}
343352
else {
344-
result = PrefixOperator(getOperatorString(token), operand)
353+
result = PrefixExpression(getOperatorString(token), operand)
345354
}
346355
}
347356

@@ -482,23 +491,6 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
482491
}
483492
}
484493

485-
protected fun getClassName(expression: PsiExpression): String {
486-
var context = expression.getContext()
487-
while (context != null) {
488-
val _context = context!!
489-
if (_context is PsiClass) {
490-
val identifier = _context.getNameIdentifier()
491-
if (identifier != null) {
492-
return identifier.getText()!!
493-
}
494-
495-
}
496-
497-
context = _context.getContext()
498-
}
499-
return ""
500-
}
501-
502494
companion object {
503495
private val needQualifierNameSet = setOf("java.lang.Byte", "java.lang.Double", "java.lang.Float", "java.lang.Long", "java.lang.Short")
504496
}

j2k/src/org/jetbrains/kotlin/j2k/StatementConverter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ class DefaultStatementConverter : JavaElementVisitor(), StatementConverter {
125125

126126
override fun visitForeachStatement(statement: PsiForeachStatement) {
127127
val iteratorExpr = codeConverter.convertExpression(statement.getIteratedValue())
128-
val iterator = if (iteratorExpr.isNullable) BangBangExpression(iteratorExpr).assignNoPrototype() else iteratorExpr
128+
val iterator = BangBangExpression.surroundIfNullable(iteratorExpr)
129129
val iterationParameter = statement.getIterationParameter()
130130
result = ForeachStatement(iterationParameter.declarationIdentifier(),
131131
if (codeConverter.settings.specifyLocalVariableTypeByDefault) codeConverter.typeConverter.convertVariableType(iterationParameter) else null,

j2k/src/org/jetbrains/kotlin/j2k/ast/Expressions.kt

Lines changed: 12 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ class BangBangExpression(val expr: Expression) : Expression() {
3737
override fun generateCode(builder: CodeBuilder) {
3838
builder.appendOperand(this, expr).append("!!")
3939
}
40+
41+
companion object {
42+
fun surroundIfNullable(expression: Expression): Expression {
43+
return if (expression.isNullable)
44+
BangBangExpression(expression).assignNoPrototype()
45+
else
46+
expression
47+
}
48+
}
4049
}
4150

4251
class BinaryExpression(val left: Expression, val right: Expression, val op: String) : Expression() {
@@ -69,7 +78,7 @@ class ParenthesizedExpression(val expression: Expression) : Expression() {
6978
}
7079
}
7180

72-
class PrefixOperator(val op: String, val expression: Expression) : Expression() {
81+
class PrefixExpression(val op: String, val expression: Expression) : Expression() {
7382
override fun generateCode(builder: CodeBuilder){
7483
builder.append(op).appendOperand(this, expression)
7584
}
@@ -155,39 +164,13 @@ class DownToExpression(val start: Expression, val end: Expression): Expression()
155164
}
156165
}
157166

158-
fun createArrayInitializerExpression(arrayType: ArrayType, initializers: List<Expression>, needExplicitType: Boolean) : MethodCallExpression {
167+
fun createArrayInitializerExpression(arrayType: ArrayType, initializers: List<Expression>, needExplicitType: Boolean = true) : MethodCallExpression {
159168
val elementType = arrayType.elementType
160169
val createArrayFunction = if (elementType is PrimitiveType)
161170
(elementType.toNotNullType().canonicalCode() + "Array").decapitalize()
162171
else if (needExplicitType)
163172
arrayType.toNotNullType().canonicalCode().decapitalize()
164173
else
165174
"array"
166-
167-
val doubleOrFloatTypes = setOf("double", "float", "java.lang.double", "java.lang.float")
168-
val afterReplace = arrayType.toNotNullType().canonicalCode().replace("Array", "").toLowerCase().replace(">", "").replace("<", "").replace("?", "")
169-
170-
fun explicitConvertIfNeeded(initializer: Expression): Expression {
171-
if (doubleOrFloatTypes.contains(afterReplace)) {
172-
if (initializer is LiteralExpression) {
173-
if (!initializer.canonicalCode().contains(".")) {
174-
return LiteralExpression(initializer.literalText + ".0").assignPrototypesFrom(initializer)
175-
}
176-
}
177-
else {
178-
val conversionFunction = when {
179-
afterReplace.contains("double") -> OperatorConventions.DOUBLE.getIdentifier()
180-
afterReplace.contains("float") -> OperatorConventions.FLOAT.getIdentifier()
181-
else -> null
182-
}
183-
if (conversionFunction != null) {
184-
return MethodCallExpression.buildNotNull(initializer, conversionFunction).assignNoPrototype()
185-
}
186-
}
187-
}
188-
189-
return initializer
190-
}
191-
192-
return MethodCallExpression.buildNotNull(null, createArrayFunction, initializers.map { explicitConvertIfNeeded(it) })
175+
return MethodCallExpression.buildNotNull(null, createArrayFunction, initializers)
193176
}

j2k/src/org/jetbrains/kotlin/j2k/ast/Util.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ private fun Expression.precedence(): Int? {
3939
return when(this) {
4040
is QualifiedExpression, is MethodCallExpression, is ArrayAccessExpression, is PostfixOperator, is BangBangExpression, is StarExpression -> 0
4141

42-
is PrefixOperator -> 1
42+
is PrefixExpression -> 1
4343

4444
is TypeCastExpression -> 2
4545

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
val a = 0
2-
val b = 0
3-
val c = 0
4-
val ds = doubleArray(a.toDouble(), b.toDouble(), c.toDouble())
1+
val a = 0.0
2+
val b = 0.0
3+
val c = 0.0
4+
val ds = doubleArray(a, b, c)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
val a = floatArray(1.0, 2.0, 3.0)
1+
val a = floatArray(1f, 2f, 3.0f)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
//statement
2-
java.lang.Double[] a = new java.lang.Double[]{1, 2, 3};
2+
java.lang.Double[] a = new java.lang.Double[]{1.0, 2.0, 3.0};
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
//statement
2-
java.lang.Float[] a = new java.lang.Float[]{1, 2, 3};
2+
java.lang.Float[] a = new java.lang.Float[]{1.0f, 2f, 3f};
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
val a = array<Float>(1.0, 2.0, 3.0)
1+
val a = array(1.0f, 2f, 3f)

j2k/testData/fileOrElement/boxedType/Boxing.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import java.util.ArrayList
33
class Boxing {
44
fun test() {
55
var i: Int? = 0
6-
val n = 0.0.toFloat()
6+
val n = 0.0f
77
i = 1
88
var j = i!!
99
val k = i!! + 2
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
// ERROR: An integer literal does not conform to the expected type kotlin.Double
21
class A {
32
deprecated("")
43
volatile var field1 = 0
54

65
transient var field2 = 1
76

8-
strictfp var field3: Double = 2
7+
strictfp var field3 = 2.0
98
}

0 commit comments

Comments
 (0)