diff --git a/j2k/src/org/jetbrains/jet/j2k/Converter.kt b/j2k/src/org/jetbrains/jet/j2k/Converter.kt index acfffab7be2..e3ac5b8e6dd 100644 --- a/j2k/src/org/jetbrains/jet/j2k/Converter.kt +++ b/j2k/src/org/jetbrains/jet/j2k/Converter.kt @@ -461,13 +461,43 @@ public class Converter(val project: Project, val settings: ConverterSettings) { var nullability = variable.nullabilityFromAnnotations() if (nullability == Nullability.Default) { val initializer = variable.getInitializer() - if (initializer != null && variable.hasModifierProperty(PsiModifier.FINAL)) { - nullability = initializer.nullability() + if (initializer != null) { + val initializerNullability = initializer.nullability() + if (variable.hasModifierProperty(PsiModifier.FINAL)) { //TODO: replace check for final modifier with effective final + nullability = initializerNullability + } + else if (initializerNullability == Nullability.Nullable) { // if variable is not final then non-nullability of initializer does not mean that variable is non-null + nullability = Nullability.Nullable + } } } return convertType(variable.getType(), nullability) } + private fun PsiExpression.nullability(): Nullability { + return when (this) { + is PsiLiteralExpression -> if (getValue() != null) Nullability.NotNull else Nullability.Nullable + + is PsiNewExpression -> Nullability.NotNull + + is PsiConditionalExpression -> { + val nullability1 = getThenExpression()?.nullability() + if (nullability1 == Nullability.Nullable) return Nullability.Nullable + val nullability2 = getElseExpression()?.nullability() + if (nullability2 == Nullability.Nullable) return Nullability.Nullable + if (nullability1 == Nullability.NotNull && nullability2 == Nullability.NotNull) return Nullability.NotNull + Nullability.Default + } + + is PsiParenthesizedExpression -> getExpression()?.nullability() ?: Nullability.Default + + //TODO: some other cases + + else -> Nullability.Default + } + } + + private fun convertToNotNullableTypes(types: Array): List = types.map { convertType(it, Nullability.NotNull) } diff --git a/j2k/src/org/jetbrains/jet/j2k/Utils.kt b/j2k/src/org/jetbrains/jet/j2k/Utils.kt index e83722a5fe2..f6980362c8e 100644 --- a/j2k/src/org/jetbrains/jet/j2k/Utils.kt +++ b/j2k/src/org/jetbrains/jet/j2k/Utils.kt @@ -62,13 +62,6 @@ fun PsiModifierListOwner.nullabilityFromAnnotations(): Nullability { Nullability.Default } -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 { if (field.`type`.isNullable) { return "null" diff --git a/j2k/tests/test/org/jetbrains/jet/j2k/test/JavaToKotlinConverterTestGenerated.java b/j2k/tests/test/org/jetbrains/jet/j2k/test/JavaToKotlinConverterTestGenerated.java index c8097b7fd64..21c4d0546c1 100644 --- a/j2k/tests/test/org/jetbrains/jet/j2k/test/JavaToKotlinConverterTestGenerated.java +++ b/j2k/tests/test/org/jetbrains/jet/j2k/test/JavaToKotlinConverterTestGenerated.java @@ -16,19 +16,22 @@ 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 java.io.File; -import java.util.regex.Pattern; +import org.jetbrains.jet.j2k.test.AbstractJavaToKotlinConverterTest; /** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ @SuppressWarnings("all") @TestMetadata("j2k/tests/testData/ast") -@InnerTestClasses({JavaToKotlinConverterTestGenerated.Annotations.class, JavaToKotlinConverterTestGenerated.AnonymousBlock.class, JavaToKotlinConverterTestGenerated.ArrayAccessExpression.class, JavaToKotlinConverterTestGenerated.ArrayInitializerExpression.class, JavaToKotlinConverterTestGenerated.ArrayType.class, JavaToKotlinConverterTestGenerated.AssertStatement.class, JavaToKotlinConverterTestGenerated.AssignmentExpression.class, JavaToKotlinConverterTestGenerated.BinaryExpression.class, JavaToKotlinConverterTestGenerated.BoxedType.class, JavaToKotlinConverterTestGenerated.BreakStatement.class, JavaToKotlinConverterTestGenerated.CallChainExpression.class, JavaToKotlinConverterTestGenerated.Class.class, JavaToKotlinConverterTestGenerated.ClassExpression.class, JavaToKotlinConverterTestGenerated.ConditionalExpression.class, JavaToKotlinConverterTestGenerated.Constructors.class, JavaToKotlinConverterTestGenerated.ContinueStatement.class, JavaToKotlinConverterTestGenerated.DeclarationStatement.class, JavaToKotlinConverterTestGenerated.DoWhileStatement.class, JavaToKotlinConverterTestGenerated.Enum.class, JavaToKotlinConverterTestGenerated.Field.class, JavaToKotlinConverterTestGenerated.For.class, JavaToKotlinConverterTestGenerated.ForeachStatement.class, JavaToKotlinConverterTestGenerated.Formatting.class, JavaToKotlinConverterTestGenerated.Function.class, JavaToKotlinConverterTestGenerated.Identifier.class, JavaToKotlinConverterTestGenerated.IfStatement.class, JavaToKotlinConverterTestGenerated.ImportStatement.class, JavaToKotlinConverterTestGenerated.InProjectionType.class, JavaToKotlinConverterTestGenerated.Inheritance.class, JavaToKotlinConverterTestGenerated.IsOperator.class, JavaToKotlinConverterTestGenerated.Issues.class, JavaToKotlinConverterTestGenerated.KotlinExclusion.class, JavaToKotlinConverterTestGenerated.LabelStatement.class, JavaToKotlinConverterTestGenerated.List.class, JavaToKotlinConverterTestGenerated.LiteralExpression.class, JavaToKotlinConverterTestGenerated.LocalVariable.class, JavaToKotlinConverterTestGenerated.MethodCallExpression.class, JavaToKotlinConverterTestGenerated.Misc.class, JavaToKotlinConverterTestGenerated.NewClassExpression.class, JavaToKotlinConverterTestGenerated.ObjectLiteral.class, JavaToKotlinConverterTestGenerated.OutProjectionType.class, JavaToKotlinConverterTestGenerated.PackageStatement.class, JavaToKotlinConverterTestGenerated.ParenthesizedExpression.class, JavaToKotlinConverterTestGenerated.PolyadicExpression.class, JavaToKotlinConverterTestGenerated.PostfixOperator.class, JavaToKotlinConverterTestGenerated.PrefixOperator.class, JavaToKotlinConverterTestGenerated.RawGenerics.class, JavaToKotlinConverterTestGenerated.ReturnStatement.class, JavaToKotlinConverterTestGenerated.Settings.class, JavaToKotlinConverterTestGenerated.StarProjectionType.class, JavaToKotlinConverterTestGenerated.SuperExpression.class, JavaToKotlinConverterTestGenerated.Switch.class, JavaToKotlinConverterTestGenerated.SynchronizedStatement.class, JavaToKotlinConverterTestGenerated.ThisExpression.class, JavaToKotlinConverterTestGenerated.ThrowStatement.class, JavaToKotlinConverterTestGenerated.Trait.class, JavaToKotlinConverterTestGenerated.TryStatement.class, JavaToKotlinConverterTestGenerated.TypeCastExpression.class, JavaToKotlinConverterTestGenerated.TypeParameters.class, JavaToKotlinConverterTestGenerated.VarArg.class, JavaToKotlinConverterTestGenerated.WhileStatement.class}) +@InnerTestClasses({JavaToKotlinConverterTestGenerated.Annotations.class, JavaToKotlinConverterTestGenerated.AnonymousBlock.class, JavaToKotlinConverterTestGenerated.ArrayAccessExpression.class, JavaToKotlinConverterTestGenerated.ArrayInitializerExpression.class, JavaToKotlinConverterTestGenerated.ArrayType.class, JavaToKotlinConverterTestGenerated.AssertStatement.class, JavaToKotlinConverterTestGenerated.AssignmentExpression.class, JavaToKotlinConverterTestGenerated.BinaryExpression.class, JavaToKotlinConverterTestGenerated.BoxedType.class, JavaToKotlinConverterTestGenerated.BreakStatement.class, JavaToKotlinConverterTestGenerated.CallChainExpression.class, JavaToKotlinConverterTestGenerated.Class.class, JavaToKotlinConverterTestGenerated.ClassExpression.class, JavaToKotlinConverterTestGenerated.ConditionalExpression.class, JavaToKotlinConverterTestGenerated.Constructors.class, JavaToKotlinConverterTestGenerated.ContinueStatement.class, JavaToKotlinConverterTestGenerated.DeclarationStatement.class, JavaToKotlinConverterTestGenerated.DoWhileStatement.class, JavaToKotlinConverterTestGenerated.Enum.class, JavaToKotlinConverterTestGenerated.Field.class, JavaToKotlinConverterTestGenerated.For.class, JavaToKotlinConverterTestGenerated.ForeachStatement.class, JavaToKotlinConverterTestGenerated.Formatting.class, JavaToKotlinConverterTestGenerated.Function.class, JavaToKotlinConverterTestGenerated.Identifier.class, JavaToKotlinConverterTestGenerated.IfStatement.class, JavaToKotlinConverterTestGenerated.ImportStatement.class, JavaToKotlinConverterTestGenerated.InProjectionType.class, JavaToKotlinConverterTestGenerated.Inheritance.class, JavaToKotlinConverterTestGenerated.IsOperator.class, JavaToKotlinConverterTestGenerated.Issues.class, JavaToKotlinConverterTestGenerated.KotlinExclusion.class, JavaToKotlinConverterTestGenerated.LabelStatement.class, JavaToKotlinConverterTestGenerated.List.class, JavaToKotlinConverterTestGenerated.LiteralExpression.class, JavaToKotlinConverterTestGenerated.LocalVariable.class, JavaToKotlinConverterTestGenerated.MethodCallExpression.class, JavaToKotlinConverterTestGenerated.Misc.class, JavaToKotlinConverterTestGenerated.NewClassExpression.class, JavaToKotlinConverterTestGenerated.Nullability.class, JavaToKotlinConverterTestGenerated.ObjectLiteral.class, JavaToKotlinConverterTestGenerated.OutProjectionType.class, JavaToKotlinConverterTestGenerated.PackageStatement.class, JavaToKotlinConverterTestGenerated.ParenthesizedExpression.class, JavaToKotlinConverterTestGenerated.PolyadicExpression.class, JavaToKotlinConverterTestGenerated.PostfixOperator.class, JavaToKotlinConverterTestGenerated.PrefixOperator.class, JavaToKotlinConverterTestGenerated.RawGenerics.class, JavaToKotlinConverterTestGenerated.ReturnStatement.class, JavaToKotlinConverterTestGenerated.Settings.class, JavaToKotlinConverterTestGenerated.StarProjectionType.class, JavaToKotlinConverterTestGenerated.SuperExpression.class, JavaToKotlinConverterTestGenerated.Switch.class, JavaToKotlinConverterTestGenerated.SynchronizedStatement.class, JavaToKotlinConverterTestGenerated.ThisExpression.class, JavaToKotlinConverterTestGenerated.ThrowStatement.class, JavaToKotlinConverterTestGenerated.Trait.class, JavaToKotlinConverterTestGenerated.TryStatement.class, JavaToKotlinConverterTestGenerated.TypeCastExpression.class, JavaToKotlinConverterTestGenerated.TypeParameters.class, JavaToKotlinConverterTestGenerated.VarArg.class, JavaToKotlinConverterTestGenerated.WhileStatement.class}) public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConverterTest { public void testAllFilesPresentInAst() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("j2k/tests/testData/ast"), Pattern.compile("^(.+)\\.java$"), true); @@ -1771,6 +1774,29 @@ public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConv } + @TestMetadata("j2k/tests/testData/ast/nullability") + public static class Nullability extends AbstractJavaToKotlinConverterTest { + public void testAllFilesPresentInNullability() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("j2k/tests/testData/ast/nullability"), Pattern.compile("^(.+)\\.java$"), true); + } + + @TestMetadata("FieldInitializedWithNull.java") + public void testFieldInitializedWithNull() throws Exception { + doTest("j2k/tests/testData/ast/nullability/FieldInitializedWithNull.java"); + } + + @TestMetadata("InitializedWithNull.java") + public void testInitializedWithNull() throws Exception { + doTest("j2k/tests/testData/ast/nullability/InitializedWithNull.java"); + } + + @TestMetadata("InitializedWithTernaryNull.java") + public void testInitializedWithTernaryNull() throws Exception { + doTest("j2k/tests/testData/ast/nullability/InitializedWithTernaryNull.java"); + } + + } + @TestMetadata("j2k/tests/testData/ast/objectLiteral") public static class ObjectLiteral extends AbstractJavaToKotlinConverterTest { public void testAllFilesPresentInObjectLiteral() throws Exception { @@ -2404,6 +2430,7 @@ public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConv suite.addTestSuite(MethodCallExpression.class); suite.addTestSuite(Misc.class); suite.addTestSuite(NewClassExpression.class); + suite.addTestSuite(Nullability.class); suite.addTestSuite(ObjectLiteral.class); suite.addTestSuite(OutProjectionType.class); suite.addTestSuite(PackageStatement.class); diff --git a/j2k/tests/testData/ast/nullability/FieldInitializedWithNull.java b/j2k/tests/testData/ast/nullability/FieldInitializedWithNull.java new file mode 100644 index 00000000000..c44955232d7 --- /dev/null +++ b/j2k/tests/testData/ast/nullability/FieldInitializedWithNull.java @@ -0,0 +1,8 @@ +//file +class C { + private String s = null; + + void foo() { + s = "x" + } +} diff --git a/j2k/tests/testData/ast/nullability/FieldInitializedWithNull.kt b/j2k/tests/testData/ast/nullability/FieldInitializedWithNull.kt new file mode 100644 index 00000000000..f0ace432167 --- /dev/null +++ b/j2k/tests/testData/ast/nullability/FieldInitializedWithNull.kt @@ -0,0 +1,7 @@ +class C() { + private var s: String? = null + + fun foo() { + s = "x" + } +} \ No newline at end of file diff --git a/j2k/tests/testData/ast/nullability/InitializedWithNull.java b/j2k/tests/testData/ast/nullability/InitializedWithNull.java new file mode 100644 index 00000000000..4ae405974ed --- /dev/null +++ b/j2k/tests/testData/ast/nullability/InitializedWithNull.java @@ -0,0 +1,8 @@ +//method +// !specifyLocalVariableTypeByDefault: true +void foo(boolean b) { + String s = null; + if (b) { + s = "abc" + } +} \ No newline at end of file diff --git a/j2k/tests/testData/ast/nullability/InitializedWithNull.kt b/j2k/tests/testData/ast/nullability/InitializedWithNull.kt new file mode 100644 index 00000000000..3cf757e8d10 --- /dev/null +++ b/j2k/tests/testData/ast/nullability/InitializedWithNull.kt @@ -0,0 +1,7 @@ +// !specifyLocalVariableTypeByDefault: true +fun foo(b: Boolean) { + val s: String? = null + if (b) { + s = "abc" + } +} \ No newline at end of file diff --git a/j2k/tests/testData/ast/nullability/InitializedWithTernaryNull.java b/j2k/tests/testData/ast/nullability/InitializedWithTernaryNull.java new file mode 100644 index 00000000000..345ab62cd15 --- /dev/null +++ b/j2k/tests/testData/ast/nullability/InitializedWithTernaryNull.java @@ -0,0 +1,5 @@ +//method +// !specifyLocalVariableTypeByDefault: true +void foo(boolean b) { + String s = (b ? "abc" : null); +} \ No newline at end of file diff --git a/j2k/tests/testData/ast/nullability/InitializedWithTernaryNull.kt b/j2k/tests/testData/ast/nullability/InitializedWithTernaryNull.kt new file mode 100644 index 00000000000..d9b779d3f53 --- /dev/null +++ b/j2k/tests/testData/ast/nullability/InitializedWithTernaryNull.kt @@ -0,0 +1,7 @@ +// !specifyLocalVariableTypeByDefault: true +fun foo(b: Boolean) { + val s: String? = ((if (b) + "abc" + else + null)) +} \ No newline at end of file