Skip to content

Commit f5dc515

Browse files
committed
Don't introduce @NotNull on Kotlin methods that already have that annotation
This usually can't happen, but delegates pointing at Java appear to be synthesised with this normally-hidden annotation
1 parent c8e2ae8 commit f5dc515

File tree

7 files changed

+40
-6
lines changed

7 files changed

+40
-6
lines changed

java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1366,14 +1366,19 @@ open class KotlinFileExtractor(
13661366
if (t !is IrSimpleType)
13671367
return null
13681368

1369+
fun hasExistingAnnotation(name: FqName) =
1370+
existingAnnotations.any { existing -> existing.type.classFqName == name }
1371+
13691372
return if (declOrigin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB) {
13701373
// Java declaration: restore a NotNull or Nullable annotation if the original Java member had one but the Kotlin compiler removed it.
13711374
javaAnnotations?.mapNotNull { it.classId?.asSingleFqName() }
13721375
?.singleOrNull { NOT_NULL_ANNOTATIONS.contains(it) || NULLABLE_ANNOTATIONS.contains(it) }
1373-
?.takeUnless { existingAnnotations.any { existing -> existing.type.classFqName == it } }
1376+
?.takeUnless { hasExistingAnnotation(it) }
13741377
} else {
1375-
// Kotlin declaration: add a NotNull annotation to a non-nullable non-primitive type.
1376-
JvmAnnotationNames.JETBRAINS_NOT_NULL_ANNOTATION.takeUnless { t.isNullable() || primitiveTypeMapping.getPrimitiveInfo(t) != null }
1378+
// Kotlin declaration: add a NotNull annotation to a non-nullable non-primitive type, unless one is already present.
1379+
// Usually Kotlin declarations can't have a manual `@NotNull`, but this happens at least when delegating members are
1380+
// synthesised and inherit the annotation from the delegate (which given it has @NotNull, is likely written in Java)
1381+
JvmAnnotationNames.JETBRAINS_NOT_NULL_ANNOTATION.takeUnless { t.isNullable() || primitiveTypeMapping.getPrimitiveInfo(t) != null || hasExistingAnnotation(it) }
13771382
}
13781383
}
13791384

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import org.jetbrains.annotations.*;
2+
import zpkg.A;
3+
4+
public interface AnnotatedInterface {
5+
6+
public @A @NotNull String notNullAnnotated(@A @NotNull String param);
7+
8+
public @A @Nullable String nullableAnnotated(@A @Nullable String param);
9+
10+
}

java/ql/integration-tests/all-platforms/kotlin/nullability-annotations/AnnotatedMethods.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import org.jetbrains.annotations.*;
22
import zpkg.A;
33

4-
public class AnnotatedMethods {
4+
public class AnnotatedMethods implements AnnotatedInterface {
55

66
public @A @NotNull String notNullAnnotated(@A @NotNull String param) { return param; }
77

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
public class JavaUser {
22

3-
public static void test(KotlinAnnotatedMethods km) {
3+
public static void test(KotlinAnnotatedMethods km, KotlinDelegate kd) {
44
km.f(null);
5+
kd.notNullAnnotated("Hello world");
56
}
67

78
}

java/ql/integration-tests/all-platforms/kotlin/nullability-annotations/ktUser.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ class KotlinAnnotatedMethods {
55
@A fun f(@A m: AnnotatedMethods): String = m.notNullAnnotated("hello") + m.nullableAnnotated("world")!!
66

77
}
8+
9+
class KotlinDelegate(c: AnnotatedMethods) : AnnotatedInterface by c { }

java/ql/integration-tests/all-platforms/kotlin/nullability-annotations/test.expected

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
| AnnotatedInterface.java:6:29:6:44 | notNullAnnotated | parameter | AnnotatedInterface.java:6:46:6:47 | A |
2+
| AnnotatedInterface.java:6:29:6:44 | notNullAnnotated | parameter | AnnotatedInterface.java:6:49:6:56 | NotNull |
3+
| AnnotatedInterface.java:6:29:6:44 | notNullAnnotated | return value | AnnotatedInterface.java:6:10:6:11 | A |
4+
| AnnotatedInterface.java:6:29:6:44 | notNullAnnotated | return value | AnnotatedInterface.java:6:13:6:20 | NotNull |
5+
| AnnotatedInterface.java:8:30:8:46 | nullableAnnotated | parameter | AnnotatedInterface.java:8:48:8:49 | A |
6+
| AnnotatedInterface.java:8:30:8:46 | nullableAnnotated | parameter | AnnotatedInterface.java:8:51:8:59 | Nullable |
7+
| AnnotatedInterface.java:8:30:8:46 | nullableAnnotated | return value | AnnotatedInterface.java:8:10:8:11 | A |
8+
| AnnotatedInterface.java:8:30:8:46 | nullableAnnotated | return value | AnnotatedInterface.java:8:13:8:21 | Nullable |
19
| AnnotatedMethods.java:6:29:6:44 | notNullAnnotated | parameter | AnnotatedMethods.java:6:46:6:47 | A |
210
| AnnotatedMethods.java:6:29:6:44 | notNullAnnotated | parameter | AnnotatedMethods.java:6:49:6:56 | NotNull |
311
| AnnotatedMethods.java:6:29:6:44 | notNullAnnotated | return value | AnnotatedMethods.java:6:10:6:11 | A |
@@ -6,6 +14,14 @@
614
| AnnotatedMethods.java:8:30:8:46 | nullableAnnotated | parameter | AnnotatedMethods.java:8:51:8:59 | Nullable |
715
| AnnotatedMethods.java:8:30:8:46 | nullableAnnotated | return value | AnnotatedMethods.java:8:10:8:11 | A |
816
| AnnotatedMethods.java:8:30:8:46 | nullableAnnotated | return value | AnnotatedMethods.java:8:13:8:21 | Nullable |
17+
| ktUser.kt:0:0:0:0 | notNullAnnotated | parameter | ktUser.kt:0:0:0:0 | A |
18+
| ktUser.kt:0:0:0:0 | notNullAnnotated | parameter | ktUser.kt:0:0:0:0 | NotNull |
19+
| ktUser.kt:0:0:0:0 | notNullAnnotated | return value | ktUser.kt:0:0:0:0 | A |
20+
| ktUser.kt:0:0:0:0 | notNullAnnotated | return value | ktUser.kt:0:0:0:0 | NotNull |
21+
| ktUser.kt:0:0:0:0 | nullableAnnotated | parameter | ktUser.kt:0:0:0:0 | A |
22+
| ktUser.kt:0:0:0:0 | nullableAnnotated | parameter | ktUser.kt:0:0:0:0 | Nullable |
23+
| ktUser.kt:0:0:0:0 | nullableAnnotated | return value | ktUser.kt:0:0:0:0 | A |
24+
| ktUser.kt:0:0:0:0 | nullableAnnotated | return value | ktUser.kt:0:0:0:0 | Nullable |
925
| ktUser.kt:5:6:5:105 | f | parameter | ktUser.kt:0:0:0:0 | NotNull |
1026
| ktUser.kt:5:6:5:105 | f | parameter | ktUser.kt:5:12:5:13 | A |
1127
| ktUser.kt:5:6:5:105 | f | return value | ktUser.kt:0:0:0:0 | NotNull |

java/ql/integration-tests/all-platforms/kotlin/nullability-annotations/test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
os.mkdir('out')
44
os.mkdir('out2')
55
os.mkdir('out3')
6-
run_codeql_database_create(["javac AnnotatedMethods.java zpkg/A.java org/jetbrains/annotations/NotNull.java org/jetbrains/annotations/Nullable.java -d out", "kotlinc ktUser.kt -cp out -d out2", "javac JavaUser.java -cp out:out2 -d out3"], lang="java")
6+
run_codeql_database_create(["javac AnnotatedInterface.java AnnotatedMethods.java zpkg/A.java org/jetbrains/annotations/NotNull.java org/jetbrains/annotations/Nullable.java -d out", "kotlinc ktUser.kt -cp out -d out2", "javac JavaUser.java -cp out:out2 -d out3"], lang="java")

0 commit comments

Comments
 (0)