JPS: implement tracking of null annotations
#KT-12933 fixed
#KT-14266 fixed
Original commit: 05f278ce20
This commit is contained in:
committed by
Dmitry Jemerov
parent
efd8357035
commit
efc1164489
+18
@@ -1065,6 +1065,24 @@ public class ExperimentalIncrementalJpsTestGenerated extends AbstractExperimenta
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("javaFieldNullabilityChanged")
|
||||||
|
public void testJavaFieldNullabilityChanged() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/javaFieldNullabilityChanged/");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("javaMethodParamNullabilityChanged")
|
||||||
|
public void testJavaMethodParamNullabilityChanged() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/javaMethodParamNullabilityChanged/");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("javaMethodReturnTypeNullabilityChanged")
|
||||||
|
public void testJavaMethodReturnTypeNullabilityChanged() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/javaMethodReturnTypeNullabilityChanged/");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("methodAddedInSuper")
|
@TestMetadata("methodAddedInSuper")
|
||||||
public void testMethodAddedInSuper() throws Exception {
|
public void testMethodAddedInSuper() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/methodAddedInSuper/");
|
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/methodAddedInSuper/");
|
||||||
|
|||||||
+18
@@ -1065,6 +1065,24 @@ public class IncrementalJpsTestGenerated extends AbstractIncrementalJpsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("javaFieldNullabilityChanged")
|
||||||
|
public void testJavaFieldNullabilityChanged() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/javaFieldNullabilityChanged/");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("javaMethodParamNullabilityChanged")
|
||||||
|
public void testJavaMethodParamNullabilityChanged() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/javaMethodParamNullabilityChanged/");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("javaMethodReturnTypeNullabilityChanged")
|
||||||
|
public void testJavaMethodReturnTypeNullabilityChanged() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/javaMethodReturnTypeNullabilityChanged/");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("methodAddedInSuper")
|
@TestMetadata("methodAddedInSuper")
|
||||||
public void testMethodAddedInSuper() throws Exception {
|
public void testMethodAddedInSuper() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/methodAddedInSuper/");
|
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/methodAddedInSuper/");
|
||||||
|
|||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
org.jetbrains.jps.builders.java.dependencyView.NullabilityAnnotationsTracker
|
||||||
+73
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.jps.builders.java.dependencyView
|
||||||
|
|
||||||
|
import gnu.trove.TIntHashSet
|
||||||
|
import org.jetbrains.jps.builders.java.dependencyView.TypeRepr.ClassType
|
||||||
|
import org.jetbrains.kotlin.fileClasses.internalNameWithoutInnerClasses
|
||||||
|
import org.jetbrains.kotlin.load.java.JAVAX_NONNULL_ANNOTATION
|
||||||
|
import org.jetbrains.kotlin.load.java.NOT_NULL_ANNOTATIONS
|
||||||
|
import org.jetbrains.kotlin.load.java.NULLABLE_ANNOTATIONS
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
internal class NullabilityAnnotationsTracker : AnnotationsChangeTracker() {
|
||||||
|
private val annotations = (NULLABLE_ANNOTATIONS + JAVAX_NONNULL_ANNOTATION + NOT_NULL_ANNOTATIONS).mapTo(HashSet()) { it.internalNameWithoutInnerClasses }.toTypedArray()
|
||||||
|
|
||||||
|
override fun methodAnnotationsChanged(
|
||||||
|
context: DependencyContext,
|
||||||
|
method: MethodRepr,
|
||||||
|
annotationsDiff: Difference.Specifier<ClassType, Difference>,
|
||||||
|
paramAnnotationsDiff: Difference.Specifier<ParamAnnotation, Difference>
|
||||||
|
): Set<Recompile> {
|
||||||
|
val changedAnnotations = annotationsDiff.addedOrRemoved() +
|
||||||
|
paramAnnotationsDiff.addedOrRemoved().map { it.type }
|
||||||
|
|
||||||
|
return handleNullAnnotationsChanges(context, method, changedAnnotations)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun fieldAnnotationsChanged(
|
||||||
|
context: NamingContext,
|
||||||
|
field: FieldRepr,
|
||||||
|
annotationsDiff: Difference.Specifier<ClassType, Difference>
|
||||||
|
): Set<Recompile> {
|
||||||
|
return handleNullAnnotationsChanges(context, field, annotationsDiff.addedOrRemoved())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleNullAnnotationsChanges(context: NamingContext, protoMember: ProtoMember, annotations: Sequence<TypeRepr.ClassType>): Set<Recompile> {
|
||||||
|
val nullabilityAnnotations = TIntHashSet(this.annotations.toIntArray { context.get(it) })
|
||||||
|
val changedNullAnnotation = annotations.firstOrNull { nullabilityAnnotations.contains(it.className) }
|
||||||
|
|
||||||
|
val result = EnumSet.noneOf(Recompile::class.java)
|
||||||
|
if (changedNullAnnotation != null) {
|
||||||
|
result.add(Recompile.USAGES)
|
||||||
|
|
||||||
|
if (protoMember is MethodRepr && !protoMember.isFinal) {
|
||||||
|
// methods can be overridden, whereas fields cannot be
|
||||||
|
result.add(Recompile.SUBCLASSES)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> Difference.Specifier<T, Difference>.addedOrRemoved(): Sequence<T> =
|
||||||
|
added().asSequence() + removed().asSequence()
|
||||||
|
|
||||||
|
private inline fun <T> Array<T>.toIntArray(fn: (T)->Int): IntArray =
|
||||||
|
IntArray(size) { i -> fn(get(i)) }
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
class A {
|
||||||
|
@Nullable String s = "A.s";
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
class A {
|
||||||
|
@NotNull String s = "A.s";
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
internal class AChild : A()
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
class Dummy
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
================ Step #1 =================
|
||||||
|
|
||||||
|
Cleaning output files:
|
||||||
|
out/production/module/A.class
|
||||||
|
End of files
|
||||||
|
Exit code: NOTHING_DONE
|
||||||
|
------------------------------------------
|
||||||
|
Compiling files:
|
||||||
|
src/A.java
|
||||||
|
End of files
|
||||||
|
Cleaning output files:
|
||||||
|
out/production/module/META-INF/module.kotlin_module
|
||||||
|
out/production/module/UseAKt.class
|
||||||
|
End of files
|
||||||
|
Compiling files:
|
||||||
|
src/useA.kt
|
||||||
|
End of files
|
||||||
|
Exit code: OK
|
||||||
|
------------------------------------------
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun useA() {
|
||||||
|
A().s
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
class A {
|
||||||
|
void f(@Nullable String s) {
|
||||||
|
System.out.println(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
class A {
|
||||||
|
void f(@NotNull String s) {
|
||||||
|
System.out.println(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
internal class AChild : A() {
|
||||||
|
override fun f(s: String?) {}
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
internal class AChild : A() {
|
||||||
|
override fun f(s: String) {}
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
class Dummy
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
================ Step #1 =================
|
||||||
|
|
||||||
|
Cleaning output files:
|
||||||
|
out/production/module/A.class
|
||||||
|
End of files
|
||||||
|
Exit code: NOTHING_DONE
|
||||||
|
------------------------------------------
|
||||||
|
Compiling files:
|
||||||
|
src/A.java
|
||||||
|
End of files
|
||||||
|
Cleaning output files:
|
||||||
|
out/production/module/AChild.class
|
||||||
|
out/production/module/META-INF/module.kotlin_module
|
||||||
|
out/production/module/UseAKt.class
|
||||||
|
End of files
|
||||||
|
Compiling files:
|
||||||
|
src/AChild.kt
|
||||||
|
src/useA.kt
|
||||||
|
End of files
|
||||||
|
Exit code: ABORT
|
||||||
|
------------------------------------------
|
||||||
|
COMPILATION FAILED
|
||||||
|
'f' overrides nothing
|
||||||
|
Null can not be a value of a non-null type String
|
||||||
|
|
||||||
|
================ Step #2 =================
|
||||||
|
|
||||||
|
Cleaning output files:
|
||||||
|
out/production/module/A.class
|
||||||
|
End of files
|
||||||
|
Compiling files:
|
||||||
|
src/AChild.kt
|
||||||
|
src/useA.kt
|
||||||
|
End of files
|
||||||
|
Exit code: ADDITIONAL_PASS_REQUIRED
|
||||||
|
------------------------------------------
|
||||||
|
Compiling files:
|
||||||
|
src/A.java
|
||||||
|
End of files
|
||||||
|
Cleaning output files:
|
||||||
|
out/production/module/Dummy.class
|
||||||
|
End of files
|
||||||
|
Compiling files:
|
||||||
|
src/Dummy.kt
|
||||||
|
End of files
|
||||||
|
Exit code: OK
|
||||||
|
------------------------------------------
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
================ Step #1 =================
|
||||||
|
|
||||||
|
Cleaning output files:
|
||||||
|
out/production/module/A.class
|
||||||
|
End of files
|
||||||
|
Exit code: NOTHING_DONE
|
||||||
|
------------------------------------------
|
||||||
|
Compiling files:
|
||||||
|
src/A.java
|
||||||
|
End of files
|
||||||
|
Cleaning output files:
|
||||||
|
out/production/module/AChild.class
|
||||||
|
out/production/module/META-INF/module.kotlin_module
|
||||||
|
out/production/module/UseAKt.class
|
||||||
|
End of files
|
||||||
|
Compiling files:
|
||||||
|
src/AChild.kt
|
||||||
|
src/useA.kt
|
||||||
|
End of files
|
||||||
|
Exit code: ABORT
|
||||||
|
------------------------------------------
|
||||||
|
COMPILATION FAILED
|
||||||
|
'f' overrides nothing
|
||||||
|
Null can not be a value of a non-null type String
|
||||||
|
|
||||||
|
================ Step #2 =================
|
||||||
|
|
||||||
|
Cleaning output files:
|
||||||
|
out/production/module/A.class
|
||||||
|
End of files
|
||||||
|
Compiling files:
|
||||||
|
src/AChild.kt
|
||||||
|
src/useA.kt
|
||||||
|
End of files
|
||||||
|
Exit code: OK
|
||||||
|
------------------------------------------
|
||||||
|
Compiling files:
|
||||||
|
src/A.java
|
||||||
|
End of files
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun useA() {
|
||||||
|
A().f(null)
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun useA() {
|
||||||
|
A().f("useA")
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
class A {
|
||||||
|
@Nullable
|
||||||
|
String f() {
|
||||||
|
return "A.f";
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
class A {
|
||||||
|
@NotNull
|
||||||
|
String f() {
|
||||||
|
return "A.f";
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
internal class AChild : A() {
|
||||||
|
override fun f(): String? = "AChild.f"
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
internal class AChild : A() {
|
||||||
|
override fun f(): String = "AChild.f"
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
class Dummy
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
================ Step #1 =================
|
||||||
|
|
||||||
|
Cleaning output files:
|
||||||
|
out/production/module/A.class
|
||||||
|
End of files
|
||||||
|
Exit code: NOTHING_DONE
|
||||||
|
------------------------------------------
|
||||||
|
Compiling files:
|
||||||
|
src/A.java
|
||||||
|
End of files
|
||||||
|
Cleaning output files:
|
||||||
|
out/production/module/AChild.class
|
||||||
|
out/production/module/META-INF/module.kotlin_module
|
||||||
|
out/production/module/UseAKt.class
|
||||||
|
End of files
|
||||||
|
Compiling files:
|
||||||
|
src/AChild.kt
|
||||||
|
src/useA.kt
|
||||||
|
End of files
|
||||||
|
Exit code: ABORT
|
||||||
|
------------------------------------------
|
||||||
|
COMPILATION FAILED
|
||||||
|
Return type of 'f' is not a subtype of the return type of the overridden member '@NotNull public/*package*/ open fun f(): String defined in A'
|
||||||
|
|
||||||
|
================ Step #2 =================
|
||||||
|
|
||||||
|
Cleaning output files:
|
||||||
|
out/production/module/A.class
|
||||||
|
End of files
|
||||||
|
Compiling files:
|
||||||
|
src/AChild.kt
|
||||||
|
src/useA.kt
|
||||||
|
End of files
|
||||||
|
Exit code: ADDITIONAL_PASS_REQUIRED
|
||||||
|
------------------------------------------
|
||||||
|
Compiling files:
|
||||||
|
src/A.java
|
||||||
|
End of files
|
||||||
|
Cleaning output files:
|
||||||
|
out/production/module/Dummy.class
|
||||||
|
End of files
|
||||||
|
Compiling files:
|
||||||
|
src/Dummy.kt
|
||||||
|
End of files
|
||||||
|
Exit code: OK
|
||||||
|
------------------------------------------
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
================ Step #1 =================
|
||||||
|
|
||||||
|
Cleaning output files:
|
||||||
|
out/production/module/A.class
|
||||||
|
End of files
|
||||||
|
Exit code: NOTHING_DONE
|
||||||
|
------------------------------------------
|
||||||
|
Compiling files:
|
||||||
|
src/A.java
|
||||||
|
End of files
|
||||||
|
Cleaning output files:
|
||||||
|
out/production/module/AChild.class
|
||||||
|
out/production/module/META-INF/module.kotlin_module
|
||||||
|
out/production/module/UseAKt.class
|
||||||
|
End of files
|
||||||
|
Compiling files:
|
||||||
|
src/AChild.kt
|
||||||
|
src/useA.kt
|
||||||
|
End of files
|
||||||
|
Exit code: ABORT
|
||||||
|
------------------------------------------
|
||||||
|
COMPILATION FAILED
|
||||||
|
Return type of 'f' is not a subtype of the return type of the overridden member '@NotNull public/*package*/ open fun f(): String defined in A'
|
||||||
|
|
||||||
|
================ Step #2 =================
|
||||||
|
|
||||||
|
Cleaning output files:
|
||||||
|
out/production/module/A.class
|
||||||
|
End of files
|
||||||
|
Compiling files:
|
||||||
|
src/AChild.kt
|
||||||
|
src/useA.kt
|
||||||
|
End of files
|
||||||
|
Exit code: OK
|
||||||
|
------------------------------------------
|
||||||
|
Compiling files:
|
||||||
|
src/A.java
|
||||||
|
End of files
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun useA() {
|
||||||
|
val s = A().f()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user