KT-12039 Convert Java to Kotlin -- static imports are imported as Class.CONST (missing .Companion)
#KT-12039 Fixed
This commit is contained in:
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
|
||||
import org.jetbrains.kotlin.j2k.ast.*
|
||||
import org.jetbrains.kotlin.j2k.ast.Annotation
|
||||
import org.jetbrains.kotlin.load.java.components.JavaAnnotationTargetMapper
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import java.lang.annotation.ElementType
|
||||
import java.lang.annotation.Target
|
||||
|
||||
@@ -31,8 +32,9 @@ class AnnotationConverter(private val converter: Converter) {
|
||||
+ NullableNotNullManager.getInstance(converter.project).nullables
|
||||
+ listOf(CommonClassNames.JAVA_LANG_OVERRIDE, ElementType::class.java.name)).toSet()
|
||||
|
||||
fun isImportNotRequired(annotationName: String): Boolean {
|
||||
return annotationName in annotationsToRemove || annotationName == Target::class.java.name
|
||||
fun isImportNotRequired(fqName: FqName): Boolean {
|
||||
val nameAsString = fqName.asString()
|
||||
return nameAsString in annotationsToRemove || nameAsString == Target::class.java.name
|
||||
}
|
||||
|
||||
fun convertAnnotations(owner: PsiModifierListOwner): Annotations
|
||||
|
||||
@@ -114,7 +114,7 @@ class Converter private constructor(
|
||||
is PsiStatement -> createDefaultCodeConverter().convertStatement(element)
|
||||
is PsiExpression -> createDefaultCodeConverter().convertExpression(element)
|
||||
is PsiImportList -> convertImportList(element)
|
||||
is PsiImportStatementBase -> convertImport(element, false)
|
||||
is PsiImportStatementBase -> convertImport(element, false).singleOrNull()
|
||||
is PsiAnnotation -> annotationConverter.convertAnnotation(element, newLineAfter = false)
|
||||
is PsiPackageStatement -> PackageStatement(quoteKeywords(element.packageName ?: "")).assignPrototype(element)
|
||||
is PsiJavaCodeReferenceElement -> {
|
||||
|
||||
@@ -18,60 +18,136 @@ package org.jetbrains.kotlin.j2k
|
||||
|
||||
import com.intellij.psi.PsiImportList
|
||||
import com.intellij.psi.PsiImportStatementBase
|
||||
import com.intellij.psi.PsiImportStaticStatement
|
||||
import com.intellij.psi.PsiJavaCodeReferenceElement
|
||||
import org.jetbrains.kotlin.asJava.KtLightClass
|
||||
import org.jetbrains.kotlin.asJava.KtLightClassForFacade
|
||||
import org.jetbrains.kotlin.asJava.KtLightDeclaration
|
||||
import org.jetbrains.kotlin.asJava.KtLightMethod
|
||||
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.j2k.ast.Import
|
||||
import org.jetbrains.kotlin.j2k.ast.ImportList
|
||||
import org.jetbrains.kotlin.j2k.ast.assignPrototype
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.renderer.render
|
||||
import org.jetbrains.kotlin.resolve.annotations.hasJvmStaticAnnotation
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
||||
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
||||
|
||||
fun Converter.convertImportList(importList: PsiImportList): ImportList =
|
||||
ImportList(importList.allImportStatements.mapNotNull { convertImport(it, true) }).assignPrototype(importList)
|
||||
fun Converter.convertImportList(importList: PsiImportList): ImportList {
|
||||
val imports = importList.allImportStatements
|
||||
.flatMap { convertImport(it, true) }
|
||||
.distinctBy { it.name } // duplicated imports may appear
|
||||
return ImportList(imports).assignPrototype(importList)
|
||||
}
|
||||
|
||||
fun Converter.convertImport(anImport: PsiImportStatementBase, filter: Boolean): Import? {
|
||||
fun doConvert(): Import? {
|
||||
val reference = anImport.importReference ?: return null
|
||||
val qualifiedName = quoteKeywords(reference.qualifiedName!!)
|
||||
if (anImport.isOnDemand) {
|
||||
return Import(qualifiedName + ".*")
|
||||
fun Converter.convertImport(anImport: PsiImportStatementBase, filter: Boolean): List<Import> {
|
||||
fun doConvert(): List<Import> {
|
||||
val reference = anImport.importReference ?: return emptyList()
|
||||
val fqName = FqName(reference.qualifiedName!!)
|
||||
val onDemand = anImport.isOnDemand
|
||||
return if (filter) {
|
||||
filterImport(fqName, reference, onDemand, anImport is PsiImportStaticStatement)
|
||||
.map { Import(it) }
|
||||
}
|
||||
else {
|
||||
return if (filter) {
|
||||
val filteredName = filterImport(qualifiedName, reference)
|
||||
if (filteredName != null) Import(filteredName) else null
|
||||
listOf(Import(renderImportName(fqName, onDemand)))
|
||||
}
|
||||
}
|
||||
|
||||
return doConvert().map { it.assignPrototype(anImport) }
|
||||
}
|
||||
|
||||
private fun Converter.filterImport(fqName: FqName, ref: PsiJavaCodeReferenceElement, isOnDemand: Boolean, isImportStatic: Boolean): List<String> {
|
||||
if (!isOnDemand) {
|
||||
if (annotationConverter.isImportNotRequired(fqName)) return emptyList()
|
||||
|
||||
// If imported class has a kotlin analog, drop the import
|
||||
if (!JavaToKotlinClassMap.INSTANCE.mapPlatformClass(fqName, DefaultBuiltIns.Instance).isEmpty()) return emptyList()
|
||||
}
|
||||
|
||||
//TODO: how to detect compiled Kotlin here?
|
||||
val target = ref.resolve()
|
||||
if (isImportStatic) {
|
||||
if (isOnDemand) {
|
||||
when (target) {
|
||||
is KtLightClassForFacade -> return listOf(target.getFqName().parent().render() + ".*")
|
||||
|
||||
is KtLightClass -> {
|
||||
val kotlinOrigin = target.kotlinOrigin
|
||||
val importFromObject = when (kotlinOrigin) {
|
||||
is KtObjectDeclaration -> kotlinOrigin
|
||||
is KtClass -> kotlinOrigin.getCompanionObjects().singleOrNull()
|
||||
else -> null
|
||||
}
|
||||
if (importFromObject != null) {
|
||||
val objectFqName = importFromObject.fqName
|
||||
if (objectFqName != null) {
|
||||
// cannot import on demand from object, generate imports for all members
|
||||
return importFromObject.declarations
|
||||
.mapNotNull {
|
||||
val descriptor = services.resolverForConverter.resolveToDescriptor(it) ?: return@mapNotNull null
|
||||
if (descriptor.hasJvmStaticAnnotation() || descriptor is PropertyDescriptor && descriptor.isConst)
|
||||
descriptor.name
|
||||
else
|
||||
null
|
||||
}
|
||||
.distinct()
|
||||
.map { objectFqName.child(it).render() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
Import(qualifiedName)
|
||||
}
|
||||
else {
|
||||
if (target is KtLightDeclaration<*, *>) {
|
||||
val kotlinOrigin = target.kotlinOrigin
|
||||
|
||||
val nameToImport = if (target is KtLightMethod && kotlinOrigin is KtProperty)
|
||||
kotlinOrigin.nameAsName
|
||||
else
|
||||
fqName.shortName()
|
||||
|
||||
if (nameToImport != null) {
|
||||
val originParent = kotlinOrigin?.parent
|
||||
when (originParent) {
|
||||
is KtFile -> { // import of function or property accessor from file facade
|
||||
return listOf(originParent.packageFqName.child(nameToImport).render())
|
||||
}
|
||||
|
||||
is KtClassBody -> {
|
||||
val parentClass = originParent.parent as KtClassOrObject
|
||||
if (parentClass is KtObjectDeclaration && parentClass.isCompanion()) {
|
||||
return parentClass.getFqName()?.child(nameToImport)?.render().singletonOrEmptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
when (target) {
|
||||
is KtLightClassForFacade -> return listOf(target.getFqName().parent().render() + ".*")
|
||||
|
||||
is KtLightClass -> {
|
||||
if (!isOnDemand) {
|
||||
if (isFacadeClassFromLibrary(target)) return emptyList()
|
||||
|
||||
if (isImportedByDefault(target)) return emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return doConvert()?.assignPrototype(anImport)
|
||||
return listOf(renderImportName(fqName, isOnDemand))
|
||||
}
|
||||
|
||||
private fun Converter.filterImport(name: String, ref: PsiJavaCodeReferenceElement): String? {
|
||||
if (annotationConverter.isImportNotRequired(name)) return null
|
||||
|
||||
// If imported class has a kotlin analog, drop the import
|
||||
if (!JavaToKotlinClassMap.INSTANCE.mapPlatformClass(FqName(name), DefaultBuiltIns.Instance).isEmpty()) return null
|
||||
|
||||
val target = ref.resolve()
|
||||
if (target is KtLightClassForFacade) {
|
||||
return target.getFqName().parent().render() + ".*"
|
||||
}
|
||||
else if (target is KtLightClass) {
|
||||
if (isFacadeClassFromLibrary(target)) return null
|
||||
|
||||
if (isImportedByDefault(target)) return null
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
private fun renderImportName(fqName: FqName, isOnDemand: Boolean)
|
||||
= if (isOnDemand) fqName.render() + ".*" else fqName.render()
|
||||
|
||||
private val DEFAULT_IMPORTS_SET: Set<FqName> = JvmPlatform.defaultModuleParameters.defaultImports
|
||||
.filter { it.isAllUnder }
|
||||
|
||||
Vendored
+7
@@ -9,14 +9,19 @@ public open class KotlinClass(public var field: Int) {
|
||||
fun get(i: Int) = 0
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
public fun staticFun(p: Int): Int = p
|
||||
@JvmStatic
|
||||
public var staticVar: Int = 1
|
||||
@JvmStatic
|
||||
public var staticProperty: Int
|
||||
get() = 1
|
||||
set(value) {}
|
||||
|
||||
public fun nullableStaticFun(p: Int?): Int? = p
|
||||
public var nullableStaticVar: Int? = 1
|
||||
|
||||
const val CONST = 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +48,9 @@ public var String.extensionProperty: Int
|
||||
set(value) {}
|
||||
|
||||
public object KotlinObject {
|
||||
@JvmStatic
|
||||
public fun foo(): Int = 1
|
||||
@JvmStatic
|
||||
public var property1: Int = 1
|
||||
public var property2: Int
|
||||
get() = 1
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import static kotlinApi.KotlinApiKt.*;
|
||||
|
||||
class C {
|
||||
int foo() {
|
||||
setExtensionProperty("a", 1);
|
||||
return getExtensionProperty("b");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import kotlinApi.*
|
||||
|
||||
internal class C {
|
||||
fun foo(): Int {
|
||||
"a".extensionProperty = 1
|
||||
return "b".extensionProperty
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import static kotlinApi.KotlinObject.*;
|
||||
|
||||
class C {
|
||||
int bar() {
|
||||
return foo();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import kotlinApi.KotlinObject.foo
|
||||
import kotlinApi.KotlinObject.property1
|
||||
|
||||
internal class C {
|
||||
fun bar(): Int {
|
||||
return foo()
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import static kotlinApi.KotlinClass.CONST;
|
||||
|
||||
public class C {
|
||||
void bar() {
|
||||
System.out.println(CONST);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import kotlinApi.KotlinClass.Companion.CONST
|
||||
|
||||
class C {
|
||||
internal fun bar() {
|
||||
println(CONST)
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import static kotlinApi.KotlinClass.*;
|
||||
|
||||
public class C {
|
||||
void bar() {
|
||||
System.out.println(CONST);
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import kotlinApi.KotlinClass.Companion.staticFun
|
||||
import kotlinApi.KotlinClass.Companion.staticVar
|
||||
import kotlinApi.KotlinClass.Companion.staticProperty
|
||||
import kotlinApi.KotlinClass.Companion.CONST
|
||||
|
||||
class C {
|
||||
internal fun bar() {
|
||||
println(CONST)
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import static kotlinApi.KotlinClass.getStaticProperty;
|
||||
|
||||
class C {
|
||||
int foo() {
|
||||
return getStaticProperty();
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import kotlinApi.KotlinClass.Companion.staticProperty
|
||||
|
||||
internal class C {
|
||||
fun foo(): Int {
|
||||
return staticProperty
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import static kotlinApi.KotlinApiKt.extensionFunction;
|
||||
import static kotlinApi.KotlinApiKt.getExtensionProperty;
|
||||
import static kotlinApi.KotlinApiKt.setExtensionProperty;
|
||||
|
||||
class C {
|
||||
int foo() {
|
||||
extensionFunction(1)
|
||||
setExtensionProperty("a", 1);
|
||||
return getExtensionProperty("b");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import kotlinApi.extensionFunction
|
||||
import kotlinApi.extensionProperty
|
||||
|
||||
internal class C {
|
||||
fun foo(): Int {
|
||||
1.extensionFunction()
|
||||
"a".extensionProperty = 1
|
||||
return "b".extensionProperty
|
||||
}
|
||||
}
|
||||
@@ -3033,6 +3033,42 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
|
||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/kotlinApiAccess/Property2.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StaticImportAllFromFileFacade.java")
|
||||
public void testStaticImportAllFromFileFacade() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/kotlinApiAccess/StaticImportAllFromFileFacade.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StaticImportAllFromObject.java")
|
||||
public void testStaticImportAllFromObject() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/kotlinApiAccess/StaticImportAllFromObject.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StaticImportFromCompanionObject.java")
|
||||
public void testStaticImportFromCompanionObject() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/kotlinApiAccess/StaticImportFromCompanionObject.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StaticImportFromCompanionObject2.java")
|
||||
public void testStaticImportFromCompanionObject2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/kotlinApiAccess/StaticImportFromCompanionObject2.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StaticImportFromCompanionObject3.java")
|
||||
public void testStaticImportFromCompanionObject3() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/kotlinApiAccess/StaticImportFromCompanionObject3.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StaticImportFromFileFacade.java")
|
||||
public void testStaticImportFromFileFacade() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/kotlinApiAccess/StaticImportFromFileFacade.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("j2k/testData/fileOrElement/labelStatement")
|
||||
|
||||
@@ -3033,6 +3033,42 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
|
||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/kotlinApiAccess/Property2.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StaticImportAllFromFileFacade.java")
|
||||
public void testStaticImportAllFromFileFacade() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/kotlinApiAccess/StaticImportAllFromFileFacade.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StaticImportAllFromObject.java")
|
||||
public void testStaticImportAllFromObject() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/kotlinApiAccess/StaticImportAllFromObject.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StaticImportFromCompanionObject.java")
|
||||
public void testStaticImportFromCompanionObject() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/kotlinApiAccess/StaticImportFromCompanionObject.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StaticImportFromCompanionObject2.java")
|
||||
public void testStaticImportFromCompanionObject2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/kotlinApiAccess/StaticImportFromCompanionObject2.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StaticImportFromCompanionObject3.java")
|
||||
public void testStaticImportFromCompanionObject3() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/kotlinApiAccess/StaticImportFromCompanionObject3.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StaticImportFromFileFacade.java")
|
||||
public void testStaticImportFromFileFacade() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/kotlinApiAccess/StaticImportFromFileFacade.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("j2k/testData/fileOrElement/labelStatement")
|
||||
|
||||
Reference in New Issue
Block a user