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
|
- Place comments from the middle of the call to the end
|
||||||
- Drop line breaks between operator arguments (except '+', "-", "&&" and "||")
|
- Drop line breaks between operator arguments (except '+', "-", "&&" and "||")
|
||||||
- Add non-null assertions on call site for non-null parameters
|
- 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
|
### Tools. Android
|
||||||
- Fixed sequential build with kapt and stubs enabled when Kotlin source file was modified and no Java source files were modified
|
- 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
|
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
|
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(
|
fun convertMethod(
|
||||||
method: PsiMethod,
|
method: PsiMethod,
|
||||||
fieldsToDrop: MutableSet<PsiField>?,
|
fieldsToDrop: MutableSet<PsiField>?,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.j2k
|
package org.jetbrains.kotlin.j2k
|
||||||
|
|
||||||
import com.intellij.psi.*
|
import com.intellij.psi.*
|
||||||
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
import com.intellij.psi.util.PsiUtil
|
import com.intellij.psi.util.PsiUtil
|
||||||
|
|
||||||
interface ReferenceSearcher {
|
interface ReferenceSearcher {
|
||||||
@@ -67,6 +68,13 @@ fun PsiField.isVar(searcher: ReferenceSearcher): Boolean {
|
|||||||
fun PsiVariable.hasWriteAccesses(searcher: ReferenceSearcher, scope: PsiElement?): Boolean
|
fun PsiVariable.hasWriteAccesses(searcher: ReferenceSearcher, scope: PsiElement?): Boolean
|
||||||
= if (scope != null) searcher.findVariableUsages(this, scope).any { PsiUtil.isAccessedForWriting(it) } else false
|
= 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 {
|
object EmptyReferenceSearcher: ReferenceSearcher {
|
||||||
override fun findLocalUsages(element: PsiElement, scope: PsiElement): Collection<PsiReference> = emptyList()
|
override fun findLocalUsages(element: PsiElement, scope: PsiElement): Collection<PsiReference> = emptyList()
|
||||||
override fun hasInheritors(`class`: PsiClass) = false
|
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 toNotNullType(): Type = ClassType(referenceElement, Nullability.NotNull, settings).assignPrototypesFrom(this)
|
||||||
override fun toNullableType(): Type = ClassType(referenceElement, Nullability.Nullable, 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)
|
class ArrayType(val elementType: Type, nullability: Nullability, settings: ConverterSettings)
|
||||||
|
|||||||
@@ -25,4 +25,37 @@ class A {
|
|||||||
field9 = null;
|
field9 = null;
|
||||||
field10 = 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
|
field9 = null
|
||||||
field10 = 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