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);
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testMethodAddedInSuper() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/methodAddedInSuper/");
|
||||
|
||||
+18
@@ -1065,6 +1065,24 @@ public class IncrementalJpsTestGenerated extends AbstractIncrementalJpsTest {
|
||||
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")
|
||||
public void testMethodAddedInSuper() throws Exception {
|
||||
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