J2K breaks code if applied to a java class with static field which is accessed from another java class

#KT-11587 Fixed
This commit is contained in:
Natalia Ukhorskaya
2016-03-24 14:36:46 +03:00
parent c7ae9981cb
commit 79bac598bf
16 changed files with 97 additions and 72 deletions
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.j2k
import com.intellij.psi.* import com.intellij.psi.*
import org.jetbrains.kotlin.j2k.ast.* import org.jetbrains.kotlin.j2k.ast.*
import org.jetbrains.kotlin.j2k.usageProcessing.AccessorToPropertyProcessing import org.jetbrains.kotlin.j2k.usageProcessing.AccessorToPropertyProcessing
import org.jetbrains.kotlin.j2k.usageProcessing.MethodIntoObjectProcessing import org.jetbrains.kotlin.j2k.usageProcessing.MemberIntoObjectProcessing
import org.jetbrains.kotlin.j2k.usageProcessing.ToObjectWithOnlyMethodsProcessing import org.jetbrains.kotlin.j2k.usageProcessing.ToObjectWithOnlyMethodsProcessing
import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.name.SpecialNames
@@ -100,9 +100,8 @@ class ClassBodyConverter(private val psiClass: PsiClass,
} }
else { else {
for (psiMember in psiMembers) { for (psiMember in psiMembers) {
if (psiMember is PsiMethod /* fields in object can be accessed as fields from java */ if (!psiMember.hasModifierProperty(PsiModifier.PRIVATE)) {
&& !psiMember.hasModifierProperty(PsiModifier.PRIVATE)) { converter.addUsageProcessing(MemberIntoObjectProcessing(psiMember, JvmAbi.INSTANCE_FIELD))
converter.addUsageProcessing(MethodIntoObjectProcessing(psiMember, JvmAbi.INSTANCE_FIELD))
} }
} }
} }
@@ -123,9 +122,8 @@ class ClassBodyConverter(private val psiClass: PsiClass,
} }
else if (useCompanionObject && member !is Class && psiMember !is PsiEnumConstant && psiMember.hasModifierProperty(PsiModifier.STATIC)) { else if (useCompanionObject && member !is Class && psiMember !is PsiEnumConstant && psiMember.hasModifierProperty(PsiModifier.STATIC)) {
companionObjectMembers.add(member) companionObjectMembers.add(member)
if (psiMember is PsiMethod /* fields in companion object can be accessed as fields from java */ if (!psiMember.hasModifierProperty(PsiModifier.PRIVATE)) {
&& !psiMember.hasModifierProperty(PsiModifier.PRIVATE)) { converter.addUsageProcessing(MemberIntoObjectProcessing(psiMember, SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT.identifier))
converter.addUsageProcessing(MethodIntoObjectProcessing(psiMember, SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT.identifier))
} }
} }
else { else {
@@ -152,7 +152,7 @@ class JavaToKotlinConverter(
val map: Map<PsiElement, Collection<UsageProcessing>> = usageProcessings.values val map: Map<PsiElement, Collection<UsageProcessing>> = usageProcessings.values
.flatMap { it } .flatMap { it }
.filter { it.javaCodeProcessor != null || it.kotlinCodeProcessor != null } .filter { it.javaCodeProcessors != null || it.kotlinCodeProcessors != null }
.groupBy { it.targetElement } .groupBy { it.targetElement }
if (map.isEmpty()) return null if (map.isEmpty()) return null
@@ -171,8 +171,8 @@ class JavaToKotlinConverter(
ProgressManager.getInstance().runProcess( ProgressManager.getInstance().runProcess(
{ {
val searchJava = processings.any { it.javaCodeProcessor != null } val searchJava = processings.any { it.javaCodeProcessors != null }
val searchKotlin = processings.any { it.kotlinCodeProcessor != null } val searchKotlin = processings.any { it.kotlinCodeProcessors != null }
services.referenceSearcher.findUsagesForExternalCodeProcessing(psiElement, searchJava, searchKotlin) services.referenceSearcher.findUsagesForExternalCodeProcessing(psiElement, searchJava, searchKotlin)
.filterNot { inConversionScope(it.element) } .filterNot { inConversionScope(it.element) }
.mapTo(refs) { ReferenceInfo(it, psiElement, it.element.containingFile, processings) } .mapTo(refs) { ReferenceInfo(it, psiElement, it.element.containingFile, processings) }
@@ -191,8 +191,8 @@ class JavaToKotlinConverter(
ReferenceLoop@ ReferenceLoop@
for ((reference, target, file, processings) in fileRefs.sortedWith(ReferenceComparator)) { for ((reference, target, file, processings) in fileRefs.sortedWith(ReferenceComparator)) {
val processors = when (reference.element.language) { val processors = when (reference.element.language) {
JavaLanguage.INSTANCE -> processings.mapNotNull { it.javaCodeProcessor } JavaLanguage.INSTANCE -> processings.flatMap { it.javaCodeProcessors }
KotlinLanguage.INSTANCE -> processings.mapNotNull { it.kotlinCodeProcessor } KotlinLanguage.INSTANCE -> processings.flatMap { it.kotlinCodeProcessors }
else -> continue@ReferenceLoop else -> continue@ReferenceLoop
} }
@@ -21,11 +21,12 @@ import org.jetbrains.kotlin.j2k.AccessorKind
import org.jetbrains.kotlin.j2k.CodeConverter import org.jetbrains.kotlin.j2k.CodeConverter
import org.jetbrains.kotlin.j2k.ast.* import org.jetbrains.kotlin.j2k.ast.*
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
class AccessorToPropertyProcessing(val accessorMethod: PsiMethod, val accessorKind: AccessorKind, val propertyName: String) : UsageProcessing { class AccessorToPropertyProcessing(val accessorMethod: PsiMethod, val accessorKind: AccessorKind, val propertyName: String) : UsageProcessing {
override val targetElement: PsiElement get() = accessorMethod override val targetElement: PsiElement get() = accessorMethod
override val convertedCodeProcessor = object: ConvertedCodeProcessor { override val convertedCodeProcessor = object : ConvertedCodeProcessor {
override fun convertMethodUsage(methodCall: PsiMethodCallExpression, codeConverter: CodeConverter): Expression? { override fun convertMethodUsage(methodCall: PsiMethodCallExpression, codeConverter: CodeConverter): Expression? {
val isNullable = codeConverter.typeConverter.methodNullability(accessorMethod).isNullable(codeConverter.settings) val isNullable = codeConverter.typeConverter.methodNullability(accessorMethod).isNullable(codeConverter.settings)
@@ -47,44 +48,47 @@ class AccessorToPropertyProcessing(val accessorMethod: PsiMethod, val accessorKi
} }
} }
override val javaCodeProcessor: ExternalCodeProcessor? = null override val javaCodeProcessors = emptyList<ExternalCodeProcessor>()
override val kotlinCodeProcessor: ExternalCodeProcessor? = if (accessorMethod.hasModifierProperty(PsiModifier.PRIVATE)) override val kotlinCodeProcessors =
null if (accessorMethod.hasModifierProperty(PsiModifier.PRIVATE))
else emptyList()
object : ExternalCodeProcessor { else
override fun processUsage(reference: PsiReference): Array<PsiReference>? { AccessorToPropertyProcessor().singletonList()
val nameExpr = reference.element as? KtSimpleNameExpression ?: return null
val callExpr = nameExpr.parent as? KtCallExpression ?: return null
val arguments = callExpr.valueArguments inner class AccessorToPropertyProcessor: ExternalCodeProcessor {
override fun processUsage(reference: PsiReference): Array<PsiReference>? {
val nameExpr = reference.element as? KtSimpleNameExpression ?: return null
val callExpr = nameExpr.parent as? KtCallExpression ?: return null
val factory = KtPsiFactory(nameExpr.project) val arguments = callExpr.valueArguments
var propertyNameExpr = factory.createSimpleName(propertyName)
if (accessorKind == AccessorKind.GETTER) { val factory = KtPsiFactory(nameExpr.project)
if (arguments.size != 0) return null // incorrect call var propertyNameExpr = factory.createSimpleName(propertyName)
propertyNameExpr = callExpr.replace(propertyNameExpr) as KtSimpleNameExpression if (accessorKind == AccessorKind.GETTER) {
return propertyNameExpr.references if (arguments.size != 0) return null // incorrect call
propertyNameExpr = callExpr.replace(propertyNameExpr) as KtSimpleNameExpression
return propertyNameExpr.references
}
else {
val value = arguments.singleOrNull()?.getArgumentExpression() ?: return null
var assignment = factory.createExpression("a = b") as KtBinaryExpression
assignment.right!!.replace(value)
val qualifiedExpression = callExpr.parent as? KtQualifiedExpression
if (qualifiedExpression != null && qualifiedExpression.selectorExpression == callExpr) {
callExpr.replace(propertyNameExpr)
assignment.left!!.replace(qualifiedExpression)
assignment = qualifiedExpression.replace(assignment) as KtBinaryExpression
return (assignment.left as KtQualifiedExpression).selectorExpression!!.references
} }
else { else {
val value = arguments.singleOrNull()?.getArgumentExpression() ?: return null assignment.left!!.replace(propertyNameExpr)
var assignment = factory.createExpression("a = b") as KtBinaryExpression assignment = callExpr.replace(assignment) as KtBinaryExpression
assignment.right!!.replace(value) return assignment.left!!.references
val qualifiedExpression = callExpr.parent as? KtQualifiedExpression
if (qualifiedExpression != null && qualifiedExpression.selectorExpression == callExpr) {
callExpr.replace(propertyNameExpr)
assignment.left!!.replace(qualifiedExpression)
assignment = qualifiedExpression.replace(assignment) as KtBinaryExpression
return (assignment.left as KtQualifiedExpression).selectorExpression!!.references
}
else {
assignment.left!!.replace(propertyNameExpr)
assignment = callExpr.replace(assignment) as KtBinaryExpression
return assignment.left!!.references
}
} }
} }
} }
}
} }
@@ -21,6 +21,7 @@ import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.j2k.AccessorKind import org.jetbrains.kotlin.j2k.AccessorKind
import org.jetbrains.kotlin.j2k.CodeConverter import org.jetbrains.kotlin.j2k.CodeConverter
import org.jetbrains.kotlin.j2k.ast.* import org.jetbrains.kotlin.j2k.ast.*
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
class FieldToPropertyProcessing(val field: PsiField, val propertyName: String, val isNullable: Boolean) : UsageProcessing { class FieldToPropertyProcessing(val field: PsiField, val propertyName: String, val isNullable: Boolean) : UsageProcessing {
override val targetElement: PsiElement get() = this.field override val targetElement: PsiElement get() = this.field
@@ -28,16 +29,19 @@ class FieldToPropertyProcessing(val field: PsiField, val propertyName: String, v
override val convertedCodeProcessor: ConvertedCodeProcessor? = override val convertedCodeProcessor: ConvertedCodeProcessor? =
if (field.name != propertyName) MyConvertedCodeProcessor() else null if (field.name != propertyName) MyConvertedCodeProcessor() else null
override var javaCodeProcessor = if (field.hasModifierProperty(PsiModifier.PRIVATE)) override var javaCodeProcessors =
null if (field.hasModifierProperty(PsiModifier.PRIVATE))
else if (!field.hasModifierProperty(PsiModifier.STATIC)) emptyList()
UseAccessorsJavaCodeProcessor() else if (field.name != propertyName)
else if (field.name != propertyName) listOf(ElementRenamedCodeProcessor(propertyName), UseAccessorsJavaCodeProcessor())
ElementRenamedCodeProcessor(propertyName) else
else UseAccessorsJavaCodeProcessor().singletonList()
null
override val kotlinCodeProcessor = if (field.name != propertyName) ElementRenamedCodeProcessor(propertyName) else null override val kotlinCodeProcessors =
if (field.name != propertyName)
ElementRenamedCodeProcessor(propertyName).singletonList()
else
emptyList()
private inner class MyConvertedCodeProcessor : ConvertedCodeProcessor { private inner class MyConvertedCodeProcessor : ConvertedCodeProcessor {
override fun convertVariableUsage(expression: PsiReferenceExpression, codeConverter: CodeConverter): Expression? { override fun convertVariableUsage(expression: PsiReferenceExpression, codeConverter: CodeConverter): Expression? {
@@ -17,17 +17,22 @@
package org.jetbrains.kotlin.j2k.usageProcessing package org.jetbrains.kotlin.j2k.usageProcessing
import com.intellij.psi.* import com.intellij.psi.*
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
class MethodIntoObjectProcessing(private val method: PsiMethod, private val objectName: String) : UsageProcessing { class MemberIntoObjectProcessing(private val member: PsiMember, private val objectName: String) : UsageProcessing {
override val targetElement: PsiElement get() = method override val targetElement: PsiElement get() = member
override val convertedCodeProcessor: ConvertedCodeProcessor? get() = null override val convertedCodeProcessor: ConvertedCodeProcessor? get() = null
override val javaCodeProcessor = object: ExternalCodeProcessor { override val javaCodeProcessors = AppendObjectNameProcessor().singletonList()
override val kotlinCodeProcessors = emptyList<ExternalCodeProcessor>()
inner class AppendObjectNameProcessor : ExternalCodeProcessor {
override fun processUsage(reference: PsiReference): Array<PsiReference>? { override fun processUsage(reference: PsiReference): Array<PsiReference>? {
val refExpr = reference.element as? PsiReferenceExpression ?: return null val refExpr = reference.element as? PsiReferenceExpression ?: return null
val qualifier = refExpr.qualifierExpression val qualifier = refExpr.qualifierExpression
val factory = PsiElementFactory.SERVICE.getInstance(method.project) val factory = PsiElementFactory.SERVICE.getInstance(member.project)
if (qualifier != null) { if (qualifier != null) {
val newQualifier = factory.createExpressionFromText(qualifier.text + "." + objectName, null) val newQualifier = factory.createExpressionFromText(qualifier.text + "." + objectName, null)
qualifier.replace(newQualifier) qualifier.replace(newQualifier)
@@ -40,6 +45,4 @@ class MethodIntoObjectProcessing(private val method: PsiMethod, private val obje
} }
} }
} }
override val kotlinCodeProcessor: ExternalCodeProcessor? get() = null
} }
@@ -18,13 +18,18 @@ package org.jetbrains.kotlin.j2k.usageProcessing
import com.intellij.psi.* import com.intellij.psi.*
import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
class ToObjectWithOnlyMethodsProcessing(private val psiClass: PsiClass) : UsageProcessing { class ToObjectWithOnlyMethodsProcessing(private val psiClass: PsiClass) : UsageProcessing {
override val targetElement: PsiElement get() = psiClass override val targetElement: PsiElement get() = psiClass
override val convertedCodeProcessor: ConvertedCodeProcessor? get() = null override val convertedCodeProcessor: ConvertedCodeProcessor? get() = null
override val javaCodeProcessor = object: ExternalCodeProcessor { override val javaCodeProcessors = ToObjectWithOnlyMethodsProcessor().singletonList()
override val kotlinCodeProcessors = emptyList<ExternalCodeProcessor>()
inner class ToObjectWithOnlyMethodsProcessor: ExternalCodeProcessor {
override fun processUsage(reference: PsiReference): Array<PsiReference>? { override fun processUsage(reference: PsiReference): Array<PsiReference>? {
val refExpr = reference.element as? PsiReferenceExpression ?: return null val refExpr = reference.element as? PsiReferenceExpression ?: return null
val factory = PsiElementFactory.SERVICE.getInstance(psiClass.project) val factory = PsiElementFactory.SERVICE.getInstance(psiClass.project)
@@ -33,6 +38,4 @@ class ToObjectWithOnlyMethodsProcessing(private val psiClass: PsiClass) : UsageP
return arrayOf(qualifiedExpr) return arrayOf(qualifiedExpr)
} }
} }
override val kotlinCodeProcessor: ExternalCodeProcessor? get() = null
} }
@@ -24,8 +24,8 @@ import org.jetbrains.kotlin.j2k.ast.Expression
interface UsageProcessing { interface UsageProcessing {
val targetElement: PsiElement val targetElement: PsiElement
val convertedCodeProcessor: ConvertedCodeProcessor? val convertedCodeProcessor: ConvertedCodeProcessor?
val javaCodeProcessor: ExternalCodeProcessor? val javaCodeProcessors: List<ExternalCodeProcessor>
val kotlinCodeProcessor: ExternalCodeProcessor? val kotlinCodeProcessors: List<ExternalCodeProcessor>
} }
interface ConvertedCodeProcessor { interface ConvertedCodeProcessor {
+2
View File
@@ -8,4 +8,6 @@ public class JavaClass {
public int getProperty() { public int getProperty() {
return myProperty; return myProperty;
} }
public static final String NAME = "Bar";
} }
+5
View File
@@ -5,4 +5,9 @@ open class JavaClass {
var property = 0 var property = 0
protected set protected set
companion object {
val NAME = "Bar"
}
} }
@@ -9,5 +9,7 @@ public class C extends JavaClass {
field = myProperty; field = myProperty;
field *= 2; field *= 2;
field = field * 2; field = field * 2;
String s = JavaClass.NAME;
} }
} }
@@ -9,5 +9,7 @@ public class C extends JavaClass {
setField(getProperty()); setField(getProperty());
setField(getField() * 2); setField(getField() * 2);
setField(getField() * 2); setField(getField() * 2);
String s = JavaClass.Companion.getNAME();
} }
} }
@@ -6,6 +6,7 @@ class ClassWithStatics {
public static void staticMethod(int p) {} public static void staticMethod(int p) {}
public static final int staticField = 1; public static final int staticField = 1;
public static int staticNonFinalField = 1;
protected static int ourValue = 0; protected static int ourValue = 0;
@@ -10,6 +10,7 @@ internal open class ClassWithStatics {
} }
val staticField = 1 val staticField = 1
var staticNonFinalField = 1
var value = 0 var value = 0
} }
@@ -4,7 +4,7 @@ class C {
void foo(ClassWithStatics c) { void foo(ClassWithStatics c) {
ClassWithStatics.staticMethod(ClassWithStatics.staticField); ClassWithStatics.staticMethod(ClassWithStatics.staticField);
c.instanceMethod(); c.instanceMethod();
ClassWithStatics.staticField += 2; ClassWithStatics.staticNonFinalField += 2;
} }
void methodReferences() { void methodReferences() {
@@ -2,9 +2,9 @@ package test;
class C { class C {
void foo(ClassWithStatics c) { void foo(ClassWithStatics c) {
ClassWithStatics.Companion.staticMethod(ClassWithStatics.staticField); ClassWithStatics.Companion.staticMethod(ClassWithStatics.Companion.getStaticField());
c.instanceMethod(); c.instanceMethod();
ClassWithStatics.staticField += 2; ClassWithStatics.Companion.setStaticNonFinalField(ClassWithStatics.Companion.getStaticNonFinalField() + 2);
} }
void methodReferences() { void methodReferences() {
@@ -15,7 +15,7 @@ class C {
class D extends ClassWithStatics { class D extends ClassWithStatics {
void foo() { void foo() {
Companion.staticMethod(staticField); Companion.staticMethod(Companion.getStaticField());
value *= 2; Companion.setValue(Companion.getValue() * 2);
} }
} }
@@ -2,8 +2,8 @@ package test;
class C { class C {
void foo() { void foo() {
Utils.INSTANCE.foo1(Utils.staticField); Utils.INSTANCE.foo1(Utils.INSTANCE.getStaticField());
Utils.staticField += Utils.INSTANCE.foo2(); Utils.INSTANCE.setStaticField(Utils.INSTANCE.getStaticField() + Utils.INSTANCE.foo2());
PureUtils.INSTANCE.foo1(PureUtils.INSTANCE.foo2()) PureUtils.INSTANCE.foo1(PureUtils.INSTANCE.foo2())
} }
} }