J2K: specify type for variables with anonymous type if they have write accesses
This commit is contained in:
@@ -23,6 +23,7 @@
|
||||
- Place comments from the middle of the call to the end
|
||||
- Drop line breaks between operator arguments (except '+', "-", "&&" and "||")
|
||||
- Add non-null assertions on call site for non-null parameters
|
||||
- Specify type for variables with anonymous type if they have write accesses
|
||||
|
||||
### Tools. Android
|
||||
- Fixed sequential build with kapt and stubs enabled when Kotlin source file was modified and no Java source files were modified
|
||||
|
||||
@@ -451,13 +451,36 @@ class Converter private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
val initializerType = createDefaultCodeConverter().convertedExpressionType(initializer, variable.type)
|
||||
// do not add explicit type when initializer is not resolved, let user add it if really needed
|
||||
if (initializerType is ErrorType) return false
|
||||
|
||||
if (shouldSpecifyTypeForAnonymousType(variable, initializerType)) return true
|
||||
|
||||
if (canChangeType) return false
|
||||
|
||||
var initializerType = createDefaultCodeConverter().convertedExpressionType(initializer, variable.type)
|
||||
if (initializerType is ErrorType) return false // do not add explicit type when initializer is not resolved, let user add it if really needed
|
||||
return type != initializerType
|
||||
}
|
||||
|
||||
// add explicit type when initializer has anonymous type,
|
||||
// the variable is private or local and
|
||||
// it has write accesses or is stored to another variable
|
||||
private fun shouldSpecifyTypeForAnonymousType(variable: PsiVariable, initializerType: Type): Boolean {
|
||||
if (initializerType !is ClassType || !initializerType.isAnonymous()) return false
|
||||
|
||||
val scope: PsiElement? =
|
||||
when {
|
||||
variable is PsiField && variable.hasModifierProperty(PsiModifier.PRIVATE) -> variable.containingClass
|
||||
variable is PsiLocalVariable -> variable.getContainingMethod()
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (scope == null) return false
|
||||
|
||||
return variable.hasWriteAccesses(referenceSearcher, scope) ||
|
||||
variable.isInVariableInitializer(referenceSearcher, scope)
|
||||
}
|
||||
|
||||
fun convertMethod(
|
||||
method: PsiMethod,
|
||||
fieldsToDrop: MutableSet<PsiField>?,
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.j2k
|
||||
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.psi.util.PsiUtil
|
||||
|
||||
interface ReferenceSearcher {
|
||||
@@ -67,6 +68,13 @@ fun PsiField.isVar(searcher: ReferenceSearcher): Boolean {
|
||||
fun PsiVariable.hasWriteAccesses(searcher: ReferenceSearcher, scope: PsiElement?): Boolean
|
||||
= if (scope != null) searcher.findVariableUsages(this, scope).any { PsiUtil.isAccessedForWriting(it) } else false
|
||||
|
||||
fun PsiVariable.isInVariableInitializer(searcher: ReferenceSearcher, scope: PsiElement?): Boolean {
|
||||
return if (scope != null) searcher.findVariableUsages(this, scope).any {
|
||||
val parent = PsiTreeUtil.skipParentsOfType(it, PsiParenthesizedExpression::class.java)
|
||||
parent is PsiVariable && parent.initializer == it
|
||||
} else false
|
||||
}
|
||||
|
||||
object EmptyReferenceSearcher: ReferenceSearcher {
|
||||
override fun findLocalUsages(element: PsiElement, scope: PsiElement): Collection<PsiReference> = emptyList()
|
||||
override fun hasInheritors(`class`: PsiClass) = false
|
||||
|
||||
@@ -100,6 +100,8 @@ class ClassType(val referenceElement: ReferenceElement, nullability: Nullability
|
||||
|
||||
override fun toNotNullType(): Type = ClassType(referenceElement, Nullability.NotNull, settings).assignPrototypesFrom(this)
|
||||
override fun toNullableType(): Type = ClassType(referenceElement, Nullability.Nullable, settings).assignPrototypesFrom(this)
|
||||
|
||||
fun isAnonymous() = referenceElement.name.isEmpty
|
||||
}
|
||||
|
||||
class ArrayType(val elementType: Type, nullability: Nullability, settings: ConverterSettings)
|
||||
|
||||
@@ -25,4 +25,37 @@ class A {
|
||||
field9 = null;
|
||||
field10 = null;
|
||||
}
|
||||
|
||||
interface I
|
||||
|
||||
private I anonymous = new I() {
|
||||
};
|
||||
|
||||
public I anonymous2 = new I() {
|
||||
};
|
||||
|
||||
private I anonymous3 = new I() {
|
||||
};
|
||||
|
||||
private I iimpl = anonymous;
|
||||
|
||||
void testAnonymousObject(Object i) {
|
||||
if (true) {
|
||||
iimpl = (I) i;
|
||||
}
|
||||
else if (true) {
|
||||
anonymous3 = (I) i;
|
||||
}
|
||||
|
||||
I anonymousLocal1 = new I() {
|
||||
};
|
||||
|
||||
I anonymousLocal2 = new I() {
|
||||
};
|
||||
|
||||
I iimpl = anonymousLocal1;
|
||||
if (true) {
|
||||
anonymousLocal2 = (I) i;
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -26,4 +26,41 @@ internal class A {
|
||||
field9 = null
|
||||
field10 = null
|
||||
}
|
||||
|
||||
internal interface I
|
||||
|
||||
private val anonymous: I = object : I {
|
||||
|
||||
}
|
||||
|
||||
var anonymous2: I = object : I {
|
||||
|
||||
}
|
||||
|
||||
private var anonymous3: I = object : I {
|
||||
|
||||
}
|
||||
|
||||
private var iimpl = anonymous
|
||||
|
||||
fun testAnonymousObject(i: Any) {
|
||||
if (true) {
|
||||
iimpl = i as I
|
||||
} else if (true) {
|
||||
anonymous3 = i as I
|
||||
}
|
||||
|
||||
val anonymousLocal1: I = object : I {
|
||||
|
||||
}
|
||||
|
||||
var anonymousLocal2: I = object : I {
|
||||
|
||||
}
|
||||
|
||||
val iimpl = anonymousLocal1
|
||||
if (true) {
|
||||
anonymousLocal2 = i as I
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user