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