Always consider container's experimentality in ExperimentalUsageChecker

This results in more diagnostics usually, but allows library authors to
avoid annotating everything in each experimental class with the marker
(only the class needs to be annotated now)

 #KT-22759
This commit is contained in:
Alexander Udalov
2018-02-07 16:20:47 +01:00
parent 7123202670
commit a4af9dd5d1
5 changed files with 150 additions and 3 deletions
@@ -142,9 +142,7 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
val container = containingDeclaration
if (container is ClassDescriptor && this !is ConstructorDescriptor) {
for (annotation in container.annotations) {
result.addIfNotNull(annotation.annotationClass?.loadExperimentalityForMarkerAnnotation())
}
result.addAll(container.loadExperimentalities(moduleAnnotationsResolver))
}
for (moduleAnnotationClassId in moduleAnnotationsResolver.getAnnotationsOnContainingModule(this)) {
@@ -0,0 +1,61 @@
// !API_VERSION: 1.3
// MODULE: api
// FILE: api.kt
package api
@Experimental(Experimental.Level.WARNING, [Experimental.Impact.COMPILATION])
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class ExperimentalAPI
@ExperimentalAPI
class C {
class D {
class E {
class F
}
}
}
// MODULE: usage1(api)
// FILE: usage-propagate.kt
package usage1
import api.*
@ExperimentalAPI
fun use1() {
C.D.E.F()
}
@ExperimentalAPI
fun use2(f: C.D.E.F) = f.hashCode()
// MODULE: usage2(api)
// FILE: usage-use.kt
package usage2
import api.*
@UseExperimental(ExperimentalAPI::class)
fun use1() {
C.D.E.F()
}
@UseExperimental(ExperimentalAPI::class)
fun use2(f: <!EXPERIMENTAL_API_USAGE!>C<!>.<!EXPERIMENTAL_API_USAGE!>D<!>.<!EXPERIMENTAL_API_USAGE!>E<!>.<!EXPERIMENTAL_API_USAGE!>F<!>) = f.hashCode()
// MODULE: usage3(api)
// FILE: usage-none.kt
package usage3
import api.*
fun use1() {
<!EXPERIMENTAL_API_USAGE!>C<!>.<!EXPERIMENTAL_API_USAGE!>D<!>.<!EXPERIMENTAL_API_USAGE!>E<!>.<!EXPERIMENTAL_API_USAGE!>F<!>()
}
fun use2(f: <!EXPERIMENTAL_API_USAGE!>C<!>.<!EXPERIMENTAL_API_USAGE!>D<!>.<!EXPERIMENTAL_API_USAGE!>E<!>.<!EXPERIMENTAL_API_USAGE!>F<!>) = f.<!EXPERIMENTAL_API_USAGE!>hashCode<!>()
@@ -0,0 +1,76 @@
// -- Module: <api> --
package
package api {
@api.ExperimentalAPI public final class C {
public constructor C()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public final class D {
public constructor D()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public final class E {
public constructor E()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public final class F {
public constructor F()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
}
}
@kotlin.Experimental(changesMayBreak = {Impact.COMPILATION}, level = Level.WARNING) @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.FUNCTION}) public final annotation class ExperimentalAPI : kotlin.Annotation {
public constructor ExperimentalAPI()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
// -- Module: <usage1> --
package
package api {
}
package usage1 {
@api.ExperimentalAPI public fun use1(): kotlin.Unit
@api.ExperimentalAPI public fun use2(/*0*/ f: api.C.D.E.F): kotlin.Int
}
// -- Module: <usage2> --
package
package api {
}
package usage2 {
@kotlin.UseExperimental(markerClass = {api.ExperimentalAPI::class}) public fun use1(): kotlin.Unit
@kotlin.UseExperimental(markerClass = {api.ExperimentalAPI::class}) public fun use2(/*0*/ f: api.C.D.E.F): kotlin.Int
}
// -- Module: <usage3> --
package
package api {
}
package usage3 {
public fun use1(): kotlin.Unit
public fun use2(/*0*/ f: api.C.D.E.F): kotlin.Int
}
@@ -1839,6 +1839,12 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW
doTest(fileName);
}
@TestMetadata("deeplyNestedClass.kt")
public void testDeeplyNestedClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/experimental/deeplyNestedClass.kt");
doTest(fileName);
}
@TestMetadata("errors.kt")
public void testErrors() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/experimental/errors.kt");
@@ -1839,6 +1839,12 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno
doTest(fileName);
}
@TestMetadata("deeplyNestedClass.kt")
public void testDeeplyNestedClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/experimental/deeplyNestedClass.kt");
doTest(fileName);
}
@TestMetadata("errors.kt")
public void testErrors() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/experimental/errors.kt");