New J2K: Add ImplicitCastsConversion
This commit is contained in:
committed by
Ilya Kirillov
parent
2d37a2b28e
commit
6b889bdf57
@@ -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)
|
||||
}
|
||||
}
|
||||
-58
@@ -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() }
|
||||
}
|
||||
Reference in New Issue
Block a user