[FIR] Support several annotation class diagnostics
This commit is contained in:
committed by
Mikhail Glukhikh
parent
c112d37ac1
commit
80cd26c9df
+1
-1
@@ -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
|
||||
)
|
||||
|
||||
+91
-1
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user