J2K: specify type for variables with anonymous type if they have write accesses

This commit is contained in:
Natalia Ukhorskaya
2016-03-25 16:28:59 +03:00
parent 23908fb8f3
commit 470027bfd8
6 changed files with 106 additions and 2 deletions
+1
View File
@@ -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
+25 -2
View File
@@ -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)
+33
View File
@@ -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
View File
@@ -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
}
}
}