Disallow usages of experimental markers as qualifiers

Only allow explicit imports of markers, but not of nested classes
declared in them
This commit is contained in:
Alexander Udalov
2018-05-04 17:06:26 +02:00
parent d27657e8f6
commit 143c3ccb95
3 changed files with 47 additions and 7 deletions
@@ -26,7 +26,6 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getTopmostParentQualifiedExpressionForSelector
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
@@ -246,7 +245,7 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
else -> null
}
if (targetClass != null && targetClass.loadExperimentalityForMarkerAnnotation() != null) {
if (!element.isUsageAsAnnotationOrQualifier() &&
if (!element.isUsageAsAnnotationOrImport() &&
!element.isUsageAsUseExperimentalArgument(context.trace.bindingContext)
) {
context.trace.report(
@@ -261,12 +260,12 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
context.trace.report(Errors.EXPERIMENTAL_IS_NOT_ENABLED.on(element))
}
if (!element.isUsageAsAnnotationOrQualifier()) {
if (!element.isUsageAsAnnotationOrImport() && !element.isUsageAsQualifier()) {
context.trace.report(Errors.EXPERIMENTAL_CAN_ONLY_BE_USED_AS_ANNOTATION.on(element))
}
}
private fun PsiElement.isUsageAsAnnotationOrQualifier(): Boolean {
private fun PsiElement.isUsageAsAnnotationOrImport(): Boolean {
val parent = parent
if (parent is KtUserType) {
@@ -275,6 +274,10 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
parent.parent.parent.parent is KtAnnotationEntry
}
return parent is KtDotQualifiedExpression && parent.parent is KtImportDirective
}
private fun PsiElement.isUsageAsQualifier(): Boolean {
if (this is KtSimpleNameExpression) {
val qualifier = getTopmostParentQualifiedExpressionForSelector() ?: this
if ((qualifier.parent as? KtDotQualifiedExpression)?.receiverExpression == qualifier) {
@@ -282,8 +285,6 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
}
}
if (getParentOfType<KtImportDirective>(true) != null) return true
return false
}
@@ -1,5 +1,7 @@
// !LANGUAGE: +NestedClassesInAnnotations
// !USE_EXPERIMENTAL: kotlin.Experimental
// !DIAGNOSTICS: -UNUSED_PARAMETER
// FILE: api.kt
package test
@@ -43,7 +45,13 @@ fun f5() {}
// Usages of markers as types should be errors
@Experimental
annotation class Marker
annotation class Marker {
class NestedClass
companion object {
const val value = 42
}
}
fun f6(m: <!EXPERIMENTAL_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_USE_EXPERIMENTAL!>Marker<!>) {}
fun f7(): List<<!EXPERIMENTAL_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_USE_EXPERIMENTAL!>Marker<!>>? = null
@@ -52,3 +60,18 @@ fun f8(): test.<!EXPERIMENTAL_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_
typealias Marker0 = <!EXPERIMENTAL_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_USE_EXPERIMENTAL!>Marker<!>
fun f9(m: <!EXPERIMENTAL_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_USE_EXPERIMENTAL!>Marker0<!>) {}
// Usages of markers as qualifiers are errors as well (we can lift this restriction for select cases)
fun f10(m: <!EXPERIMENTAL_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_USE_EXPERIMENTAL!>Marker<!>.NestedClass) {
<!EXPERIMENTAL_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_USE_EXPERIMENTAL!>Marker<!>.value
}
// FILE: usage-from-other-file.kt
// Usages of markers in import statements should be OK, but not as qualifiers to import their nested classes
import test.Marker
import test.<!EXPERIMENTAL_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_USE_EXPERIMENTAL!>Marker<!>.NestedClass
import test.<!EXPERIMENTAL_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_USE_EXPERIMENTAL!>Marker<!>.Companion
@@ -2,6 +2,7 @@ package
package test {
public fun f1(/*0*/ e: kotlin.Experimental): kotlin.Unit
public fun f10(/*0*/ m: test.Marker.NestedClass): kotlin.Unit
public fun f2(/*0*/ u: kotlin.UseExperimental?): kotlin.Unit
public fun f3(/*0*/ e: test.Experimental0 /* = kotlin.Experimental */): kotlin.Unit
public fun f4(/*0*/ u: test.UseExperimental0 /* = kotlin.UseExperimental */): kotlin.Unit
@@ -23,6 +24,21 @@ package test {
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 companion object Companion {
private constructor Companion()
public const final val value: kotlin.Int = 42
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 NestedClass {
public constructor NestedClass()
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 annotation class VarargKClasses : kotlin.Annotation {