J2K: Convert usages of kotlin functions from library correctly
#KT-6115 Fixed
This commit is contained in:
@@ -63,6 +63,7 @@ object J2KPostProcessingRegistrar {
|
||||
registerIntentionBasedProcessing(ReplaceGetOrSetIntention(), additionalChecker = ReplaceGetOrSetInspection.additionalChecker) { applyTo(it) }
|
||||
registerIntentionBasedProcessing(AddOperatorModifierIntention()) { applyTo(it) }
|
||||
registerIntentionBasedProcessing(ObjectLiteralToLambdaIntention()) { applyTo(it) }
|
||||
registerIntentionBasedProcessing(AnonymousFunctionToLambdaIntention()) { applyTo(it) }
|
||||
|
||||
registerDiagnosticBasedProcessing<KtBinaryExpressionWithTypeRHS>(Errors.USELESS_CAST) { element, diagnostic ->
|
||||
val expression = RemoveRightPartOfBinaryExpressionFix(element, "").invoke()
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.asJava.KtLightField
|
||||
import org.jetbrains.kotlin.asJava.KtLightMethod
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.j2k.ast.*
|
||||
import org.jetbrains.kotlin.j2k.ast.Function
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
@@ -174,7 +175,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
|
||||
override fun visitClassObjectAccessExpression(expression: PsiClassObjectAccessExpression) {
|
||||
val operand = expression.operand
|
||||
val typeName = operand.type.canonicalText
|
||||
val primitiveType = JvmPrimitiveType.values.firstOrNull { it.javaKeywordName == typeName }
|
||||
val primitiveType = JvmPrimitiveType.values().firstOrNull { it.javaKeywordName == typeName }
|
||||
val wrapperTypeName = if (primitiveType != null) {
|
||||
primitiveType.wrapperFqName
|
||||
}
|
||||
@@ -309,6 +310,27 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
|
||||
return
|
||||
}
|
||||
}
|
||||
else if (origin == null){
|
||||
val resolvedQualifier = (methodExpr.qualifier as? PsiReferenceExpression)?.resolve()
|
||||
if (isFacadeClassFromLibrary(resolvedQualifier)) {
|
||||
result = if (target.isKotlinExtensionFunction()) {
|
||||
val qualifier = codeConverter.convertExpression(arguments.firstOrNull())
|
||||
MethodCallExpression.build(qualifier,
|
||||
methodExpr.referenceName!!,
|
||||
convertArguments(expression, isExtension = true),
|
||||
typeArguments,
|
||||
isNullable)
|
||||
}
|
||||
else {
|
||||
MethodCallExpression.build(null,
|
||||
methodExpr.referenceName!!,
|
||||
convertArguments(expression),
|
||||
typeArguments,
|
||||
isNullable)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (target is PsiMethod) {
|
||||
@@ -328,6 +350,14 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
|
||||
isNullable)
|
||||
}
|
||||
|
||||
private fun KtLightMethod.isKotlinExtensionFunction(): Boolean {
|
||||
val origin = this.getOrigin()
|
||||
if (origin != null) return origin.isExtensionDeclaration()
|
||||
|
||||
val parameters = parameterList.parameters
|
||||
return parameters.size > 0 && parameters[0].name == "\$receiver"
|
||||
}
|
||||
|
||||
private fun convertTypeArguments(call: PsiCallExpression): List<Type> {
|
||||
var typeArgs = call.typeArguments.toList()
|
||||
|
||||
@@ -371,10 +401,19 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
|
||||
val qualifier = expression.qualifier
|
||||
val classRef = expression.classOrAnonymousClassReference
|
||||
val classRefConverted = if (classRef != null) converter.convertCodeReferenceElement(classRef, hasExternalQualifier = qualifier != null) else null
|
||||
|
||||
val anonymousClass = expression.anonymousClass?.let { converter.convertAnonymousClassBody(it) }
|
||||
if (isFunctionType(expression.type)) {
|
||||
val function = anonymousClass?.body?.members?.singleOrNull() as? Function
|
||||
if (function != null) {
|
||||
result = AnonymousFunction(function.returnType.toNotNullType(), function.typeParameterList, function.parameterList, function.body)
|
||||
return
|
||||
}
|
||||
}
|
||||
result = NewClassExpression(classRefConverted,
|
||||
convertArguments(expression),
|
||||
codeConverter.convertExpression(qualifier),
|
||||
expression.anonymousClass?.let { converter.convertAnonymousClassBody(it) })
|
||||
convertArguments(expression),
|
||||
codeConverter.convertExpression(qualifier),
|
||||
anonymousClass)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -602,7 +641,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
|
||||
val functionalType = expression.functionalInterfaceType
|
||||
|
||||
val isTypeInQualifier = (qualifier as? PsiReference)?.resolve() is PsiClass
|
||||
val isKotlinFunctionType = functionalType?.canonicalText?.startsWith("kotlin.jvm.functions.Function") ?: false
|
||||
val isKotlinFunctionType = isFunctionType(functionalType)
|
||||
|
||||
// method can be null in case of default constructor
|
||||
val method = expression.resolve() as? PsiMethod
|
||||
@@ -659,6 +698,8 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
|
||||
}
|
||||
}
|
||||
|
||||
private fun isFunctionType(functionalType: PsiType?) = functionalType?.canonicalText?.startsWith("kotlin.jvm.functions.Function") ?: false
|
||||
|
||||
private fun convertMethodReferenceQualifier(qualifier: PsiElement): String {
|
||||
return when(qualifier) {
|
||||
is PsiExpression -> codeConverter.convertExpression(qualifier).canonicalCode()
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.j2k
|
||||
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.util.PsiMethodUtil
|
||||
import org.jetbrains.kotlin.asJava.KtLightClass
|
||||
import org.jetbrains.kotlin.j2k.ast.*
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
|
||||
@@ -114,3 +115,6 @@ fun PsiMember.isImported(file: PsiJavaFile): Boolean {
|
||||
}
|
||||
|
||||
fun PsiExpression.isNullLiteral() = this is PsiLiteralExpression && type == PsiType.NULL
|
||||
|
||||
// TODO: set origin for facade classes in library
|
||||
fun isFacadeClassFromLibrary(element: PsiElement?) = element is KtLightClass && element.getOrigin() == null
|
||||
|
||||
@@ -76,3 +76,14 @@ class Function(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AnonymousFunction(
|
||||
returnType: Type,
|
||||
typeParameterList: TypeParameterList,
|
||||
parameterList: ParameterList,
|
||||
body: DeferredElement<Block>?
|
||||
): Expression() {
|
||||
private val function = Function(Identifier.Empty, Annotations.Empty, Modifiers.Empty, returnType, typeParameterList, parameterList, body, false)
|
||||
|
||||
override fun generateCode(builder: CodeBuilder) = function.generateCode(builder)
|
||||
}
|
||||
|
||||
@@ -19,11 +19,9 @@ package org.jetbrains.kotlin.j2k.ast
|
||||
import com.intellij.psi.PsiImportList
|
||||
import com.intellij.psi.PsiImportStatementBase
|
||||
import com.intellij.psi.PsiJavaCodeReferenceElement
|
||||
import org.jetbrains.kotlin.asJava.KtLightClass
|
||||
import org.jetbrains.kotlin.asJava.KtLightClassForFacade
|
||||
import org.jetbrains.kotlin.j2k.CodeBuilder
|
||||
import org.jetbrains.kotlin.j2k.Converter
|
||||
import org.jetbrains.kotlin.j2k.append
|
||||
import org.jetbrains.kotlin.j2k.quoteKeywords
|
||||
import org.jetbrains.kotlin.j2k.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
|
||||
|
||||
@@ -76,6 +74,20 @@ private fun Converter.filterImport(name: String, ref: PsiJavaCodeReferenceElemen
|
||||
if (target is KtLightClassForFacade) {
|
||||
return quoteKeywords(target.getFqName().parent().toString()) + ".*"
|
||||
}
|
||||
else if (target is KtLightClass) {
|
||||
if (isFacadeClassFromLibrary(target)) return null
|
||||
|
||||
if (isUnderDefaultImports(target)) return null
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
val DEFAULT_IMPORTS = setOf("java.lang",
|
||||
"kotlin", "kotlin.annotation", "kotlin.jvm",
|
||||
"kotlin.collections", "kotlin.ranges", "kotlin.sequences",
|
||||
"kotlin.text", "kotlin.io")
|
||||
|
||||
fun isUnderDefaultImports(c: KtLightClass): Boolean {
|
||||
return c.getFqName().parent().asString() in DEFAULT_IMPORTS
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package demo;
|
||||
|
||||
import kotlin.Lazy;
|
||||
import kotlin.LazyKt;
|
||||
import kotlin.LazyThreadSafetyMode;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import kotlin.jvm.functions.Function0;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TestJava {
|
||||
public static void main(String[] args) {
|
||||
List<String> x = new ArrayList<String>();
|
||||
CollectionsKt.filter(x, new Function1<String, Boolean>() {
|
||||
@Override
|
||||
public Boolean invoke(String o) {
|
||||
return o.equals("a");
|
||||
}
|
||||
});
|
||||
Lazy<String> lazy = LazyKt.lazy(LazyThreadSafetyMode.NONE, new Function0<String>() {
|
||||
@Override
|
||||
public String invoke() {
|
||||
return "aaa";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void f(Function1<String, Unit> result) {
|
||||
result.invoke("a")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package demo
|
||||
|
||||
import java.util.ArrayList
|
||||
|
||||
class TestJava {
|
||||
|
||||
fun f(result: Function1<String, Unit>) {
|
||||
result.invoke("a")
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic fun main(args: Array<String>) {
|
||||
val x = ArrayList<String>()
|
||||
x.filter { o -> o == "a" }
|
||||
val lazy = lazy(LazyThreadSafetyMode.NONE) { "aaa" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4135,6 +4135,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("LibraryFunctions.java")
|
||||
public void testLibraryFunctions() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/toKotlinClasses/LibraryFunctions.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("TypeParameterBound.java")
|
||||
public void testTypeParameterBound() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/toKotlinClasses/TypeParameterBound.java");
|
||||
|
||||
@@ -4135,6 +4135,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("LibraryFunctions.java")
|
||||
public void testLibraryFunctions() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/toKotlinClasses/LibraryFunctions.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("TypeParameterBound.java")
|
||||
public void testTypeParameterBound() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/toKotlinClasses/TypeParameterBound.java");
|
||||
|
||||
Reference in New Issue
Block a user