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) }
fun Converter.convertImport(anImport: PsiImportStatementBase, filter: Boolean): Import? { .distinctBy { it.name } // duplicated imports may appear
fun doConvert(): Import? { return ImportList(imports).assignPrototype(importList)
val reference = anImport.importReference ?: return null
val qualifiedName = quoteKeywords(reference.qualifiedName!!)
if (anImport.isOnDemand) {
return Import(qualifiedName + ".*")
} }
else {
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) { return if (filter) {
val filteredName = filterImport(qualifiedName, reference) filterImport(fqName, reference, onDemand, anImport is PsiImportStaticStatement)
if (filteredName != null) Import(filteredName) else null .map { Import(it) }
} }
else { else {
Import(qualifiedName) listOf(Import(renderImportName(fqName, onDemand)))
}
} }
} }
return doConvert()?.assignPrototype(anImport) return doConvert().map { it.assignPrototype(anImport) }
} }
private fun Converter.filterImport(name: String, ref: PsiJavaCodeReferenceElement): String? { private fun Converter.filterImport(fqName: FqName, ref: PsiJavaCodeReferenceElement, isOnDemand: Boolean, isImportStatic: Boolean): List<String> {
if (annotationConverter.isImportNotRequired(name)) return null if (!isOnDemand) {
if (annotationConverter.isImportNotRequired(fqName)) return emptyList()
// If imported class has a kotlin analog, drop the import // If imported class has a kotlin analog, drop the import
if (!JavaToKotlinClassMap.INSTANCE.mapPlatformClass(FqName(name), DefaultBuiltIns.Instance).isEmpty()) return null if (!JavaToKotlinClassMap.INSTANCE.mapPlatformClass(fqName, DefaultBuiltIns.Instance).isEmpty()) return emptyList()
}
//TODO: how to detect compiled Kotlin here?
val target = ref.resolve() val target = ref.resolve()
if (target is KtLightClassForFacade) { if (isImportStatic) {
return target.getFqName().parent().render() + ".*" if (isOnDemand) {
} when (target) {
else if (target is KtLightClass) { is KtLightClassForFacade -> return listOf(target.getFqName().parent().render() + ".*")
if (isFacadeClassFromLibrary(target)) return null
if (isImportedByDefault(target)) return null 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 {
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())
} }
return name 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 listOf(renderImportName(fqName, isOnDemand))
}
private fun renderImportName(fqName: FqName, isOnDemand: Boolean)
= if (isOnDemand) fqName.render() + ".*" else fqName.render()
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")