Basic Java nullability warnings implemented

#KT-6723 In Progress
This commit is contained in:
Andrey Breslav
2015-02-03 15:08:37 +03:00
parent 29d24374d6
commit 5db6bb04e3
44 changed files with 953 additions and 15 deletions
@@ -42,10 +42,21 @@ import org.jetbrains.kotlin.load.kotlin.nativeDeclarations.NativeFunChecker
import org.jetbrains.kotlin.psi.JetPropertyAccessor
import org.jetbrains.kotlin.descriptors.MemberDescriptor
import org.jetbrains.kotlin.resolve.jvm.calls.checkers.NeedSyntheticChecker
import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker
import org.jetbrains.kotlin.psi.JetExpression
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.load.java.lazy.types.isMarkedNullable
import org.jetbrains.kotlin.load.java.lazy.types.isMarkedNotNull
import org.jetbrains.kotlin.types.isFlexible
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.NullabilityInformationSource
public object KotlinJvmCheckerProvider : AdditionalCheckerProvider(
annotationCheckers = listOf(PlatformStaticAnnotationChecker(), LocalFunInlineChecker(), ReifiedTypeParameterAnnotationChecker(), NativeFunChecker()),
additionalCallCheckers = listOf(NeedSyntheticChecker())
additionalCallCheckers = listOf(NeedSyntheticChecker()),
additionalTypeCheckers = listOf(JavaNullabilityWarningsChecker())
)
public class LocalFunInlineChecker : AnnotationChecker {
@@ -127,3 +138,30 @@ private fun checkTypeParameterDescriptorsAreNotReified(
)
}
}
public class JavaNullabilityWarningsChecker : AdditionalTypeChecker {
private fun JetType.mayBeNull(): NullabilityInformationSource? {
if (!isFlexible() && TypeUtils.isNullableType(this)) return NullabilityInformationSource.KOTLIN
if (getAnnotations().isMarkedNullable()) return NullabilityInformationSource.JAVA
return null
}
private fun JetType.mustNotBeNull(): NullabilityInformationSource? {
if (!isFlexible() && !TypeUtils.isNullableType(this)) return NullabilityInformationSource.KOTLIN
if (getAnnotations().isMarkedNotNull()) return NullabilityInformationSource.JAVA
return null
}
override fun checkType(expression: JetExpression, expressionType: JetType, c: ResolutionContext<*>) {
if (TypeUtils.noExpectedType(c.expectedType)) return
val expectedMustNotBeNull = c.expectedType.mustNotBeNull()
val actualNullabilityInKotlin = c.dataFlowInfo.getNullability(DataFlowValueFactory.createDataFlowValue(expression, expressionType, c.trace.getBindingContext()))
val actualMayBeNull = if (!actualNullabilityInKotlin.canBeNull()) null else expressionType.mayBeNull()
if (expectedMustNotBeNull != null && actualMayBeNull != null) {
c.trace.report(ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.on(expression, expectedMustNotBeNull, actualMayBeNull))
}
}
}
@@ -20,6 +20,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages;
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap;
import org.jetbrains.kotlin.diagnostics.rendering.Renderers;
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
import org.jetbrains.kotlin.renderer.Renderer;
@@ -51,6 +52,9 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
MAP.put(ErrorsJvm.NATIVE_DECLARATION_CANNOT_HAVE_BODY, "Native declaration can not have a body");
MAP.put(ErrorsJvm.NATIVE_DECLARATION_IN_TRAIT, "Members of traits can not be native");
MAP.put(ErrorsJvm.NATIVE_DECLARATION_CANNOT_BE_INLINED, "Members of traits can not be inlined");
MAP.put(ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS,
"Expected type does not accept nulls in {0}, but the value may be null in {1}", Renderers.TO_STRING, Renderers.TO_STRING);
}
@@ -17,14 +17,18 @@
package org.jetbrains.kotlin.resolve.jvm.diagnostics;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0;
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1;
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory2;
import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.psi.JetDeclaration;
import org.jetbrains.kotlin.psi.JetExpression;
import static org.jetbrains.kotlin.diagnostics.PositioningStrategies.*;
import static org.jetbrains.kotlin.diagnostics.Severity.ERROR;
import static org.jetbrains.kotlin.diagnostics.Severity.WARNING;
public interface ErrorsJvm {
DiagnosticFactory1<PsiElement, ConflictingJvmDeclarationsData> CONFLICTING_JVM_DECLARATIONS =
@@ -42,6 +46,25 @@ public interface ErrorsJvm {
DiagnosticFactory0<JetDeclaration> NATIVE_DECLARATION_IN_TRAIT = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
DiagnosticFactory0<JetDeclaration> NATIVE_DECLARATION_CANNOT_BE_INLINED = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
enum NullabilityInformationSource {
KOTLIN {
@NotNull
@Override
public String toString() {
return "Kotlin";
}
},
JAVA {
@NotNull
@Override
public String toString() {
return "Java";
}
};
}
DiagnosticFactory2<JetExpression, NullabilityInformationSource, NullabilityInformationSource> NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS = DiagnosticFactory2.create(WARNING);
@SuppressWarnings("UnusedDeclaration")
Object _initializer = new Object() {
{
@@ -23,11 +23,12 @@ private val DEFAULT_TYPE_CHECKERS = listOf(TypeApproximator())
public abstract class AdditionalCheckerProvider(
public val annotationCheckers: List<AnnotationChecker>,
additionalCallCheckers: List<CallChecker>
additionalCallCheckers: List<CallChecker>,
additionalTypeCheckers: List<AdditionalTypeChecker>
) {
public val callCheckers: List<CallChecker> = DEFAULT_CALL_CHECKERS + additionalCallCheckers
public val additionalTypeCheckers: List<AdditionalTypeChecker> = DEFAULT_TYPE_CHECKERS
public val additionalTypeCheckers: List<AdditionalTypeChecker> = DEFAULT_TYPE_CHECKERS + additionalTypeCheckers
public object DefaultProvider : AdditionalCheckerProvider(listOf(), listOf()) {}
public object DefaultProvider : AdditionalCheckerProvider(listOf(), listOf(), listOf()) {}
}
@@ -1,29 +1,29 @@
import java.sql.DriverManager
fun getConnection(url: String?) {
DriverManager.getConnection(url)
DriverManager.getConnection(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>url<!>)
DriverManager.getConnection(url!!) : java.sql.Connection
}
fun getConnection(url: String?, props: java.util.Properties?) {
DriverManager.getConnection(url, props)
DriverManager.getConnection(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>url<!>, props)
DriverManager.getConnection(url!!, props) : java.sql.Connection
}
fun getConnection(url: String?, user: String?, password: String?) {
DriverManager.getConnection(url, user!!, password!!)
DriverManager.getConnection(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>url<!>, user!!, password!!)
DriverManager.getConnection(url!!, user, password<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>)
DriverManager.getConnection(url<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>, user<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>, password)
DriverManager.getConnection(url<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>, user<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>, password<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>) : java.sql.Connection
}
fun getDriver(url: String?) {
DriverManager.getDriver(url)
DriverManager.getDriver(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>url<!>)
DriverManager.getDriver(url!!) : java.sql.Driver
}
fun registerDriver(driver: java.sql.Driver?) {
DriverManager.registerDriver(driver)
DriverManager.registerDriver(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>driver<!>)
DriverManager.registerDriver(driver!!)
}
@@ -1,5 +1,5 @@
fun executeQuery(statement: java.sql.Statement, cmd: String?) {
statement.executeQuery(cmd)
statement.executeQuery(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>cmd<!>)
statement.executeQuery(cmd!!) : java.sql.ResultSet
}
@@ -8,6 +8,6 @@ fun executeQuery(statement: java.sql.PreparedStatement) {
}
fun executeUpdate(statement: java.sql.Statement, cmd: String?) {
statement.executeUpdate(cmd)
statement.executeUpdate(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>cmd<!>)
statement.executeUpdate(cmd!!)
}
@@ -0,0 +1,58 @@
// FILE: p/J.java
package p;
import org.jetbrains.annotations.*;
public class J {
@NotNull
public static Integer staticNN;
@Nullable
public static Integer staticN;
public static Integer staticJ;
}
// FILE: k.kt
import p.*
fun test() {
// @NotNull platform type
var platformNN = J.staticNN
// @Nullable platform type
var platformN = J.staticN
// platform type with no annotation
var platformJ = J.staticJ
+platformNN
+platformN
+platformJ
++platformNN
++platformN
++platformJ
platformNN++
platformN++
platformJ++
1 + platformNN
1 + <!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!>
1 + platformJ
platformNN + 1
platformN + 1
platformJ + 1
1 plus platformNN
1 plus <!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!>
1 plus platformJ
platformNN plus 1
platformN plus 1
platformJ plus 1
platformNN += 1
platformN += 1
platformJ += 1
}
@@ -0,0 +1,3 @@
package
internal fun test(): kotlin.Unit
@@ -0,0 +1,34 @@
// FILE: p/J.java
package p;
import org.jetbrains.annotations.*;
public class J {
@NotNull
public static Integer[] staticNN;
@Nullable
public static Integer[] staticN;
public static Integer[] staticJ;
}
// FILE: k.kt
import p.*
fun test() {
// @NotNull platform type
val platformNN = J.staticNN
// @Nullable platform type
val platformN = J.staticN
// platform type with no annotation
val platformJ = J.staticJ
platformNN[0]
platformN[0]
platformJ[0]
platformNN[0] = 1
platformN[0] = 1
platformJ[0] = 1
}
@@ -0,0 +1,3 @@
package
internal fun test(): kotlin.Unit
@@ -0,0 +1,29 @@
// FILE: p/J.java
package p;
import org.jetbrains.annotations.*;
public class J {
@NotNull
public static J staticNN;
@Nullable
public static J staticN;
public static J staticJ;
}
// FILE: k.kt
import p.*
var v: J = J()
var n: J? = J()
fun test() {
v = J.staticNN
v = <!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>J.staticN<!>
v = J.staticJ
n = J.staticNN
n = J.staticN
n = J.staticJ
}
@@ -0,0 +1,5 @@
package
internal var n: p.J?
internal var v: p.J
internal fun test(): kotlin.Unit
@@ -0,0 +1,50 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
// FILE: p/J.java
package p;
import org.jetbrains.annotations.*;
public class J {
@NotNull
public static Boolean staticNN;
@Nullable
public static Boolean staticN;
public static Boolean staticJ;
}
// FILE: k.kt
import p.*
fun test() {
// @NotNull platform type
val platformNN = J.staticNN
// @Nullable platform type
val platformN = J.staticN
// platform type with no annotation
val platformJ = J.staticJ
if (platformNN) {}
if (<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!>) {}
if (platformJ) {}
while (platformNN) {}
while (<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!>) {}
while (platformJ) {}
do {} while (platformNN)
do {} while (<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!>)
do {} while (platformJ)
platformNN && false
<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!> && false
platformJ && false
platformNN || false
<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!> || false
platformJ || false
!platformNN
!<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!>
!platformJ
}
@@ -0,0 +1,3 @@
package
internal fun test(): kotlin.Unit
@@ -0,0 +1,34 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// FILE: p/J.java
package p;
import org.jetbrains.annotations.*;
public class J {
@NotNull
public static J staticNN;
@Nullable
public static J staticN;
}
// FILE: k.kt
import p.*
fun test() {
val n = J.staticN
foo(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>n<!>)
J.staticNN = <!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>n<!>
if (n != null) {
foo(n)
J.staticNN = n
}
val x: J? = null
J.staticNN = <!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>x<!>
if (x != null) {
J.staticNN = x
}
}
fun foo(j: J) {}
@@ -0,0 +1,4 @@
package
internal fun foo(/*0*/ j: p.J): kotlin.Unit
internal fun test(): kotlin.Unit
@@ -0,0 +1,30 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// FILE: p/J.java
package p;
import org.jetbrains.annotations.*;
public class J {
@NotNull
public static J staticNN;
@Nullable
public static J staticN;
public static J staticJ;
}
// FILE: k.kt
import p.*
fun test() {
// @NotNull platform type
val platformNN = J.staticNN
// @Nullable platform type
val platformN = J.staticN
// platform type with no annotation
val platformJ = J.staticJ
fun foo(p: J = platformNN, p1: J = <!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!>, p2: J = platformJ) {}
fun foo1(p: J? = platformNN, p1: J? = platformN, p2: J? = platformJ) {}
}
@@ -0,0 +1,3 @@
package
internal fun test(): kotlin.Unit
@@ -0,0 +1,26 @@
// FILE: p/J.java
package p;
import org.jetbrains.annotations.*;
public class J {
public interface DP {
String get(Object a, Object b);
String set(Object a, Object b, Object c);
}
@NotNull
public static DP staticNN;
@Nullable
public static DP staticN;
public static DP staticJ;
}
// FILE: k.kt
import p.*
var A by J.staticNN
var B by J.staticN
var C by J.staticJ
@@ -0,0 +1,5 @@
package
internal var A: kotlin.String!
internal var B: kotlin.String!
internal var C: kotlin.String!
@@ -0,0 +1,21 @@
// FILE: p/J.java
package p;
import org.jetbrains.annotations.*;
import java.util.*;
public class J {
@NotNull
public static List<String> staticNN;
@Nullable
public static List<String> staticN;
public static List<String> staticJ;
}
// FILE: k.kt
import p.*
class A : List<String> by J.staticNN
class B : List<String> by <!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>J.staticN<!>
class C : List<String> by J.staticJ
@@ -0,0 +1,55 @@
package
internal final class A : kotlin.List<kotlin.String> {
public constructor A()
public open override /*1*/ /*delegation*/ fun contains(/*0*/ o: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*delegation*/ fun containsAll(/*0*/ c: kotlin.Collection<kotlin.Any?>): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*delegation*/ fun get(/*0*/ index: kotlin.Int): kotlin.String
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*delegation*/ fun indexOf(/*0*/ o: kotlin.Any?): kotlin.Int
public open override /*1*/ /*delegation*/ fun isEmpty(): kotlin.Boolean
public open override /*1*/ /*delegation*/ fun iterator(): kotlin.Iterator<kotlin.String>
public open override /*1*/ /*delegation*/ fun lastIndexOf(/*0*/ o: kotlin.Any?): kotlin.Int
public open override /*1*/ /*delegation*/ fun listIterator(): kotlin.ListIterator<kotlin.String>
public open override /*1*/ /*delegation*/ fun listIterator(/*0*/ index: kotlin.Int): kotlin.ListIterator<kotlin.String>
public open override /*1*/ /*delegation*/ fun size(): kotlin.Int
public open override /*1*/ /*delegation*/ fun subList(/*0*/ fromIndex: kotlin.Int, /*1*/ toIndex: kotlin.Int): kotlin.List<kotlin.String>
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
internal final class B : kotlin.List<kotlin.String> {
public constructor B()
public open override /*1*/ /*delegation*/ fun contains(/*0*/ o: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*delegation*/ fun containsAll(/*0*/ c: kotlin.Collection<kotlin.Any?>): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*delegation*/ fun get(/*0*/ index: kotlin.Int): kotlin.String
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*delegation*/ fun indexOf(/*0*/ o: kotlin.Any?): kotlin.Int
public open override /*1*/ /*delegation*/ fun isEmpty(): kotlin.Boolean
public open override /*1*/ /*delegation*/ fun iterator(): kotlin.Iterator<kotlin.String>
public open override /*1*/ /*delegation*/ fun lastIndexOf(/*0*/ o: kotlin.Any?): kotlin.Int
public open override /*1*/ /*delegation*/ fun listIterator(): kotlin.ListIterator<kotlin.String>
public open override /*1*/ /*delegation*/ fun listIterator(/*0*/ index: kotlin.Int): kotlin.ListIterator<kotlin.String>
public open override /*1*/ /*delegation*/ fun size(): kotlin.Int
public open override /*1*/ /*delegation*/ fun subList(/*0*/ fromIndex: kotlin.Int, /*1*/ toIndex: kotlin.Int): kotlin.List<kotlin.String>
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
internal final class C : kotlin.List<kotlin.String> {
public constructor C()
public open override /*1*/ /*delegation*/ fun contains(/*0*/ o: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*delegation*/ fun containsAll(/*0*/ c: kotlin.Collection<kotlin.Any?>): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*delegation*/ fun get(/*0*/ index: kotlin.Int): kotlin.String
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*delegation*/ fun indexOf(/*0*/ o: kotlin.Any?): kotlin.Int
public open override /*1*/ /*delegation*/ fun isEmpty(): kotlin.Boolean
public open override /*1*/ /*delegation*/ fun iterator(): kotlin.Iterator<kotlin.String>
public open override /*1*/ /*delegation*/ fun lastIndexOf(/*0*/ o: kotlin.Any?): kotlin.Int
public open override /*1*/ /*delegation*/ fun listIterator(): kotlin.ListIterator<kotlin.String>
public open override /*1*/ /*delegation*/ fun listIterator(/*0*/ index: kotlin.Int): kotlin.ListIterator<kotlin.String>
public open override /*1*/ /*delegation*/ fun size(): kotlin.Int
public open override /*1*/ /*delegation*/ fun subList(/*0*/ fromIndex: kotlin.Int, /*1*/ toIndex: kotlin.Int): kotlin.List<kotlin.String>
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,36 @@
// FILE: p/J.java
package p;
import org.jetbrains.annotations.*;
public class J {
@NotNull
public static J staticNN;
@Nullable
public static J staticN;
public static J staticJ;
}
// FILE: k.kt
import p.*
fun test() {
// @NotNull platform type
val platformNN = J.staticNN
// @Nullable platform type
val platformN = J.staticN
// platform type with no annotation
val platformJ = J.staticJ
platformNN.foo()
platformN.foo()
platformJ.foo()
platformNN.bar()
platformN.bar()
platformJ.bar()
}
fun J.foo() {}
fun J?.bar() {}
@@ -0,0 +1,5 @@
package
internal fun test(): kotlin.Unit
internal fun p.J?.bar(): kotlin.Unit
internal fun p.J.foo(): kotlin.Unit
@@ -0,0 +1,31 @@
// FILE: p/J.java
package p;
import org.jetbrains.annotations.*;
public class J {
@NotNull
public static J staticNN;
@Nullable
public static J staticN;
public static J staticJ;
public void foo() {}
}
// FILE: k.kt
import p.*
fun test() {
// @NotNull platform type
val platformNN = J.staticNN
// @Nullable platform type
val platformN = J.staticN
// platform type with no annotation
val platformJ = J.staticJ
platformNN.foo()
platformN.foo()
platformJ.foo()
}
@@ -0,0 +1,3 @@
package
internal fun test(): kotlin.Unit
@@ -0,0 +1,33 @@
// FILE: p/J.java
package p;
import org.jetbrains.annotations.*;
public class J {
@NotNull
public static J staticNN;
@Nullable
public static J staticN;
public static J staticJ;
}
// FILE: k.kt
import p.*
fun test() {
// @NotNull platform type
val platformNN = J.staticNN
// @Nullable platform type
val platformN = J.staticN
// platform type with no annotation
val platformJ = J.staticJ
platformNN : J
<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!> : J
platformJ : J
platformNN : J?
platformN : J?
platformJ : J?
}
@@ -0,0 +1,3 @@
package
internal fun test(): kotlin.Unit
@@ -0,0 +1,31 @@
// FILE: p/J.java
package p;
import org.jetbrains.annotations.*;
import java.util.*;
public class J {
@NotNull
public static List<String> staticNN;
@Nullable
public static List<String> staticN;
public static List<String> staticJ;
}
// FILE: k.kt
import p.*
fun test() {
// @NotNull platform type
val platformNN = J.staticNN
// @Nullable platform type
val platformN = J.staticN
// platform type with no annotation
val platformJ = J.staticJ
for (x in platformNN) {}
for (x in platformN) {}
for (x in platformJ) {}
}
@@ -0,0 +1,3 @@
package
internal fun test(): kotlin.Unit
@@ -0,0 +1,30 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// FILE: p/J.java
package p;
import org.jetbrains.annotations.*;
public class J {
@NotNull
public static J staticNN;
@Nullable
public static J staticN;
public static J staticJ;
}
// FILE: k.kt
import p.*
fun test() {
foo(J.staticNN)
foo(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>J.staticN<!>)
foo(J.staticJ)
bar(J.staticNN)
bar(J.staticN)
bar(J.staticJ)
}
fun foo(j: J) {}
fun bar(j: J?) {}
@@ -0,0 +1,5 @@
package
internal fun bar(/*0*/ j: p.J?): kotlin.Unit
internal fun foo(/*0*/ j: p.J): kotlin.Unit
internal fun test(): kotlin.Unit
@@ -0,0 +1,27 @@
// FILE: p/J.java
package p;
import org.jetbrains.annotations.*;
public class J {
public interface Invoke {
void invoke();
}
@NotNull
public static Invoke staticNN;
@Nullable
public static Invoke staticN;
public static Invoke staticJ;
}
// FILE: k.kt
import p.*
fun test() {
J.staticNN()
J.staticN()
J.staticJ()
}
@@ -0,0 +1,3 @@
package
internal fun test(): kotlin.Unit
@@ -0,0 +1,36 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE
// FILE: p/J.java
package p;
import org.jetbrains.annotations.*;
public class J {
public interface Multi {
String component1();
String component2();
}
@NotNull
public static Multi staticNN;
@Nullable
public static Multi staticN;
public static Multi staticJ;
}
// FILE: k.kt
import p.*
fun test() {
// @NotNull platform type
val platformNN = J.staticNN
// @Nullable platform type
val platformN = J.staticN
// platform type with no annotation
val platformJ = J.staticJ
val (a1, b1) = platformNN
val (a2, b2) = platformN
val (a3, b3) = platformJ
}
@@ -0,0 +1,3 @@
package
internal fun test(): kotlin.Unit
@@ -0,0 +1,92 @@
// FILE: p/J.java
package p;
import org.jetbrains.annotations.*;
public class J {
@NotNull
public static J staticNN;
@Nullable
public static J staticN;
public static J staticJ;
public static void staticSet(@NotNull J nn, @Nullable J n, J j) {}
public J(@NotNull J nn, @Nullable J n, J j) {}
public J() {}
@NotNull
public J nn;
@Nullable
public J n;
public J j;
public void set(@NotNull J nn, @Nullable J n, J j) {}
}
// FILE: k.kt
import p.*
fun test(n: J?, nn: J) {
// @NotNull platform type
val platformNN = J.staticNN
// @Nullable platform type
val platformN = J.staticN
// platform type with no annotation
val platformJ = J.staticJ
J.staticNN = <!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>n<!>
J.staticNN = <!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!>
J.staticNN = nn
J.staticNN = platformNN
J.staticNN = platformJ
J.staticN = n
J.staticN = platformN
J.staticN = nn
J.staticN = platformNN
J.staticN = platformJ
J.staticJ = n
J.staticJ = platformN
J.staticJ = nn
J.staticJ = platformNN
J.staticJ = platformJ
J.staticSet(nn, nn, nn)
J.staticSet(platformNN, platformNN, platformNN)
J.staticSet(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>n<!>, n, n)
J.staticSet(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!>, platformN, platformN)
J.staticSet(platformJ, platformJ, platformJ)
J().nn = <!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>n<!>
J().nn = <!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!>
J().nn = nn
J().nn = platformNN
J().nn = platformJ
J().n = n
J().n = platformN
J().n = nn
J().n = platformNN
J().n = platformJ
J().j = n
J().j = platformN
J().j = nn
J().j = platformNN
J().j = platformJ
J().set(nn, nn, nn)
J().set(platformNN, platformNN, platformNN)
J().set(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>n<!>, n, n)
J().set(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!>, platformN, platformN)
J().set(platformJ, platformJ, platformJ)
J(nn, nn, nn)
J(platformNN, platformNN, platformNN)
J(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>n<!>, n, n)
J(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!>, platformN, platformN)
J(platformJ, platformJ, platformJ)
}
@@ -0,0 +1,3 @@
package
internal fun test(/*0*/ n: p.J?, /*1*/ nn: p.J): kotlin.Unit
@@ -0,0 +1,28 @@
// FILE: p/J.java
package p;
import org.jetbrains.annotations.*;
public class J {
@NotNull
public static Exception staticNN;
@Nullable
public static Exception staticN;
public static Exception staticJ;
}
// FILE: k.kt
import p.*
fun test() {
throw J.staticNN
}
fun test1() {
throw <!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>J.staticN<!>
}
fun test2() {
throw J.staticJ
}
@@ -0,0 +1,5 @@
package
internal fun test(): kotlin.Unit
internal fun test1(): kotlin.Unit
internal fun test2(): kotlin.Unit
@@ -8262,7 +8262,7 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
@TestMetadata("compiler/testData/diagnostics/tests/platformTypes")
@TestDataPath("$PROJECT_ROOT")
@InnerTestClasses({PlatformTypes.CommonSupertype.class, PlatformTypes.Intersection.class, PlatformTypes.MethodCall.class})
@InnerTestClasses({PlatformTypes.CommonSupertype.class, PlatformTypes.Intersection.class, PlatformTypes.MethodCall.class, PlatformTypes.NullabilityWarnings.class})
@RunWith(JUnit3RunnerWithInners.class)
public static class PlatformTypes extends AbstractJetDiagnosticsTest {
public void testAllFilesPresentInPlatformTypes() throws Exception {
@@ -8529,6 +8529,117 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest(fileName);
}
}
@TestMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class NullabilityWarnings extends AbstractJetDiagnosticsTest {
public void testAllFilesPresentInNullabilityWarnings() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("arithmetic.kt")
public void testArithmetic() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/arithmetic.kt");
doTest(fileName);
}
@TestMetadata("array.kt")
public void testArray() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/array.kt");
doTest(fileName);
}
@TestMetadata("assignToVar.kt")
public void testAssignToVar() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/assignToVar.kt");
doTest(fileName);
}
@TestMetadata("conditions.kt")
public void testConditions() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/conditions.kt");
doTest(fileName);
}
@TestMetadata("dataFlowInfo.kt")
public void testDataFlowInfo() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/dataFlowInfo.kt");
doTest(fileName);
}
@TestMetadata("defaultParameters.kt")
public void testDefaultParameters() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/defaultParameters.kt");
doTest(fileName);
}
@TestMetadata("delegatedProperties.kt")
public void testDelegatedProperties() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/delegatedProperties.kt");
doTest(fileName);
}
@TestMetadata("delegation.kt")
public void testDelegation() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/delegation.kt");
doTest(fileName);
}
@TestMetadata("derefenceExtension.kt")
public void testDerefenceExtension() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/derefenceExtension.kt");
doTest(fileName);
}
@TestMetadata("derefenceMember.kt")
public void testDerefenceMember() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/derefenceMember.kt");
doTest(fileName);
}
@TestMetadata("expectedType.kt")
public void testExpectedType() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/expectedType.kt");
doTest(fileName);
}
@TestMetadata("for.kt")
public void testFor() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/for.kt");
doTest(fileName);
}
@TestMetadata("functionArguments.kt")
public void testFunctionArguments() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/functionArguments.kt");
doTest(fileName);
}
@TestMetadata("invoke.kt")
public void testInvoke() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/invoke.kt");
doTest(fileName);
}
@TestMetadata("multiDeclaration.kt")
public void testMultiDeclaration() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/multiDeclaration.kt");
doTest(fileName);
}
@TestMetadata("passToJava.kt")
public void testPassToJava() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/passToJava.kt");
doTest(fileName);
}
@TestMetadata("throw.kt")
public void testThrow() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/throw.kt");
doTest(fileName);
}
}
}
@TestMetadata("compiler/testData/diagnostics/tests/recovery")
@@ -362,7 +362,8 @@ class LazyJavaTypeAttributes(
private fun Annotations.isMarkedReadOnly() = findAnnotation(JvmAnnotationNames.JETBRAINS_READONLY_ANNOTATION) != null
private fun Annotations.isMarkedMutable() = findAnnotation(JvmAnnotationNames.JETBRAINS_MUTABLE_ANNOTATION) != null
private fun Annotations.isMarkedNotNull() = findAnnotation(JvmAnnotationNames.JETBRAINS_NOT_NULL_ANNOTATION) != null
internal fun Annotations.isMarkedNotNull() = findAnnotation(JvmAnnotationNames.JETBRAINS_NOT_NULL_ANNOTATION) != null
internal fun Annotations.isMarkedNullable() = findAnnotation(JvmAnnotationNames.JETBRAINS_NULLABLE_ANNOTATION) != null
fun TypeUsage.toAttributes(allowFlexible: Boolean = true) = object : JavaTypeAttributes {
override val howThisTypeIsUsed: TypeUsage = this@toAttributes
@@ -35,7 +35,8 @@ import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
public object KotlinJsCheckerProvider : AdditionalCheckerProvider(
annotationCheckers = listOf(NativeInvokeChecker(), NativeGetterChecker(), NativeSetterChecker()),
additionalCallCheckers = listOf()
additionalCallCheckers = listOf(),
additionalTypeCheckers = listOf()
)
private abstract class AbstractNativeAnnotationsChecker(private val requiredAnnotation: PredefinedAnnotation) : AnnotationChecker {
+19
View File
@@ -121,3 +121,22 @@ Constructs in question: anything that provides an expected type, i.e.
- explicit expected type (foo: Bar)
- for booleans: if (foo), foo || bar, foo && bar (!foo is a call)
- argument of throw
## Warnings on nullability misuse
A type loaded from Java is said to *bare* a `@Nullable`/`@NotNull` annotation when
- it's a return type a method so annotated;
- it's a type of a field or a parameter so annotated;
- it's a so annotated type (Java 8 and later).
A value is `@Nullable`/`@NotNull` when its type bares such an annotation.
Inside this section, a value is *nullable*/*not-null* when
- it's `@Nullable`/`@NotNull`, or
- it's type in Kotlin when refined with data flow info is nullable/not-null.
The compiler issues warnings specific to `@Nullable`/`@NotNull` in the following situations:
- a `@Nullable` value is assigned to a not-null location (including passing parameters and receivers to functions/properties);
- a nullable value is assigned to a `@NotNull` location;
- a `@NotNull` value is dereferenced with a safe call (`?.`), used in `!!` or on the left-hand side of an elvis operator `?:`;
- a `@NotNull` value is compared with `null` through `==`, `!=`, `===` or `!==`