From 6b889bdf57374ac634984384910e897f44b30b56 Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Mon, 24 Dec 2018 12:39:33 +0300 Subject: [PATCH] New J2K: Add ImplicitCastsConversion --- .../jetbrains/kotlin/j2k/JKSymbolProvider.kt | 9 +- .../conversions/ImplicitCastsConversion.kt | 120 ++++++++++++++++++ .../PrimitiveTypesInitializersConversion.kt | 58 --------- .../jetbrains/kotlin/j2k/tree/impl/symbols.kt | 81 +++++++++--- .../org/jetbrains/kotlin/j2k/tree/types.kt | 61 +++++++-- 5 files changed, 238 insertions(+), 91 deletions(-) create mode 100644 j2k/newSrc/org/jetbrains/kotlin/j2k/conversions/ImplicitCastsConversion.kt delete mode 100644 j2k/newSrc/org/jetbrains/kotlin/j2k/conversions/PrimitiveTypesInitializersConversion.kt diff --git a/j2k/newSrc/org/jetbrains/kotlin/j2k/JKSymbolProvider.kt b/j2k/newSrc/org/jetbrains/kotlin/j2k/JKSymbolProvider.kt index 63bfa5feffa..574d77d6686 100644 --- a/j2k/newSrc/org/jetbrains/kotlin/j2k/JKSymbolProvider.kt +++ b/j2k/newSrc/org/jetbrains/kotlin/j2k/JKSymbolProvider.kt @@ -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 provideByFqName(fqName: FqName, context: PsiElement = symbolsByPsi.keys.first()): T = provideByFqName(fqName.asString(), context) + internal inline fun provideByFqName(fqName: FqNameUnsafe, context: PsiElement = symbolsByPsi.keys.first()): T = + provideByFqName(fqName.asString(), context) + private inner class ElementVisitor : JavaElementVisitor() { override fun visitClass(aClass: PsiClass) { diff --git a/j2k/newSrc/org/jetbrains/kotlin/j2k/conversions/ImplicitCastsConversion.kt b/j2k/newSrc/org/jetbrains/kotlin/j2k/conversions/ImplicitCastsConversion.kt new file mode 100644 index 00000000000..ab16553cf24 --- /dev/null +++ b/j2k/newSrc/org/jetbrains/kotlin/j2k/conversions/ImplicitCastsConversion.kt @@ -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) + } +} \ No newline at end of file diff --git a/j2k/newSrc/org/jetbrains/kotlin/j2k/conversions/PrimitiveTypesInitializersConversion.kt b/j2k/newSrc/org/jetbrains/kotlin/j2k/conversions/PrimitiveTypesInitializersConversion.kt deleted file mode 100644 index 3e5c0ad9d83..00000000000 --- a/j2k/newSrc/org/jetbrains/kotlin/j2k/conversions/PrimitiveTypesInitializersConversion.kt +++ /dev/null @@ -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() - ) - ) - } -} \ No newline at end of file diff --git a/j2k/newSrc/org/jetbrains/kotlin/j2k/tree/impl/symbols.kt b/j2k/newSrc/org/jetbrains/kotlin/j2k/tree/impl/symbols.kt index 6e9e4b07aae..9e5622e4894 100644 --- a/j2k/newSrc/org/jetbrains/kotlin/j2k/tree/impl/symbols.kt +++ b/j2k/newSrc/org/jetbrains/kotlin/j2k/tree/impl/symbols.kt @@ -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 : JKSymbol { +interface JKUniverseSymbol : 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 - val returnType: JKType + val parameterTypes: List? + val returnType: JKType? } +fun JKMethodSymbol.parameterTypesWithUnfoldedVarargs(): Sequence? { + 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 { @@ -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 - get() = target.valueParameters.map { it.typeReference!!.typeElement!!.toJK(symbolProvider) } - override val returnType: JKType - get() = target.typeReference!!.typeElement!!.toJK(symbolProvider) + override val parameterTypes: List? + 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 + + 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 - 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 + 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? diff --git a/j2k/newSrc/org/jetbrains/kotlin/j2k/tree/types.kt b/j2k/newSrc/org/jetbrains/kotlin/j2k/tree/types.kt index 42160eed202..890871cbd26 100644 --- a/j2k/newSrc/org/jetbrains/kotlin/j2k/tree/types.kt +++ b/j2k/newSrc/org/jetbrains/kotlin/j2k/tree/types.kt @@ -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 \ No newline at end of file + this == JKJavaPrimitiveTypeImpl.DOUBLE + +inline fun 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() + .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() } +} \ No newline at end of file