Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 70ebb41

Browse files
committed
Restore nullability annotations
This is imperfect since arguments to those annotations will be missing, but at least the common case of a plain `@NotNull` or `@Nullable` will be right, and the `@NotNull`s introduced by the Kotlin compiler will be present as expected.
1 parent 8bbb34a commit 70ebb41

10 files changed

Lines changed: 111 additions & 3 deletions

File tree

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

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.descriptors.java.JavaVisibilities
1414
import org.jetbrains.kotlin.ir.IrElement
1515
import org.jetbrains.kotlin.ir.IrStatement
1616
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
17+
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
1718
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
1819
import org.jetbrains.kotlin.ir.builders.declarations.*
1920
import org.jetbrains.kotlin.ir.declarations.*
@@ -44,15 +45,21 @@ import org.jetbrains.kotlin.ir.util.parentClassOrNull
4445
import org.jetbrains.kotlin.ir.util.primaryConstructor
4546
import org.jetbrains.kotlin.ir.util.render
4647
import org.jetbrains.kotlin.ir.util.target
48+
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
49+
import org.jetbrains.kotlin.load.java.NOT_NULL_ANNOTATIONS
50+
import org.jetbrains.kotlin.load.java.NULLABLE_ANNOTATIONS
4751
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
52+
import org.jetbrains.kotlin.load.java.structure.JavaAnnotation
4853
import org.jetbrains.kotlin.load.java.structure.JavaClass
54+
import org.jetbrains.kotlin.load.java.structure.JavaConstructor
4955
import org.jetbrains.kotlin.load.java.structure.JavaMethod
5056
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter
5157
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameterListOwner
5258
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass
5359
import org.jetbrains.kotlin.name.FqName
5460
import org.jetbrains.kotlin.types.Variance
5561
import org.jetbrains.kotlin.util.OperatorNameConventions
62+
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
5663
import java.io.Closeable
5764
import java.util.*
5865
import kotlin.collections.ArrayList
@@ -887,7 +894,13 @@ open class KotlinFileExtractor(
887894
extractTypeAccessRecursive(substitutedType, location, id, -1)
888895
}
889896
val syntheticParameterNames = isUnderscoreParameter(vp) || ((vp.parent as? IrFunction)?.let { hasSynthesizedParameterNames(it) } ?: true)
890-
extractAnnotations(vp, id, extractTypeAccess)
897+
val javaParameter = when(val callable = (vp.parent as? IrFunction)?.let { getJavaCallable(it) }) {
898+
is JavaConstructor -> callable.valueParameters.getOrNull(idx)
899+
is JavaMethod -> callable.valueParameters.getOrNull(idx)
900+
else -> null
901+
}
902+
val extraAnnotations = listOfNotNull(getNullabilityAnnotation(vp.type, vp.origin, vp.annotations, javaParameter?.annotations))
903+
extractAnnotations(vp.annotations + extraAnnotations, id, extractTypeAccess)
891904
return extractValueParameter(id, substitutedType, vp.name.asString(), location, parent, idx, useValueParameter(vp, parentSourceDeclaration), syntheticParameterNames, vp.isVararg, vp.isNoinline, vp.isCrossinline)
892905
}
893906
}
@@ -1337,6 +1350,32 @@ open class KotlinFileExtractor(
13371350
logger.warn("Needed a signature for a type that doesn't have one")
13381351
}
13391352

1353+
private fun getNullabilityAnnotationName(t: IrType, declOrigin: IrDeclarationOrigin, existingAnnotations: List<IrConstructorCall>, javaAnnotations: Collection<JavaAnnotation>?): FqName? {
1354+
if (t !is IrSimpleType)
1355+
return null
1356+
1357+
return if (declOrigin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB) {
1358+
// Java declaration: restore a NotNull or Nullable annotation if the original Java member had one but the Kotlin compiler removed it.
1359+
javaAnnotations?.mapNotNull { it.classId?.asSingleFqName() }
1360+
?.singleOrNull { NOT_NULL_ANNOTATIONS.contains(it) || NULLABLE_ANNOTATIONS.contains(it) }
1361+
?.takeUnless { existingAnnotations.any { existing -> existing.type.classFqName == it } }
1362+
} else {
1363+
// Kotlin declaration: add a NotNull annotation to a non-nullable non-primitive type.
1364+
JvmAnnotationNames.JETBRAINS_NOT_NULL_ANNOTATION.takeUnless { t.isNullable() || primitiveTypeMapping.getPrimitiveInfo(t) != null }
1365+
}
1366+
}
1367+
1368+
private fun getNullabilityAnnotation(t: IrType, declOrigin: IrDeclarationOrigin, existingAnnotations: List<IrConstructorCall>, javaAnnotations: Collection<JavaAnnotation>?) =
1369+
getNullabilityAnnotationName(t, declOrigin, existingAnnotations, javaAnnotations)?.let {
1370+
pluginContext.referenceClass(it)?.let { annotationClass ->
1371+
annotationClass.owner.declarations.firstIsInstanceOrNull<IrConstructor>()?.let { annotationConstructor ->
1372+
IrConstructorCallImpl.fromSymbolOwner(
1373+
UNDEFINED_OFFSET, UNDEFINED_OFFSET, annotationConstructor.returnType, annotationConstructor.symbol, 0
1374+
)
1375+
}
1376+
}
1377+
}
1378+
13401379
private fun forceExtractFunction(f: IrFunction, parentId: Label<out DbReftype>, extractBody: Boolean, extractMethodAndParameterTypeAccesses: Boolean, extractAnnotations: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?, extractOrigin: Boolean = true, overriddenAttributes: OverriddenFunctionAttributes? = null): Label<out DbCallable> {
13411380
with("function", f) {
13421381
DeclarationStackAdjuster(f, overriddenAttributes).use {
@@ -1427,8 +1466,10 @@ open class KotlinFileExtractor(
14271466

14281467
linesOfCode?.linesOfCodeInDeclaration(f, id)
14291468

1430-
if (extractAnnotations)
1431-
extractAnnotations(f, id, extractMethodAndParameterTypeAccesses)
1469+
if (extractAnnotations) {
1470+
val extraAnnotations = listOfNotNull(getNullabilityAnnotation(f.returnType, f.origin, f.annotations, getJavaCallable(f)?.annotations))
1471+
extractAnnotations(f.annotations + extraAnnotations, id, extractMethodAndParameterTypeAccesses)
1472+
}
14321473

14331474
return id
14341475
}
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 class AnnotatedMethods {
5+
6+
public @A @NotNull String notNullAnnotated(@A @NotNull String param) { return param; }
7+
8+
public @A @Nullable String nullableAnnotated(@A @Nullable String param) { return param; }
9+
10+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
public class JavaUser {
2+
3+
public static void test(KotlinAnnotatedMethods km) {
4+
km.f(null);
5+
}
6+
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import zpkg.A
2+
3+
class KotlinAnnotatedMethods {
4+
5+
@A fun f(@A m: AnnotatedMethods): String = m.notNullAnnotated("hello") + m.nullableAnnotated("world")!!
6+
7+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.jetbrains.annotations;
2+
3+
public @interface NotNull {
4+
String value() default "";
5+
Class<? extends Exception> exception() default Exception.class;
6+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package org.jetbrains.annotations;
2+
3+
public @interface Nullable {
4+
String value() default "";
5+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
| AnnotatedMethods.java:6:29:6:44 | notNullAnnotated | parameter | AnnotatedMethods.java:6:46:6:47 | A |
2+
| AnnotatedMethods.java:6:29:6:44 | notNullAnnotated | parameter | AnnotatedMethods.java:6:49:6:56 | NotNull |
3+
| AnnotatedMethods.java:6:29:6:44 | notNullAnnotated | return value | AnnotatedMethods.java:6:10:6:11 | A |
4+
| AnnotatedMethods.java:6:29:6:44 | notNullAnnotated | return value | AnnotatedMethods.java:6:13:6:20 | NotNull |
5+
| AnnotatedMethods.java:8:30:8:46 | nullableAnnotated | parameter | AnnotatedMethods.java:8:48:8:49 | A |
6+
| AnnotatedMethods.java:8:30:8:46 | nullableAnnotated | parameter | AnnotatedMethods.java:8:51:8:59 | Nullable |
7+
| AnnotatedMethods.java:8:30:8:46 | nullableAnnotated | return value | AnnotatedMethods.java:8:10:8:11 | A |
8+
| AnnotatedMethods.java:8:30:8:46 | nullableAnnotated | return value | AnnotatedMethods.java:8:13:8:21 | Nullable |
9+
| ktUser.kt:5:6:5:105 | f | parameter | ktUser.kt:0:0:0:0 | NotNull |
10+
| ktUser.kt:5:6:5:105 | f | parameter | ktUser.kt:5:12:5:13 | A |
11+
| ktUser.kt:5:6:5:105 | f | return value | ktUser.kt:0:0:0:0 | NotNull |
12+
| ktUser.kt:5:6:5:105 | f | return value | ktUser.kt:5:3:5:4 | A |
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from create_database_utils import *
2+
3+
os.mkdir('out')
4+
os.mkdir('out2')
5+
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")
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import java
2+
3+
from Method m, string origin, Annotation a
4+
where
5+
m.fromSource() and
6+
(
7+
origin = "return value" and a = m.getAnAnnotation()
8+
or
9+
origin = "parameter" and a = m.getAParameter().getAnAnnotation()
10+
)
11+
select m, origin, a
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package zpkg;
2+
3+
public @interface A { }

0 commit comments

Comments
 (0)