KT-4418 Converter from java should honor "@Nullable" annotations

#KT-4418 Fixed
This commit is contained in:
Valentin Kipyatkov
2014-06-04 16:00:05 +04:00
parent 118b23061b
commit f96721fa7e
10 changed files with 117 additions and 56 deletions
+25 -25
View File
@@ -206,28 +206,22 @@ public class Converter(val project: Project, val settings: ConverterSettings) {
return EnumConstant(Identifier(field.getName()!!),
getComments(field),
modifiers,
convertType(field.getType()),
convertType(field.getType(), Nullability.NotNull),
convertElement(field.getArgumentList()))
}
val isFinal = field.hasModifierProperty(PsiModifier.FINAL)
var kType = convertVariableType(field)
if (isFinal && field.getInitializer().isDefinitelyNotNull()) {
kType = kType.toNotNullType();
}
return Field(Identifier(field.getName()!!),
getComments(field),
modifiers,
kType,
convertVariableType(field),
convertExpression(field.getInitializer(), field.getType()),
isFinal,
field.hasModifierProperty(PsiModifier.FINAL),
field.countWriteAccesses(field.getContainingClass()))
}
private fun convertMethod(method: PsiMethod, membersToRemove: MutableSet<PsiMember>): Function {
methodReturnType = method.getReturnType()
val returnType = convertType(method.getReturnType(), method.isAnnotatedAsNotNull())
val returnType = convertType(method.getReturnType(), method.nullabilityFromAnnotations())
val modifiers = convertModifiers(method)
@@ -448,41 +442,46 @@ public class Converter(val project: Project, val settings: ConverterSettings) {
convertType(element.getType()))
}
public fun convertType(`type`: PsiType?): Type {
public fun convertType(`type`: PsiType?, nullability: Nullability = Nullability.Default): Type {
if (`type` == null) return Type.Empty
return `type`.accept<Type>(TypeVisitor(this))!!
val result = `type`.accept<Type>(TypeVisitor(this))!!
return when (nullability) {
Nullability.NotNull -> result.toNotNullType()
Nullability.Nullable -> result.toNullableType()
Nullability.Default -> result
}
}
public fun convertTypes(types: Array<PsiType>): List<Type> {
return types.map { convertType(it) }
}
public fun convertType(`type`: PsiType?, notNull: Boolean): Type {
val result = convertType(`type`)
if (notNull) {
return result.toNotNullType()
public fun convertVariableType(variable: PsiVariable): Type {
var nullability = variable.nullabilityFromAnnotations()
if (nullability == Nullability.Default) {
val initializer = variable.getInitializer()
if (initializer != null && variable.hasModifierProperty(PsiModifier.FINAL)) {
nullability = initializer.nullability()
}
}
return result
return convertType(variable.getType(), nullability)
}
public fun convertVariableType(variable: PsiVariable): Type
= convertType(variable.getType(), variable.isAnnotatedAsNotNull())
private fun convertToNotNullableTypes(types: Array<out PsiType?>): List<Type>
= types.map { convertType(it).toNotNullType() }
= types.map { convertType(it, Nullability.NotNull) }
public fun convertParameterList(parameters: PsiParameterList): ParameterList
= ParameterList(parameters.getParameters().map { convertParameter(it) })
public fun convertParameter(parameter: PsiParameter,
forceNotNull: Boolean = false,
nullability: Nullability = Nullability.Default,
varValModifier: Parameter.VarValModifier = Parameter.VarValModifier.None,
modifiers: Collection<Modifier> = listOf()): Parameter {
var `type` = convertVariableType(parameter)
if (forceNotNull) {
`type` = `type`.toNotNullType()
when (nullability) {
Nullability.NotNull -> `type` = `type`.toNotNullType()
Nullability.Nullable -> `type` = `type`.toNullableType()
}
return Parameter(Identifier(parameter.getName()!!), `type`, varValModifier, modifiers)
}
@@ -573,6 +572,7 @@ public class Converter(val project: Project, val settings: ConverterSettings) {
}
val NOT_NULL_ANNOTATIONS: Set<String> = setOf("org.jetbrains.annotations.NotNull", "com.sun.istack.internal.NotNull", "javax.annotation.Nonnull")
val NULLABLE_ANNOTATIONS: Set<String> = setOf("org.jetbrains.annotations.Nullable", "com.sun.istack.internal.Nullable", "javax.annotation.Nullable")
val PRIMITIVE_TYPE_CONVERSIONS: Map<String, String> = mapOf(
"byte" to BYTE.asString(),
+15 -7
View File
@@ -28,7 +28,7 @@ import com.intellij.psi.PsiModifierListOwner
import java.util.ArrayList
import com.intellij.psi.util.PsiUtil
import com.intellij.psi.PsiThisExpression
import java.util.HashMap
import org.jetbrains.jet.j2k.ast.Nullability
fun quoteKeywords(packageName: String): String = packageName.split("\\.").map { Identifier(it).toKotlin() }.makeString(".")
@@ -52,13 +52,21 @@ fun findExpressionReferences(element: PsiElement, scope: PsiElement): Collection
fun PsiElement.countWriteAccesses(scope: PsiElement?): Int
= if (scope != null) findExpressionReferences(this, scope).count { PsiUtil.isAccessedForWriting(it) } else 0
fun PsiModifierListOwner.isAnnotatedAsNotNull(): Boolean
= getModifierList()?.getAnnotations()?.any { NOT_NULL_ANNOTATIONS.contains(it.getQualifiedName()) } ?: false
fun PsiModifierListOwner.nullabilityFromAnnotations(): Nullability {
val annotations = getModifierList()?.getAnnotations() ?: return Nullability.Default
return if (annotations.any { NOT_NULL_ANNOTATIONS.contains(it.getQualifiedName()) })
Nullability.NotNull
else if (annotations.any { NULLABLE_ANNOTATIONS.contains(it.getQualifiedName()) })
Nullability.Nullable
else
Nullability.Default
}
fun PsiElement?.isDefinitelyNotNull(): Boolean = when(this) {
is PsiLiteralExpression -> getValue() != null
is PsiNewExpression -> true
else -> false
fun PsiElement?.nullability(): Nullability = when(this) {
is PsiLiteralExpression -> if (getValue() != null) Nullability.NotNull else Nullability.Nullable
is PsiNewExpression -> Nullability.NotNull
//TODO: other cases
else -> Nullability.Default
}
fun getDefaultInitializer(field: Field): String {
+1 -1
View File
@@ -30,7 +30,7 @@ class Import(val name: String) : Element {
class ImportList(val imports: List<Import>) : Element {
val filteredImports = imports.filter {
!it.name.isEmpty() && it.name !in NOT_NULL_ANNOTATIONS
!it.name.isEmpty() && it.name !in NOT_NULL_ANNOTATIONS && it.name !in NULLABLE_ANNOTATIONS
}.filter {
// If name is invalid, like with star imports, don't try to filter
if (!isValidJavaFqName(it.name))
@@ -25,16 +25,11 @@ class ElementVisitor(public val converter: Converter) : JavaElementVisitor() {
protected set
override fun visitLocalVariable(variable: PsiLocalVariable) {
var kType = converter.convertVariableType(variable)
val isFinal = variable.hasModifierProperty(PsiModifier.FINAL)
if (isFinal && variable.getInitializer().isDefinitelyNotNull()) {
kType = kType.toNotNullType()
}
result = LocalVariable(Identifier(variable.getName()!!),
converter.convertModifiers(variable),
kType,
converter.convertVariableType(variable),
converter.convertExpression(variable.getInitializer(), variable.getType()),
converter.settings.forceLocalVariableImmutability || isFinal,
converter.settings.forceLocalVariableImmutability || variable.hasModifierProperty(PsiModifier.FINAL),
converter.settings)
}
@@ -183,7 +183,7 @@ open class ExpressionVisitor(protected val converter: Converter,
private fun createNewEmptyArrayWithoutInitialization(expression: PsiNewExpression): Expression {
return ArrayWithoutInitializationExpression(
converter.convertType(expression.getType(), true),
converter.convertType(expression.getType(), Nullability.NotNull),
converter.convertExpressions(expression.getArrayDimensions()))
}
@@ -216,7 +216,7 @@ open class ExpressionVisitor(protected val converter: Converter,
val insideSecondaryConstructor = containingConstructor != null && !containingConstructor.isPrimaryConstructor()
val addReceiver = insideSecondaryConstructor && (expression.getReference()?.resolve() as? PsiField)?.getContainingClass() == containingConstructor!!.getContainingClass()
val isNullable = converter.convertType(expression.getType(), expression.isResolvedToNotNull()).isNullable
val isNullable = converter.convertType(expression.getType(), expression.nullability()).isNullable
val referencedName = expression.getReferenceName()!!
var identifier: Expression = Identifier(referencedName, isNullable)
val qualifier = expression.getQualifierExpression()
@@ -266,12 +266,12 @@ open class ExpressionVisitor(protected val converter: Converter,
result = CallChainExpression(converter.convertExpression(qualifier), identifier)
}
private fun PsiReference.isResolvedToNotNull(): Boolean {
private fun PsiReference.nullability(): Nullability {
val target = resolve()
return when(target) {
is PsiEnumConstant -> true
is PsiModifierListOwner -> target.isAnnotatedAsNotNull()
else -> false
is PsiEnumConstant -> Nullability.NotNull
is PsiModifierListOwner -> target.nullabilityFromAnnotations()
else -> Nullability.Default
}
}
@@ -216,7 +216,7 @@ class StatementVisitor(public val converter: Converter) : JavaElementVisitor() {
val catchBlocks = statement.getCatchBlocks()
val catchBlockParameters = statement.getCatchBlockParameters()
for (i in 0..catchBlocks.size - 1) {
catches.add(CatchStatement(converter.convertParameter(catchBlockParameters[i], true),
catches.add(CatchStatement(converter.convertParameter(catchBlockParameters[i], Nullability.NotNull),
converter.convertBlock(catchBlocks[i])))
}
result = TryStatement(converter.convertBlock(statement.getTryBlock()),
@@ -226,7 +226,7 @@ class StatementVisitor(public val converter: Converter) : JavaElementVisitor() {
override fun visitWhileStatement(statement: PsiWhileStatement) {
var condition: PsiExpression? = statement.getCondition()
val expression: Expression = (if (condition != null && condition?.getType() != null)
this.converter.convertExpression(condition, condition?.getType())
converter.convertExpression(condition, condition?.getType())
else
converter.convertExpression(condition))
result = WhileStatement(expression, converter.convertStatement(statement.getBody()))
@@ -236,7 +236,7 @@ class StatementVisitor(public val converter: Converter) : JavaElementVisitor() {
val returnValue = statement.getReturnValue()
val methodReturnType = converter.methodReturnType
val expression = (if (returnValue != null && methodReturnType != null)
this.converter.convertExpression(returnValue, methodReturnType)
converter.convertExpression(returnValue, methodReturnType)
else
converter.convertExpression(returnValue))
result = ReturnStatement(expression)
@@ -16,17 +16,14 @@
package org.jetbrains.jet.j2k.test;
import junit.framework.Assert;
import junit.framework.Test;
import junit.framework.TestSuite;
import java.io.File;
import java.util.regex.Pattern;
import org.jetbrains.jet.JetTestUtils;
import org.jetbrains.jet.test.InnerTestClasses;
import org.jetbrains.jet.test.TestMetadata;
import org.jetbrains.jet.j2k.test.AbstractJavaToKotlinConverterTest;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@@ -53,6 +50,11 @@ public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConv
doTest("j2k/tests/testData/ast/annotations/jetbrainsNotNullChainExpr.java");
}
@TestMetadata("jetbrainsNullable.java")
public void testJetbrainsNullable() throws Exception {
doTest("j2k/tests/testData/ast/annotations/jetbrainsNullable.java");
}
}
@TestMetadata("j2k/tests/testData/ast/anonymousBlock")
@@ -0,0 +1,31 @@
//file
// !specifyLocalVariableTypeByDefault: true
package test;
import org.jetbrains.annotations.Nullable;
public class Test {
@Nullable String myStr = "String2";
public Test(@Nullable String str) {
myStr = str;
}
public void sout(@Nullable String str) {
System.out.println(str);
}
@Nullable
public String dummy(@Nullable String str) {
return str;
}
public void test() {
sout("String");
@Nullable String test = "String2";
sout(test);
sout(dummy(test));
new Test(test);
}
}
@@ -0,0 +1,27 @@
// !specifyLocalVariableTypeByDefault: true
package test
public class Test(str: String?) {
var myStr: String? = "String2"
public fun sout(str: String?) {
System.out.println(str)
}
public fun dummy(str: String?): String? {
return str
}
public fun test() {
sout("String")
val test: String? = "String2"
sout(test)
sout(dummy(test))
Test(test)
}
{
myStr = str
}
}
@@ -1,5 +1,3 @@
package org.jetbrains.jet.j2k.`in`
import org.jetbrains.annotations.Nullable
public class Converter()