KT-12039 Convert Java to Kotlin -- static imports are imported as Class.CONST (missing .Companion)

#KT-12039 Fixed
This commit is contained in:
Valentin Kipyatkov
2016-04-28 16:40:09 +03:00
parent a80d36e8eb
commit a8eef5effc
18 changed files with 289 additions and 35 deletions
@@ -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 }
+7
View File
@@ -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()
}
}
@@ -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)
}
}
@@ -0,0 +1,7 @@
import static kotlinApi.KotlinClass.*;
public class C {
void bar() {
System.out.println(CONST);
}
}
@@ -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)
}
}
@@ -0,0 +1,7 @@
import static kotlinApi.KotlinClass.getStaticProperty;
class C {
int foo() {
return getStaticProperty();
}
}
@@ -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")