Prohibit using annotations without TYPE target in several type positions

#KT-8325 Fixed
#KT-19455 Fixed
#KT-25876 Fixed
#KT-28449 Fixed
This commit is contained in:
Dmitriy Novozhilov
2021-04-08 16:07:45 +03:00
committed by TeamCityServer
parent 1cff17bb58
commit 0b2943d34c
19 changed files with 328 additions and 12 deletions
@@ -1813,6 +1813,40 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
}
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/annotations/typeUse")
@TestDataPath("$PROJECT_ROOT")
public class TypeUse {
@Test
public void testAllFilesPresentInTypeUse() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/annotations/typeUse"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@Test
@TestMetadata("kt19455.kt")
public void testKt19455() throws Exception {
runTest("compiler/testData/diagnostics/tests/annotations/typeUse/kt19455.kt");
}
@Test
@TestMetadata("kt25876.kt")
public void testKt25876() throws Exception {
runTest("compiler/testData/diagnostics/tests/annotations/typeUse/kt25876.kt");
}
@Test
@TestMetadata("kt8325.kt")
public void testKt8325() throws Exception {
runTest("compiler/testData/diagnostics/tests/annotations/typeUse/kt8325.kt");
}
@Test
@TestMetadata("nonTypeUseAnnotationOnType.kt")
public void testNonTypeUseAnnotationOnType() throws Exception {
runTest("compiler/testData/diagnostics/tests/annotations/typeUse/nonTypeUseAnnotationOnType.kt");
}
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/annotations/withUseSiteTarget")
@TestDataPath("$PROJECT_ROOT")
@@ -10,4 +10,3 @@ import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
abstract class ConeTypeParameterType : ConeLookupTagBasedType() {
abstract override val lookupTag: ConeTypeParameterLookupTag
}
@@ -6,7 +6,7 @@
package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageFeature.*
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
@@ -41,7 +41,7 @@ class AnnotationChecker(
if (annotated is KtProperty) {
checkPropertyUseSiteTargetAnnotations(annotated, trace)
}
if (annotated is KtClass) {
if (annotated is KtClassOrObject) {
checkSuperTypeAnnotations(annotated, trace)
}
if (annotated is KtCallableDeclaration) {
@@ -51,12 +51,31 @@ class AnnotationChecker(
if (annotated is KtTypeAlias) {
annotated.getTypeReference()?.let { check(it, trace) }
}
if (annotated is KtTypeParameterListOwner && annotated is KtCallableDeclaration) {
// TODO: support type parameter annotations for type parameters on classes and properties
if (
annotated is KtTypeParameterListOwner &&
(annotated is KtCallableDeclaration || languageVersionSettings.supportsFeature(ProperCheckAnnotationsTargetInTypeUsePositions))
) {
annotated.typeParameters.forEach { check(it, trace) }
for (typeParameter in annotated.typeParameters) {
typeParameter.extendsBound?.let {
checkTypeReference(
it,
trace,
shouldCheckReferenceItself = true,
checkWithoutLanguageFeature = annotated is KtCallableDeclaration
)
}
}
for (typeConstraint in annotated.typeConstraints) {
typeConstraint.boundTypeReference?.let { checkTypeReference(it, trace, shouldCheckReferenceItself = true) }
}
}
if (annotated is KtTypeReference) {
annotated.typeElement?.typeArgumentsAsTypes?.filterNotNull()?.forEach { check(it, trace) }
if (languageVersionSettings.supportsFeature(ProperCheckAnnotationsTargetInTypeUsePositions)) {
checkTypeReference(annotated, trace)
} else {
annotated.typeElement?.typeArgumentsAsTypes?.filterNotNull()?.forEach { check(it, trace) }
}
}
if (annotated is KtDeclarationWithBody) {
// JetFunction or JetPropertyAccessor
@@ -83,7 +102,7 @@ class AnnotationChecker(
private fun checkPropertyUseSiteTargetAnnotations(property: KtProperty, trace: BindingTrace) {
fun List<KtAnnotationEntry>?.getDescriptors() = this?.mapNotNull { trace.get(BindingContext.ANNOTATION, it)?.annotationClass } ?: listOf()
val reportError = languageVersionSettings.supportsFeature(LanguageFeature.ProhibitRepeatedUseSiteTargetAnnotations)
val reportError = languageVersionSettings.supportsFeature(ProhibitRepeatedUseSiteTargetAnnotations)
val propertyAnnotations = mapOf(
AnnotationUseSiteTarget.PROPERTY_GETTER to property.getter?.annotationEntries.getDescriptors(),
@@ -107,21 +126,40 @@ class AnnotationChecker(
}
}
private fun checkSuperTypeAnnotations(annotated: KtClass, trace: BindingTrace) {
val reportError = languageVersionSettings.supportsFeature(LanguageFeature.ProhibitUseSiteTargetAnnotationsOnSuperTypes)
private fun checkSuperTypeAnnotations(annotated: KtClassOrObject, trace: BindingTrace) {
for (superType in annotated.superTypeListEntries.mapNotNull { it.typeReference }) {
for (entry in superType.annotationEntries) {
if (entry.useSiteTarget != null) {
checkTypeReference(superType, trace, isSuperType = true)
}
}
private fun checkTypeReference(
reference: KtTypeReference,
trace: BindingTrace,
isSuperType: Boolean = false,
shouldCheckReferenceItself: Boolean = false,
checkWithoutLanguageFeature: Boolean = false
) {
val shouldRunCheck = isSuperType || shouldCheckReferenceItself
if (shouldRunCheck) {
for (entry in reference.annotationEntries) {
val actualTargets = getActualTargetList(reference, null, trace.bindingContext)
if (entry.useSiteTarget != null && isSuperType) {
val reportError = languageVersionSettings.supportsFeature(ProhibitUseSiteTargetAnnotationsOnSuperTypes)
val diagnostic = if (reportError) {
Errors.ANNOTATION_ON_SUPERCLASS.on(entry)
} else {
Errors.ANNOTATION_ON_SUPERCLASS_WARNING.on(entry)
}
trace.report(diagnostic)
} else if (shouldRunCheck && (languageVersionSettings.supportsFeature(ProperCheckAnnotationsTargetInTypeUsePositions) || checkWithoutLanguageFeature)) {
checkAnnotationEntry(entry, actualTargets, trace)
}
}
}
val typeArguments = reference.typeElement?.typeArgumentsAsTypes ?: return
for (typeArgument in typeArguments) {
typeArgument?.let { checkTypeReference(it, trace, shouldCheckReferenceItself = true) }
}
}
private fun KtAnnotated?.getImplicitUseSiteTargetList(): List<AnnotationUseSiteTarget> = when (this) {
@@ -344,6 +344,9 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
return;
}
checkForCastImpossibilityOrRedundancy(expression, actualType, targetType, context);
if (context.languageVersionSettings.supportsFeature(LanguageFeature.ProperCheckAnnotationsTargetInTypeUsePositions)) {
components.annotationChecker.check(expression.getRight(), context.trace, null);
}
}
private void checkForCastImpossibilityOrRedundancy(
@@ -67,6 +67,7 @@ public class ExpressionTypingComponents {
/*package*/ NewKotlinTypeChecker kotlinTypeChecker;
/*package*/ TypeResolutionInterceptor typeResolutionInterceptor;
/*package*/ MissingSupertypesResolver missingSupertypesResolver;
/*package*/ AnnotationChecker annotationChecker;
@Inject
@@ -258,4 +259,9 @@ public class ExpressionTypingComponents {
public void setMissingSupertypesResolver(@NotNull MissingSupertypesResolver missingSupertypesResolver) {
this.missingSupertypesResolver = missingSupertypesResolver;
}
@Inject
public void setAnnotationChecker(@NotNull AnnotationChecker annotationChecker) {
this.annotationChecker = annotationChecker;
}
}
@@ -642,6 +642,10 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
detectRedundantIs(context, subjectType, targetType, isCheck, negated, subjectDataFlowValue)
if (context.languageVersionSettings.supportsFeature(LanguageFeature.ProperCheckAnnotationsTargetInTypeUsePositions)) {
components.annotationChecker.check(typeReferenceAfterIs, context.trace)
}
if (CastDiagnosticsUtil.isCastErased(subjectType, targetType, KotlinTypeChecker.DEFAULT)) {
context.trace.report(Errors.CANNOT_CHECK_FOR_ERASED.on(typeReferenceAfterIs, targetType))
}
@@ -0,0 +1,14 @@
// FIR_IDENTICAL
// !LANGUAGE: +ProperCheckAnnotationsTargetInTypeUsePositions
// ISSUE: KT-19455
package test
@Target(AnnotationTarget.TYPE)
annotation class TypeAnn
open class TypeToken<T>
object Test : TypeToken<@TypeAnn String>() // (1)
val test = object : TypeToken<@TypeAnn String>() {} // (2)
@@ -0,0 +1,26 @@
package
package test {
public val test: test.TypeToken<@test.TypeAnn kotlin.String>
public object Test : test.TypeToken<@test.TypeAnn kotlin.String> {
private constructor 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
}
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.TYPE}) public final annotation class TypeAnn : kotlin.Annotation {
public constructor TypeAnn()
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 open class TypeToken</*0*/ T> {
public constructor TypeToken</*0*/ T>()
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
}
}
@@ -0,0 +1,13 @@
// !LANGUAGE: +ProperCheckAnnotationsTargetInTypeUsePositions
// ISSUE: KT-25876
@Target(AnnotationTarget.TYPE)
annotation class Anno(val value: String)
fun foo(x: String): @Anno(Lorem, ipsum::class, "dolor", sit-amet) String { // OK
return x
}
abstract class Foo : @Anno(o_O) Throwable() // OK
abstract class Bar<T : @Anno(O_o) Any> // OK
@@ -0,0 +1,13 @@
// !LANGUAGE: +ProperCheckAnnotationsTargetInTypeUsePositions
// ISSUE: KT-25876
@Target(AnnotationTarget.TYPE)
annotation class Anno(val value: String)
fun foo(x: String): @Anno(<!UNRESOLVED_REFERENCE!>Lorem<!>, <!TOO_MANY_ARGUMENTS!><!UNRESOLVED_REFERENCE!>ipsum<!>::class<!>, <!TOO_MANY_ARGUMENTS!>"dolor"<!>, <!TOO_MANY_ARGUMENTS!><!UNRESOLVED_REFERENCE!>sit<!><!DEBUG_INFO_MISSING_UNRESOLVED!>-<!><!UNRESOLVED_REFERENCE!>amet<!><!>) String { // OK
return x
}
abstract class Foo : @Anno(<!UNRESOLVED_REFERENCE!>o_O<!>) Throwable() // OK
abstract class Bar<T : @Anno(<!UNRESOLVED_REFERENCE!>O_o<!>) Any> // OK
@@ -0,0 +1,37 @@
package
public fun foo(/*0*/ x: kotlin.String): @Anno kotlin.String
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.TYPE}) public final annotation class Anno : kotlin.Annotation {
public constructor Anno(/*0*/ value: kotlin.String)
public final val value: kotlin.String
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 abstract class Bar</*0*/ T : @Anno kotlin.Any> {
public constructor Bar</*0*/ T : @Anno kotlin.Any>()
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 abstract class Foo : @Anno kotlin.Throwable {
public constructor Foo()
public open override /*1*/ /*fake_override*/ val cause: kotlin.Throwable?
public open override /*1*/ /*fake_override*/ val message: kotlin.String?
public final override /*1*/ /*fake_override*/ fun addSuppressed(/*0*/ exception: kotlin.Throwable!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun fillInStackTrace(): kotlin.Throwable!
public open override /*1*/ /*fake_override*/ fun getLocalizedMessage(): kotlin.String!
public open override /*1*/ /*fake_override*/ fun getStackTrace(): kotlin.Array<(out) java.lang.StackTraceElement!>!
public final override /*1*/ /*fake_override*/ fun getSuppressed(): kotlin.Array<(out) kotlin.Throwable!>!
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun initCause(/*0*/ cause: kotlin.Throwable!): kotlin.Throwable!
public open override /*1*/ /*fake_override*/ fun printStackTrace(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun printStackTrace(/*0*/ s: java.io.PrintStream!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun printStackTrace(/*0*/ s: java.io.PrintWriter!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun setStackTrace(/*0*/ stackTrace: kotlin.Array<(out) java.lang.StackTraceElement!>!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,6 @@
// !LANGUAGE: +ProperCheckAnnotationsTargetInTypeUsePositions
// ISSUE: KT-8325
fun foo() {
object : @<!UNRESOLVED_REFERENCE, UNRESOLVED_REFERENCE!>__UNRESOLVED__<!> Any() {}
}
@@ -0,0 +1,6 @@
// !LANGUAGE: +ProperCheckAnnotationsTargetInTypeUsePositions
// ISSUE: KT-8325
fun foo() {
object : @<!UNRESOLVED_REFERENCE!>__UNRESOLVED__<!> Any() {}
}
@@ -0,0 +1,3 @@
package
public fun foo(): kotlin.Unit
@@ -0,0 +1,22 @@
// !LANGUAGE: +ProperCheckAnnotationsTargetInTypeUsePositions
// ISSUE: KT-28449
@Target(AnnotationTarget.PROPERTY_GETTER)
annotation class Ann
abstract class Foo : @Ann Any()
abstract class Bar<T : @Ann Any>
fun test_1(a: Any) {
if (a is @Ann String) return
}
open class TypeToken<T>
val test_2 = object : TypeToken<@Ann String>() {}
fun test_3(a: Any) {
a as @Ann Int
}
fun <T> test_4() where T : @Ann Any, T : @Ann CharSequence {}
@@ -0,0 +1,22 @@
// !LANGUAGE: +ProperCheckAnnotationsTargetInTypeUsePositions
// ISSUE: KT-28449
@Target(AnnotationTarget.PROPERTY_GETTER)
annotation class Ann
abstract class Foo : <!WRONG_ANNOTATION_TARGET!>@Ann<!> Any()
abstract class Bar<T : <!WRONG_ANNOTATION_TARGET!>@Ann<!> Any>
fun test_1(a: Any) {
if (a is <!WRONG_ANNOTATION_TARGET!>@Ann<!> String) return
}
open class TypeToken<T>
val test_2 = object : TypeToken<<!WRONG_ANNOTATION_TARGET!>@Ann<!> String>() {}
fun test_3(a: Any) {
a as <!WRONG_ANNOTATION_TARGET!>@Ann<!> Int
}
fun <T> test_4() where T : <!WRONG_ANNOTATION_TARGET!>@Ann<!> Any, T : <!WRONG_ANNOTATION_TARGET!>@Ann<!> CharSequence {}
@@ -0,0 +1,35 @@
package
public val test_2: TypeToken<@Ann kotlin.String>
public fun test_1(/*0*/ a: kotlin.Any): kotlin.Unit
public fun test_3(/*0*/ a: kotlin.Any): kotlin.Unit
public fun </*0*/ T : @Ann kotlin.Any> test_4(): kotlin.Unit where T : @Ann kotlin.CharSequence
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.PROPERTY_GETTER}) public final annotation class Ann : kotlin.Annotation {
public constructor Ann()
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 abstract class Bar</*0*/ T : @Ann kotlin.Any> {
public constructor Bar</*0*/ T : @Ann kotlin.Any>()
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 abstract class Foo {
public constructor Foo()
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 open class TypeToken</*0*/ T> {
public constructor TypeToken</*0*/ T>()
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
}
@@ -1819,6 +1819,40 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
}
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/annotations/typeUse")
@TestDataPath("$PROJECT_ROOT")
public class TypeUse {
@Test
public void testAllFilesPresentInTypeUse() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/annotations/typeUse"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@Test
@TestMetadata("kt19455.kt")
public void testKt19455() throws Exception {
runTest("compiler/testData/diagnostics/tests/annotations/typeUse/kt19455.kt");
}
@Test
@TestMetadata("kt25876.kt")
public void testKt25876() throws Exception {
runTest("compiler/testData/diagnostics/tests/annotations/typeUse/kt25876.kt");
}
@Test
@TestMetadata("kt8325.kt")
public void testKt8325() throws Exception {
runTest("compiler/testData/diagnostics/tests/annotations/typeUse/kt8325.kt");
}
@Test
@TestMetadata("nonTypeUseAnnotationOnType.kt")
public void testNonTypeUseAnnotationOnType() throws Exception {
runTest("compiler/testData/diagnostics/tests/annotations/typeUse/nonTypeUseAnnotationOnType.kt");
}
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/annotations/withUseSiteTarget")
@TestDataPath("$PROJECT_ROOT")
@@ -168,6 +168,7 @@ enum class LanguageFeature(
ForbidUsingExtensionPropertyTypeParameterInDelegate(KOTLIN_1_6, kind = BUG_FIX),
ProhibitSelfCallsInNestedObjects(KOTLIN_1_6, kind = BUG_FIX),
ApproximateIntegerLiteralTypesInReceiverPosition(KOTLIN_1_6),
ProperCheckAnnotationsTargetInTypeUsePositions(KOTLIN_1_6, kind = BUG_FIX),
// Temporarily disabled, see KT-27084/KT-22379
SoundSmartcastFromLoopConditionForLoopAssignedVariables(sinceVersion = null, kind = BUG_FIX),