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.*
import org.jetbrains.kotlin.j2k.ast.Annotation import org.jetbrains.kotlin.j2k.ast.Annotation
import org.jetbrains.kotlin.load.java.components.JavaAnnotationTargetMapper import org.jetbrains.kotlin.load.java.components.JavaAnnotationTargetMapper
import org.jetbrains.kotlin.name.FqName
import java.lang.annotation.ElementType import java.lang.annotation.ElementType
import java.lang.annotation.Target import java.lang.annotation.Target
@@ -31,8 +32,9 @@ class AnnotationConverter(private val converter: Converter) {
+ NullableNotNullManager.getInstance(converter.project).nullables + NullableNotNullManager.getInstance(converter.project).nullables
+ listOf(CommonClassNames.JAVA_LANG_OVERRIDE, ElementType::class.java.name)).toSet() + listOf(CommonClassNames.JAVA_LANG_OVERRIDE, ElementType::class.java.name)).toSet()
fun isImportNotRequired(annotationName: String): Boolean { fun isImportNotRequired(fqName: FqName): Boolean {
return annotationName in annotationsToRemove || annotationName == Target::class.java.name val nameAsString = fqName.asString()
return nameAsString in annotationsToRemove || nameAsString == Target::class.java.name
} }
fun convertAnnotations(owner: PsiModifierListOwner): Annotations fun convertAnnotations(owner: PsiModifierListOwner): Annotations
@@ -114,7 +114,7 @@ class Converter private constructor(
is PsiStatement -> createDefaultCodeConverter().convertStatement(element) is PsiStatement -> createDefaultCodeConverter().convertStatement(element)
is PsiExpression -> createDefaultCodeConverter().convertExpression(element) is PsiExpression -> createDefaultCodeConverter().convertExpression(element)
is PsiImportList -> convertImportList(element) is PsiImportList -> convertImportList(element)
is PsiImportStatementBase -> convertImport(element, false) is PsiImportStatementBase -> convertImport(element, false).singleOrNull()
is PsiAnnotation -> annotationConverter.convertAnnotation(element, newLineAfter = false) is PsiAnnotation -> annotationConverter.convertAnnotation(element, newLineAfter = false)
is PsiPackageStatement -> PackageStatement(quoteKeywords(element.packageName ?: "")).assignPrototype(element) is PsiPackageStatement -> PackageStatement(quoteKeywords(element.packageName ?: "")).assignPrototype(element)
is PsiJavaCodeReferenceElement -> { is PsiJavaCodeReferenceElement -> {
@@ -18,60 +18,136 @@ package org.jetbrains.kotlin.j2k
import com.intellij.psi.PsiImportList import com.intellij.psi.PsiImportList
import com.intellij.psi.PsiImportStatementBase import com.intellij.psi.PsiImportStatementBase
import com.intellij.psi.PsiImportStaticStatement
import com.intellij.psi.PsiJavaCodeReferenceElement import com.intellij.psi.PsiJavaCodeReferenceElement
import org.jetbrains.kotlin.asJava.KtLightClass import org.jetbrains.kotlin.asJava.KtLightClass
import org.jetbrains.kotlin.asJava.KtLightClassForFacade 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.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.j2k.ast.Import import org.jetbrains.kotlin.j2k.ast.Import
import org.jetbrains.kotlin.j2k.ast.ImportList import org.jetbrains.kotlin.j2k.ast.ImportList
import org.jetbrains.kotlin.j2k.ast.assignPrototype import org.jetbrains.kotlin.j2k.ast.assignPrototype
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.renderer.render 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.resolve.jvm.platform.JvmPlatform
import org.jetbrains.kotlin.utils.singletonOrEmptyList
fun Converter.convertImportList(importList: PsiImportList): ImportList = fun Converter.convertImportList(importList: PsiImportList): ImportList {
ImportList(importList.allImportStatements.mapNotNull { convertImport(it, true) }).assignPrototype(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 Converter.convertImport(anImport: PsiImportStatementBase, filter: Boolean): List<Import> {
fun doConvert(): Import? { fun doConvert(): List<Import> {
val reference = anImport.importReference ?: return null val reference = anImport.importReference ?: return emptyList()
val qualifiedName = quoteKeywords(reference.qualifiedName!!) val fqName = FqName(reference.qualifiedName!!)
if (anImport.isOnDemand) { val onDemand = anImport.isOnDemand
return Import(qualifiedName + ".*") return if (filter) {
filterImport(fqName, reference, onDemand, anImport is PsiImportStaticStatement)
.map { Import(it) }
} }
else { else {
return if (filter) { listOf(Import(renderImportName(fqName, onDemand)))
val filteredName = filterImport(qualifiedName, reference) }
if (filteredName != null) Import(filteredName) else null }
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? { private fun renderImportName(fqName: FqName, isOnDemand: Boolean)
if (annotationConverter.isImportNotRequired(name)) return null = if (isOnDemand) fqName.render() + ".*" else fqName.render()
// 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 val DEFAULT_IMPORTS_SET: Set<FqName> = JvmPlatform.defaultModuleParameters.defaultImports private val DEFAULT_IMPORTS_SET: Set<FqName> = JvmPlatform.defaultModuleParameters.defaultImports
.filter { it.isAllUnder } .filter { it.isAllUnder }
+7
View File
@@ -9,14 +9,19 @@ public open class KotlinClass(public var field: Int) {
fun get(i: Int) = 0 fun get(i: Int) = 0
companion object { companion object {
@JvmStatic
public fun staticFun(p: Int): Int = p public fun staticFun(p: Int): Int = p
@JvmStatic
public var staticVar: Int = 1 public var staticVar: Int = 1
@JvmStatic
public var staticProperty: Int public var staticProperty: Int
get() = 1 get() = 1
set(value) {} set(value) {}
public fun nullableStaticFun(p: Int?): Int? = p public fun nullableStaticFun(p: Int?): Int? = p
public var nullableStaticVar: Int? = 1 public var nullableStaticVar: Int? = 1
const val CONST = 0
} }
} }
@@ -43,7 +48,9 @@ public var String.extensionProperty: Int
set(value) {} set(value) {}
public object KotlinObject { public object KotlinObject {
@JvmStatic
public fun foo(): Int = 1 public fun foo(): Int = 1
@JvmStatic
public var property1: Int = 1 public var property1: Int = 1
public var property2: Int public var property2: Int
get() = 1 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"); String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/kotlinApiAccess/Property2.java");
doTest(fileName); 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") @TestMetadata("j2k/testData/fileOrElement/labelStatement")
@@ -3033,6 +3033,42 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/kotlinApiAccess/Property2.java"); String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/kotlinApiAccess/Property2.java");
doTest(fileName); 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") @TestMetadata("j2k/testData/fileOrElement/labelStatement")