提交 80cd26c9 编写于 作者: M Mikhail Likholetov 提交者: Mikhail Glukhikh

[FIR] Support several annotation class diagnostics

上级 c112d37a
class Foo
enum class Enum {
Bar
}
annotation class Ann1(
val p1: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Array<Int><!>,
val p2: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Array<Int?><!>,
val p3: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Array<IntArray><!>,
val p4: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Array<Foo><!>,
val p5: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Foo<!>,
vararg val p6: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Class<*><!>
)
annotation class Ann2(
val p1: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>Int?<!>,
val p2: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>String?<!>,
val p3: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>IntArray?<!>,
val p4: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>Array<Int>?<!>,
val p5: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>Ann1?<!>,
val p6: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>Enum?<!>
)
\ No newline at end of file
FILE: typeOfAnnotationMember.kt
public final class Foo : R|kotlin/Any| {
public constructor(): R|Foo| {
super<R|kotlin/Any|>()
}
}
public final enum class Enum : R|kotlin/Enum<Enum>| {
private constructor(): R|Enum| {
super<R|kotlin/Enum<Enum>|>()
}
public final static enum entry Bar: R|Enum|
public final static fun values(): R|kotlin/Array<Enum>| {
}
public final static fun valueOf(value: R|kotlin/String|): R|Enum| {
}
}
public final annotation class Ann1 : R|kotlin/Annotation| {
public constructor(p1: R|kotlin/Array<kotlin/Int>|, p2: R|kotlin/Array<kotlin/Int?>|, p3: R|kotlin/Array<kotlin/IntArray>|, p4: R|kotlin/Array<Foo>|, p5: R|Foo|, vararg p6: R|kotlin/Array<out java/lang/Class<*>>|): R|Ann1| {
super<R|kotlin/Any|>()
}
public final val p1: R|kotlin/Array<kotlin/Int>| = R|<local>/p1|
public get(): R|kotlin/Array<kotlin/Int>|
public final val p2: R|kotlin/Array<kotlin/Int?>| = R|<local>/p2|
public get(): R|kotlin/Array<kotlin/Int?>|
public final val p3: R|kotlin/Array<kotlin/IntArray>| = R|<local>/p3|
public get(): R|kotlin/Array<kotlin/IntArray>|
public final val p4: R|kotlin/Array<Foo>| = R|<local>/p4|
public get(): R|kotlin/Array<Foo>|
public final val p5: R|Foo| = R|<local>/p5|
public get(): R|Foo|
public final val p6: R|kotlin/Array<out java/lang/Class<*>>| = R|<local>/p6|
public get(): R|kotlin/Array<out java/lang/Class<*>>|
}
public final annotation class Ann2 : R|kotlin/Annotation| {
public constructor(p1: R|kotlin/Int?|, p2: R|kotlin/String?|, p3: R|kotlin/IntArray?|, p4: R|kotlin/Array<kotlin/Int>?|, p5: R|Ann1?|, p6: R|Enum?|): R|Ann2| {
super<R|kotlin/Any|>()
}
public final val p1: R|kotlin/Int?| = R|<local>/p1|
public get(): R|kotlin/Int?|
public final val p2: R|kotlin/String?| = R|<local>/p2|
public get(): R|kotlin/String?|
public final val p3: R|kotlin/IntArray?| = R|<local>/p3|
public get(): R|kotlin/IntArray?|
public final val p4: R|kotlin/Array<kotlin/Int>?| = R|<local>/p4|
public get(): R|kotlin/Array<kotlin/Int>?|
public final val p5: R|Ann1?| = R|<local>/p5|
public get(): R|Ann1?|
public final val p6: R|Enum?| = R|<local>/p6|
public get(): R|Enum?|
}
......@@ -11,7 +11,7 @@ annotation class WithString(val s: String)
annotation class Complex(val wi: WithInt, val ws: WithString)
annotation class VeryComplex(val f: Float, val d: Double, val b: Boolean, val l: Long, val n: Int?)
annotation class VeryComplex(val f: Float, val d: Double, val b: Boolean, val l: Long, val n: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>Int?<!>)
// FILE: main.kt
......
......@@ -871,6 +871,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/superclassNotAccessibleFromInterface.kt");
}
@TestMetadata("typeOfAnnotationMember.kt")
public void testTypeOfAnnotationMember() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/typeOfAnnotationMember.kt");
}
@TestMetadata("valOnAnnotationParameter.kt")
public void testValOnAnnotationParameter() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/valOnAnnotationParameter.kt");
......
......@@ -871,6 +871,11 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/superclassNotAccessibleFromInterface.kt");
}
@TestMetadata("typeOfAnnotationMember.kt")
public void testTypeOfAnnotationMember() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/typeOfAnnotationMember.kt");
}
@TestMetadata("valOnAnnotationParameter.kt")
public void testValOnAnnotationParameter() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/valOnAnnotationParameter.kt");
......
......@@ -11,10 +11,10 @@ import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
object DeclarationCheckers {
val DECLARATIONS: List<FirDeclarationChecker<FirDeclaration>> = listOf(
FirAnnotationClassDeclarationChecker,
FirModifierChecker
)
val MEMBER_DECLARATIONS: List<FirDeclarationChecker<FirMemberDeclaration>> = DECLARATIONS + listOf(
FirAnnotationClassDeclarationChecker,
FirInfixFunctionDeclarationChecker,
FirExposedVisibilityChecker
)
......
......@@ -9,6 +9,7 @@ import com.intellij.lang.LighterASTNode
import com.intellij.openapi.util.Ref
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.ClassKind.ANNOTATION_CLASS
import org.jetbrains.kotlin.descriptors.ClassKind.ENUM_CLASS
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticFactory0
......@@ -16,11 +17,19 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.FirLightSourceElement
import org.jetbrains.kotlin.fir.FirPsiSourceElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
import org.jetbrains.kotlin.fir.symbols.StandardClassIds.primitiveArrayTypeByElementType
import org.jetbrains.kotlin.fir.symbols.StandardClassIds.primitiveTypes
import org.jetbrains.kotlin.fir.symbols.StandardClassIds.unsignedTypes
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.KtNodeTypes.FUN
import org.jetbrains.kotlin.KtNodeTypes.VALUE_PARAMETER
import org.jetbrains.kotlin.lexer.KtTokens.VAL_KEYWORD
import org.jetbrains.kotlin.lexer.KtTokens.VAR_KEYWORD
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.psi.KtParameter
object FirAnnotationClassDeclarationChecker : FirDeclarationChecker<FirDeclaration>() {
......@@ -28,13 +37,15 @@ object FirAnnotationClassDeclarationChecker : FirDeclarationChecker<FirDeclarati
if (declaration !is FirRegularClass) return
if (declaration.classKind != ANNOTATION_CLASS) return
if (declaration.isLocal) reporter.report(declaration.source, FirErrors.LOCAL_ANNOTATION_CLASS_ERROR)
for (it in declaration.declarations) {
when {
it is FirConstructor && it.isPrimary -> {
for (parameter in it.valueParameters)
for (parameter in it.valueParameters) {
when (val parameterSourceElement = parameter.source) {
is FirPsiSourceElement<*> -> {
val parameterPsiElement = parameterSourceElement.psi as KtParameter
if (!parameterPsiElement.hasValOrVar())
reporter.report(parameterSourceElement, FirErrors.MISSING_VAL_ON_ANNOTATION_PARAMETER)
else if (parameterPsiElement.isMutable)
......@@ -43,12 +54,52 @@ object FirAnnotationClassDeclarationChecker : FirDeclarationChecker<FirDeclarati
is FirLightSourceElement -> {
val kidsRef = Ref<Array<LighterASTNode?>>()
parameterSourceElement.tree.getChildren(parameterSourceElement.element, kidsRef)
if (kidsRef.get().any { it?.tokenType == VAR_KEYWORD })
reporter.report(parameterSourceElement, FirErrors.VAR_ANNOTATION_PARAMETER)
else if (kidsRef.get().all { it?.tokenType != VAL_KEYWORD })
reporter.report(parameterSourceElement, FirErrors.MISSING_VAL_ON_ANNOTATION_PARAMETER)
}
}
val typeRef = parameter.returnTypeRef
val coneType = typeRef.coneTypeSafe<ConeLookupTagBasedType>()
val classId = coneType?.classId
if (coneType != null) when {
classId == ClassId.fromString("<error>") -> {
// TODO: replace with UNRESOLVED_REFERENCE check
}
coneType.isNullable -> {
reporter.report(typeRef.source, FirErrors.NULLABLE_TYPE_OF_ANNOTATION_MEMBER)
}
classId in primitiveTypes -> {
// DO NOTHING: primitives are allowed as annotation class parameter
}
classId in unsignedTypes -> {
// TODO: replace with EXPERIMENTAL_UNSIGNED_LITERALS check
}
classId == StandardClassIds.KClass -> {
// DO NOTHING: KClass is allowed
}
classId == StandardClassIds.String -> {
// DO NOTHING: String is allowed
}
classId in primitiveArrayTypeByElementType.values -> {
// DO NOTHING: primitive arrays are allowed
}
classId == StandardClassIds.Array -> {
if (!isAllowedArray(typeRef, context.session))
reporter.report(typeRef.source, FirErrors.INVALID_TYPE_OF_ANNOTATION_MEMBER)
}
isAllowedClassKind(coneType, context.session) -> {
// DO NOTHING: annotation or enum classes are allowed
}
else -> {
reporter.report(typeRef.source, FirErrors.INVALID_TYPE_OF_ANNOTATION_MEMBER)
}
}
}
}
it is FirRegularClass -> {
// DO NOTHING: nested annotation classes are allowed in 1.3+
......@@ -65,7 +116,46 @@ object FirAnnotationClassDeclarationChecker : FirDeclarationChecker<FirDeclarati
}
}
}
}
private fun isAllowedClassKind(cone: ConeLookupTagBasedType, session: FirSession): Boolean {
val typeRefClassKind = (cone.lookupTag.toSymbol(session)
?.fir as? FirRegularClass)
?.classKind
?: return false
return typeRefClassKind == ANNOTATION_CLASS || typeRefClassKind == ENUM_CLASS
}
private fun isAllowedArray(typeRef: FirTypeRef, session: FirSession): Boolean {
val typeArguments = typeRef.coneTypeUnsafe<ConeKotlinType>().typeArguments
if (typeArguments.size != 1) return false
val arrayType = (typeArguments[0] as? ConeKotlinTypeProjection)
?.type
?: return false
if (arrayType.isNullable) return false
val arrayTypeClassId = arrayType.classId
when {
arrayTypeClassId == StandardClassIds.KClass -> {
// KClass is allowed
return true
}
arrayTypeClassId == StandardClassIds.String -> {
// String is allowed
return true
}
isAllowedClassKind(arrayType as ConeLookupTagBasedType, session) -> {
// annotation or enum classes are allowed
return true
}
}
return false
}
private inline fun <reified T : FirSourceElement, P : PsiElement> DiagnosticReporter.report(
......
......@@ -54,8 +54,11 @@ object FirErrors {
val NON_PRIVATE_CONSTRUCTOR_IN_SEALED by existing<FirSourceElement, PsiElement>(Errors.NON_PRIVATE_CONSTRUCTOR_IN_SEALED)
val ANNOTATION_CLASS_MEMBER by existing<FirSourceElement, PsiElement>(Errors.ANNOTATION_CLASS_MEMBER)
val ANNOTATION_PARAMETER_DEFAULT_VALUE_MUST_BE_CONSTANT by existing<FirSourceElement, KtExpression>(Errors.ANNOTATION_PARAMETER_DEFAULT_VALUE_MUST_BE_CONSTANT)
val LOCAL_ANNOTATION_CLASS_ERROR by existing<FirSourceElement, KtClassOrObject>(Errors.LOCAL_ANNOTATION_CLASS_ERROR)
val MISSING_VAL_ON_ANNOTATION_PARAMETER by existing<FirSourceElement, KtParameter>(Errors.MISSING_VAL_ON_ANNOTATION_PARAMETER)
val NULLABLE_TYPE_OF_ANNOTATION_MEMBER by existing<FirSourceElement, KtTypeReference>(Errors.NULLABLE_TYPE_OF_ANNOTATION_MEMBER)
val INVALID_TYPE_OF_ANNOTATION_MEMBER by existing<FirSourceElement, KtTypeReference>(Errors.INVALID_TYPE_OF_ANNOTATION_MEMBER)
val VAR_ANNOTATION_PARAMETER by existing<FirSourceElement, KtParameter>(Errors.VAR_ANNOTATION_PARAMETER)
// Exposed visibility group
......
......@@ -25,11 +25,11 @@ annotation class Ann4(val p1: IntArray,
annotation class Ann5(val p1: MyEnum)
annotation class Ann6(val p: Class<*>)
annotation class Ann6(val p: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Class<*><!>)
annotation class Ann7(val p: RetentionPolicy)
annotation class Ann8(val p1: Array<String>,
val p2: Array<Class<*>>,
val p2: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Array<Class<*>><!>,
val p3: Array<MyEnum>,
val p4: Array<Ann1>)
......@@ -39,32 +39,32 @@ annotation class Ann9(
// INCORRECT
annotation class InAnn1(val p1: Int?,
val p3: Short?,
val p4: Long?,
val p5: Double?,
val p6: Float?,
val p7: Char?,
val p8: Boolean?)
annotation class InAnn4(val p1: Array<Int>,
val p2: Array<Int>?)
annotation class InAnn6(val p: Class<*>?)
annotation class InAnn7(val p: RetentionPolicy?)
annotation class InAnn8(val p1: Array<Int>,
val p2: Array<Int?>,
val p3: Array<MyClass>,
val p4: Array<IntArray>)
annotation class InAnn9(val p: MyClass)
annotation class InAnn10(val p1: String?)
annotation class InAnn11(val p1: Ann1?)
annotation class InAnn12(val p1: MyEnum?)
annotation class InAnn1(val p1: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>Int?<!>,
val p3: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>Short?<!>,
val p4: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>Long?<!>,
val p5: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>Double?<!>,
val p6: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>Float?<!>,
val p7: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>Char?<!>,
val p8: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>Boolean?<!>)
annotation class InAnn4(val p1: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Array<Int><!>,
val p2: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>Array<Int>?<!>)
annotation class InAnn6(val p: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>Class<*>?<!>)
annotation class InAnn7(val p: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>RetentionPolicy?<!>)
annotation class InAnn8(val p1: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Array<Int><!>,
val p2: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Array<Int?><!>,
val p3: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Array<MyClass><!>,
val p4: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Array<IntArray><!>)
annotation class InAnn9(val p: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>MyClass<!>)
annotation class InAnn10(val p1: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>String?<!>)
annotation class InAnn11(val p1: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>Ann1?<!>)
annotation class InAnn12(val p1: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>MyEnum?<!>)
annotation class InAnn13(vararg val p1: String,
vararg val p2: Class<*>,
vararg val p2: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Class<*><!>,
vararg val p3: MyEnum,
vararg val p4: Ann1,
vararg val p5: Int)
......
......@@ -5,8 +5,8 @@ import kotlin.reflect.KClass
inline class MyInt(val x: Int)
inline class MyString(val x: String)
annotation class Ann1(val a: MyInt)
annotation class Ann2(val a: Array<MyString>)
annotation class Ann3(vararg val a: MyInt)
annotation class Ann1(val a: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>MyInt<!>)
annotation class Ann2(val a: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Array<MyString><!>)
annotation class Ann3(vararg val a: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>MyInt<!>)
annotation class Ann4(val a: KClass<MyInt>)
\ No newline at end of file
......@@ -18,4 +18,4 @@ class B(vararg val s: Foo) {
constructor(a: Int, vararg s: Foo) : this(*s)
}
annotation class Ann(vararg val f: Foo)
\ No newline at end of file
annotation class Ann(vararg val f: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Foo<!>)
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册