New J2K: Add ImplicitCastsConversion

This commit is contained in:
Ilya Kirillov
2018-12-24 12:39:33 +03:00
committed by Ilya Kirillov
parent 2d37a2b28e
commit 6b889bdf57
5 changed files with 238 additions and 91 deletions
@@ -11,11 +11,9 @@ import org.jetbrains.kotlin.j2k.tree.*
import org.jetbrains.kotlin.j2k.tree.impl.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.kotlin.psi.KtProperty
class JKSymbolProvider {
@@ -100,6 +98,9 @@ class JKSymbolProvider {
internal inline fun <reified T : JKSymbol> provideByFqName(fqName: FqName, context: PsiElement = symbolsByPsi.keys.first()): T =
provideByFqName(fqName.asString(), context)
internal inline fun <reified T : JKSymbol> provideByFqName(fqName: FqNameUnsafe, context: PsiElement = symbolsByPsi.keys.first()): T =
provideByFqName(fqName.asString(), context)
private inner class ElementVisitor : JavaElementVisitor() {
override fun visitClass(aClass: PsiClass) {
@@ -0,0 +1,120 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.j2k.conversions
import org.jetbrains.kotlin.j2k.ConversionContext
import org.jetbrains.kotlin.j2k.ast.Nullability
import org.jetbrains.kotlin.j2k.bangedBangedExpr
import org.jetbrains.kotlin.j2k.copyTreeAndDetach
import org.jetbrains.kotlin.j2k.fixLiteral
import org.jetbrains.kotlin.j2k.tree.*
import org.jetbrains.kotlin.j2k.tree.impl.*
class ImplicitCastsConversion(private val context: ConversionContext) : RecursiveApplicableConversionBase() {
override fun applyToElement(element: JKTreeElement): JKTreeElement {
when (element) {
is JKVariable -> convertVariable(element)
is JKForInStatement -> convertForInStatement(element)
is JKMethodCallExpression -> convertMethodCallExpression(element)
is JKKtAssignmentStatement -> convertAssignmentStatement(element)
}
return recurse(element)
}
private fun convertVariable(variable: JKVariable) {
if (variable.initializer is JKStubExpression) return
variable.initializer.castTo(variable.type.type)?.also {
variable.initializer = it
}
}
private fun convertForInStatement(forInStatement: JKForInStatement) {
val notNullType = forInStatement.iterationExpression.type(context)?.updateNullability(Nullability.NotNull) ?: return
forInStatement.iterationExpression.addBangBang(notNullType)?.also {
forInStatement.iterationExpression = it
}
}
private fun convertAssignmentStatement(statement: JKKtAssignmentStatement) {
val expressionType = statement.field.type(context) ?: return
statement.expression.castTo(expressionType)?.also {
statement.expression = it
}
}
private fun convertMethodCallExpression(expression: JKMethodCallExpression) {
if (expression.identifier.isUnresolved()) return
val parameterTypes = expression.identifier.parameterTypesWithUnfoldedVarargs() ?: return
val newArguments =
(expression.arguments.expressions.asSequence() zip parameterTypes)
.map { (expression, toType) ->
expression.castTo(toType)
}.toList()
val needUpdate = newArguments.any { it != null }
if (needUpdate) {
expression.arguments = JKExpressionListImpl(
(newArguments zip expression.arguments.expressions)
.map { (newArgument, oldArgument) ->
(newArgument ?: oldArgument).copyTreeAndDetach()
}
)
}
}
private fun JKExpression.addBangBang(toType: JKType): JKExpression? {
val expressionType = type(context) as? JKClassType ?: return null
if (toType !is JKClassType) return null
if (expressionType.classReference == toType.classReference
&& expressionType.isNullable() && !toType.isNullable()
) {
return this.copyTreeAndDetach().bangedBangedExpr(context)
}
return null
}
private fun JKExpression.castToAsPrimitiveTypes(toType: JKType): JKExpression? {
val expressionTypeAsPrimitive = type(context)?.asPrimitiveType() ?: return null
val toTypeAsPrimitive = toType.asPrimitiveType() ?: return null
if (toTypeAsPrimitive == expressionTypeAsPrimitive) return null
if (this is JKLiteralExpression) {
if (expressionTypeAsPrimitive == JKJavaPrimitiveTypeImpl.INT
&& (toTypeAsPrimitive == JKJavaPrimitiveTypeImpl.LONG ||
toTypeAsPrimitive == JKJavaPrimitiveTypeImpl.SHORT ||
toTypeAsPrimitive == JKJavaPrimitiveTypeImpl.BYTE)
) return null
val expectedType = toTypeAsPrimitive.toLiteralType() ?: JKLiteralExpression.LiteralType.INT
if (expressionTypeAsPrimitive.isNumberType() && toTypeAsPrimitive.isNumberType()) {
return JKJavaLiteralExpressionImpl(
literal,
expectedType
).fixLiteral(expectedType)
}
}
val initialTypeName = expressionTypeAsPrimitive.jvmPrimitiveType.javaKeywordName.capitalize()
val conversionFunctionName = "to${toTypeAsPrimitive.jvmPrimitiveType.javaKeywordName.capitalize()}"
return JKQualifiedExpressionImpl(
this.copyTreeAndDetach(),
JKKtQualifierImpl.DOT,
JKJavaMethodCallExpressionImpl(
context.symbolProvider.provideByFqName("kotlin.$initialTypeName.$conversionFunctionName"),
JKExpressionListImpl()
)
)
}
private fun JKExpression.castTo(toType: JKType): JKExpression? {
val expressionType = type(context)
if (expressionType == toType) return null
castToAsPrimitiveTypes(toType)?.also { return it }
return addBangBang(toType)
}
}
@@ -1,58 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.j2k.conversions
import org.jetbrains.kotlin.j2k.ConversionContext
import org.jetbrains.kotlin.j2k.copyTree
import org.jetbrains.kotlin.j2k.copyTreeAndDetach
import org.jetbrains.kotlin.j2k.tree.*
import org.jetbrains.kotlin.j2k.tree.impl.*
class PrimitiveTypesInitializersConversion(private val context: ConversionContext) : RecursiveApplicableConversionBase() {
override fun applyToElement(element: JKTreeElement): JKTreeElement {
if (element !is JKVariable) return recurse(element)
val literalInitializer = element.initializer as? JKJavaLiteralExpression ?: return recurse(element)
val literalType = literalInitializer.type.toPrimitiveType()
?: return recurse(element)
val fieldType = element.type.type as? JKJavaPrimitiveType
?: (element.type.type as? JKClassType)?.toPrimitiveType()
?: return recurse(element)
createPrimitiveTypeInitializer(literalInitializer.copyTreeAndDetach(), literalType, fieldType)?.also {
element.initializer = it
}
return element
}
private fun createPrimitiveTypeInitializer(
literal: JKJavaLiteralExpression,
literalType: JKJavaPrimitiveType,
fieldType: JKJavaPrimitiveType
): JKExpression? {
if (literalType == fieldType) return null
if (literalType == JKJavaPrimitiveTypeImpl.INT
&& fieldType == JKJavaPrimitiveTypeImpl.LONG
) return null
if (literalType.isNumberType() && fieldType.isNumberType()) {
return JKJavaLiteralExpressionImpl(
literal.literal,
fieldType.toLiteralType() ?: JKLiteralExpression.LiteralType.INT
)
}
val conversionFunctionName = "to${fieldType.jvmPrimitiveType.javaKeywordName.capitalize()}"
return JKQualifiedExpressionImpl(
literal,
JKKtQualifierImpl.DOT,
JKJavaMethodCallExpressionImpl(
context.symbolProvider.provideByFqName("kotlin.$conversionFunctionName"),
JKExpressionListImpl()
)
)
}
}
@@ -5,18 +5,22 @@
package org.jetbrains.kotlin.j2k.tree.impl
import com.intellij.psi.*
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiReference
import com.intellij.psi.PsiVariable
import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl
import com.intellij.util.reverse
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
import org.jetbrains.kotlin.j2k.JKSymbolProvider
import org.jetbrains.kotlin.j2k.ast.Nullability
import org.jetbrains.kotlin.j2k.conversions.parentOfType
import org.jetbrains.kotlin.j2k.conversions.resolveFqName
import org.jetbrains.kotlin.j2k.tree.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getValueParameterList
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtNamedFunction
interface JKSymbol {
val target: Any
@@ -24,11 +28,16 @@ interface JKSymbol {
val fqName: String?
}
interface JKUnresolvedSymbol : JKSymbol
fun JKSymbol.isUnresolved() =
this is JKUnresolvedSymbol
interface JKNamedSymbol : JKSymbol {
val name: String
}
interface JKUniverseSymbol<T: JKTreeElement> : JKSymbol {
interface JKUniverseSymbol<T : JKTreeElement> : JKSymbol {
override var target: T
}
@@ -38,13 +47,21 @@ interface JKClassSymbol : JKNamedSymbol
interface JKMethodSymbol : JKNamedSymbol {
override val fqName: String
val receiverType: JKType?
val parameterTypes: List<JKType>
val returnType: JKType
val parameterTypes: List<JKType>?
val returnType: JKType?
}
fun JKMethodSymbol.parameterTypesWithUnfoldedVarargs(): Sequence<JKType>? {
val realParameterTypes = parameterTypes ?: return null
if (realParameterTypes.isEmpty()) return emptySequence()
val lastArrayType = realParameterTypes.last().arrayInnerType() ?: return realParameterTypes.asSequence()
return realParameterTypes.dropLast(1).asSequence() + generateSequence { lastArrayType }
}
interface JKFieldSymbol : JKNamedSymbol {
override val fqName: String
val fieldType: JKType
val fieldType: JKType?
}
class JKUniverseClassSymbol : JKClassSymbol, JKUniverseSymbol<JKClass> {
@@ -132,10 +149,21 @@ class JKMultiverseMethodSymbol(override val target: PsiMethod, private val symbo
class JKMultiverseFunctionSymbol(override val target: KtNamedFunction, private val symbolProvider: JKSymbolProvider) : JKMethodSymbol {
override val receiverType: JKType?
get() = target.receiverTypeReference?.typeElement?.toJK(symbolProvider)
override val parameterTypes: List<JKType>
get() = target.valueParameters.map { it.typeReference!!.typeElement!!.toJK(symbolProvider) }
override val returnType: JKType
get() = target.typeReference!!.typeElement!!.toJK(symbolProvider)
override val parameterTypes: List<JKType>?
get() = target.valueParameters.map { parameter ->
val type = parameter.typeReference?.typeElement?.toJK(symbolProvider)
type?.let {
if (parameter.isVarArg) {
JKClassTypeImpl(
symbolProvider.provideByFqName(KotlinBuiltIns.FQ_NAMES.array),
listOf(it)
)
} else it
}
}.takeIf { parameters -> parameters.all { it != null } } as? List<JKType>
override val returnType: JKType?
get() = target.typeReference?.typeElement?.toJK(symbolProvider)
override val name: String
get() = target.name!!
override val declaredIn: JKSymbol
@@ -168,8 +196,8 @@ class JKMultiverseFieldSymbol(override val target: PsiVariable, private val symb
}
class JKMultiversePropertySymbol(override val target: KtCallableDeclaration, private val symbolProvider: JKSymbolProvider) : JKFieldSymbol {
override val fieldType: JKType
get() = target.typeReference!!.typeElement!!.toJK(symbolProvider)
override val fieldType: JKType?
get() = target.typeReference?.typeElement?.toJK(symbolProvider)
override val name: String
get() = target.name!!
override val declaredIn: JKSymbol
@@ -178,7 +206,7 @@ class JKMultiversePropertySymbol(override val target: KtCallableDeclaration, pri
get() = target.fqName!!.asString()
}
class JKUnresolvedField(override val target: PsiReference, private val symbolProvider: JKSymbolProvider) : JKFieldSymbol {
class JKUnresolvedField(override val target: PsiReference, private val symbolProvider: JKSymbolProvider) : JKFieldSymbol, JKUnresolvedSymbol {
override val fieldType: JKType
get() {
val resolvedType = (target as? PsiReferenceExpressionImpl)?.type
@@ -198,7 +226,7 @@ class JKUnresolvedField(override val target: PsiReference, private val symbolPro
class JKUnresolvedMethod(
override val target: String,
override val returnType: JKType = JKNoTypeImpl
) : JKMethodSymbol {
) : JKMethodSymbol, JKUnresolvedSymbol {
constructor(target: PsiReference) : this(target.canonicalText)
override val declaredIn: JKSymbol
@@ -207,12 +235,27 @@ class JKUnresolvedMethod(
override val receiverType: JKType?
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
override val parameterTypes: List<JKType>
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
get() = TODO(target) //To change initializer of created properties use File | Settings | File Templates.
override val name: String
get() = target
}
class JKUnresolvedClassSymbol(override val target: String) : JKClassSymbol {
class JKExclExclMethod(
operandType: JKType
) : JKMethodSymbol {
override val target: String = "!!"
override val returnType: JKType = operandType.updateNullability(Nullability.NotNull)
override val declaredIn: JKSymbol
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
override val fqName: String = "!!"
override val receiverType: JKType?
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
override val parameterTypes: List<JKType>
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
override val name: String = "!!"
}
class JKUnresolvedClassSymbol(override val target: String) : JKClassSymbol, JKUnresolvedSymbol {
override val declaredIn: JKSymbol
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
override val fqName: String?
@@ -200,15 +200,18 @@ fun JKJavaPrimitiveType.toLiteralType(): JKLiteralExpression.LiteralType? =
else -> null
}
fun JKClassType.toPrimitiveType(): JKJavaPrimitiveType? =
when (classReference.fqName) {
"java.lang.Character" -> JKJavaPrimitiveTypeImpl.CHAR
"java.lang.Boolean" -> JKJavaPrimitiveTypeImpl.BOOLEAN
"java.lang.Integer" -> JKJavaPrimitiveTypeImpl.INT
"java.lang.Long" -> JKJavaPrimitiveTypeImpl.LONG
"java.lang.Float" -> JKJavaPrimitiveTypeImpl.FLOAT
"java.lang.Double" -> JKJavaPrimitiveTypeImpl.DOUBLE
"java.lang.Byte" -> JKJavaPrimitiveTypeImpl.BYTE
fun JKType.asPrimitiveType(): JKJavaPrimitiveType? =
if (this is JKJavaPrimitiveType) this
else when ((this as? JKClassType)?.classReference?.fqName) {
KotlinBuiltIns.FQ_NAMES._char.asString(), CommonClassNames.JAVA_LANG_CHARACTER -> JKJavaPrimitiveTypeImpl.CHAR
KotlinBuiltIns.FQ_NAMES._boolean.asString(), CommonClassNames.JAVA_LANG_BOOLEAN -> JKJavaPrimitiveTypeImpl.BOOLEAN
KotlinBuiltIns.FQ_NAMES._int.asString(), CommonClassNames.JAVA_LANG_INTEGER -> JKJavaPrimitiveTypeImpl.INT
KotlinBuiltIns.FQ_NAMES._long.asString(), CommonClassNames.JAVA_LANG_LONG -> JKJavaPrimitiveTypeImpl.LONG
KotlinBuiltIns.FQ_NAMES._float.asString(), CommonClassNames.JAVA_LANG_FLOAT -> JKJavaPrimitiveTypeImpl.FLOAT
KotlinBuiltIns.FQ_NAMES._double.asString(), CommonClassNames.JAVA_LANG_DOUBLE -> JKJavaPrimitiveTypeImpl.DOUBLE
KotlinBuiltIns.FQ_NAMES._byte.asString(), CommonClassNames.JAVA_LANG_BYTE -> JKJavaPrimitiveTypeImpl.BYTE
KotlinBuiltIns.FQ_NAMES._short.asString(), CommonClassNames.JAVA_LANG_SHORT -> JKJavaPrimitiveTypeImpl.SHORT
else -> null
}
@@ -216,4 +219,42 @@ fun JKJavaPrimitiveType.isNumberType() =
this == JKJavaPrimitiveTypeImpl.INT ||
this == JKJavaPrimitiveTypeImpl.LONG ||
this == JKJavaPrimitiveTypeImpl.FLOAT ||
this == JKJavaPrimitiveTypeImpl.DOUBLE
this == JKJavaPrimitiveTypeImpl.DOUBLE
inline fun <reified T : JKType> T.addTypeParametersToRawProjectionType(typeParameter: JKType): T =
if (this is JKClassType && parameters.isEmpty()) {
val resolvedClass = classReference.target
val parametersCount =
when (resolvedClass) {
is PsiClass -> resolvedClass.typeParameters.size
is KtClass -> resolvedClass.typeParameters.size
else -> 0
}
val typeParameters = List(parametersCount) { typeParameter }
JKClassTypeImpl(
classReference,
typeParameters,
nullability
) as T
} else this
fun JKClassSymbol.isArrayType(): Boolean =
fqName in
JKJavaPrimitiveTypeImpl.KEYWORD_TO_INSTANCE.values
.filterIsInstance<JKJavaPrimitiveType>()
.map { PrimitiveType.valueOf(it.jvmPrimitiveType.name).arrayTypeFqName.asString() } +
KotlinBuiltIns.FQ_NAMES.array.asString()
fun JKType.arrayInnerType(): JKType? =
when (this) {
is JKJavaArrayType -> type
is JKClassType ->
if (this.classReference.isArrayType()) this.parameters.singleOrNull()
else null
else -> null
}
val namesOfPrimitiveTypes by lazy {
KotlinBuiltIns.FQ_NAMES.primitiveTypeShortNames.map { it.identifier.decapitalize() }
}