JPS: implement tracking of null annotations

#KT-12933 fixed
    #KT-14266 fixed

Original commit: 05f278ce20
This commit is contained in:
Alexey Tsvetkov
2016-08-29 17:43:40 +03:00
committed by Dmitry Jemerov
parent efd8357035
commit efc1164489
27 changed files with 367 additions and 0 deletions
@@ -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/");
@@ -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/");
@@ -0,0 +1 @@
org.jetbrains.jps.builders.java.dependencyView.NullabilityAnnotationsTracker
@@ -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)) }
}
@@ -0,0 +1,5 @@
import org.jetbrains.annotations.Nullable;
class A {
@Nullable String s = "A.s";
}
@@ -0,0 +1,5 @@
import org.jetbrains.annotations.NotNull;
class A {
@NotNull String s = "A.s";
}
@@ -0,0 +1 @@
internal class AChild : A()
@@ -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
------------------------------------------
@@ -0,0 +1,7 @@
import org.jetbrains.annotations.Nullable;
class A {
void f(@Nullable String s) {
System.out.println(s);
}
}
@@ -0,0 +1,7 @@
import org.jetbrains.annotations.NotNull;
class A {
void f(@NotNull String s) {
System.out.println(s);
}
}
@@ -0,0 +1,3 @@
internal class AChild : A() {
override fun f(s: String?) {}
}
@@ -0,0 +1,3 @@
internal class AChild : A() {
override fun f(s: String) {}
}
@@ -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
------------------------------------------
@@ -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
@@ -0,0 +1,3 @@
fun useA() {
A().f(null)
}
@@ -0,0 +1,8 @@
import org.jetbrains.annotations.Nullable;
class A {
@Nullable
String f() {
return "A.f";
}
}
@@ -0,0 +1,8 @@
import org.jetbrains.annotations.NotNull;
class A {
@NotNull
String f() {
return "A.f";
}
}
@@ -0,0 +1,3 @@
internal class AChild : A() {
override fun f(): String? = "AChild.f"
}
@@ -0,0 +1,3 @@
internal class AChild : A() {
override fun f(): String = "AChild.f"
}
@@ -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
------------------------------------------
@@ -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
@@ -0,0 +1,3 @@
fun useA() {
val s = A().f()
}