[FIR JS] Implement FirJsRuntimeAnnotationChecker
This commit is contained in:
committed by
Space Team
parent
56285bb2d6
commit
7b8f5f9980
+12
@@ -1007,6 +1007,18 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirJsErrors.RUNTIME_ANNOTATION_NOT_SUPPORTED) { firDiagnostic ->
|
||||
RuntimeAnnotationNotSupportedImpl(
|
||||
firDiagnostic as KtPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirJsErrors.RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION) { firDiagnostic ->
|
||||
RuntimeAnnotationOnExternalDeclarationImpl(
|
||||
firDiagnostic as KtPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.OPT_IN_USAGE) { firDiagnostic ->
|
||||
OptInUsageImpl(
|
||||
firDiagnostic.a,
|
||||
|
||||
+8
@@ -724,6 +724,14 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
override val diagnosticClass get() = NestedJsModuleProhibited::class
|
||||
}
|
||||
|
||||
abstract class RuntimeAnnotationNotSupported : KtFirDiagnostic<PsiElement>() {
|
||||
override val diagnosticClass get() = RuntimeAnnotationNotSupported::class
|
||||
}
|
||||
|
||||
abstract class RuntimeAnnotationOnExternalDeclaration : KtFirDiagnostic<PsiElement>() {
|
||||
override val diagnosticClass get() = RuntimeAnnotationOnExternalDeclaration::class
|
||||
}
|
||||
|
||||
abstract class OptInUsage : KtFirDiagnostic<PsiElement>() {
|
||||
override val diagnosticClass get() = OptInUsage::class
|
||||
abstract val optInMarkerFqName: FqName
|
||||
|
||||
+10
@@ -870,6 +870,16 @@ internal class NestedJsModuleProhibitedImpl(
|
||||
override val token: KtLifetimeToken,
|
||||
) : KtFirDiagnostic.NestedJsModuleProhibited(), KtAbstractFirDiagnostic<KtElement>
|
||||
|
||||
internal class RuntimeAnnotationNotSupportedImpl(
|
||||
override val firDiagnostic: KtPsiDiagnostic,
|
||||
override val token: KtLifetimeToken,
|
||||
) : KtFirDiagnostic.RuntimeAnnotationNotSupported(), KtAbstractFirDiagnostic<PsiElement>
|
||||
|
||||
internal class RuntimeAnnotationOnExternalDeclarationImpl(
|
||||
override val firDiagnostic: KtPsiDiagnostic,
|
||||
override val token: KtLifetimeToken,
|
||||
) : KtFirDiagnostic.RuntimeAnnotationOnExternalDeclaration(), KtAbstractFirDiagnostic<PsiElement>
|
||||
|
||||
internal class OptInUsageImpl(
|
||||
override val optInMarkerFqName: FqName,
|
||||
override val message: String,
|
||||
|
||||
+2
@@ -22,6 +22,8 @@ object JS_DIAGNOSTICS_LIST : DiagnosticList("FirJsErrors") {
|
||||
val JS_MODULE_PROHIBITED_ON_VAR by error<KtElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT)
|
||||
val JS_MODULE_PROHIBITED_ON_NON_NATIVE by error<KtElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT)
|
||||
val NESTED_JS_MODULE_PROHIBITED by error<KtElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT)
|
||||
val RUNTIME_ANNOTATION_NOT_SUPPORTED by warning<PsiElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT)
|
||||
val RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION by error<PsiElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT)
|
||||
}
|
||||
|
||||
val SUPERTYPES by object : DiagnosticGroup("Supertypes") {
|
||||
|
||||
+2
@@ -26,6 +26,8 @@ object FirJsErrors {
|
||||
val JS_MODULE_PROHIBITED_ON_VAR by error0<KtElement>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
|
||||
val JS_MODULE_PROHIBITED_ON_NON_NATIVE by error0<KtElement>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
|
||||
val NESTED_JS_MODULE_PROHIBITED by error0<KtElement>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
|
||||
val RUNTIME_ANNOTATION_NOT_SUPPORTED by warning0<PsiElement>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
|
||||
val RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION by error0<PsiElement>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
|
||||
|
||||
// Supertypes
|
||||
val WRONG_MULTIPLE_INHERITANCE by error1<KtElement, FirCallableSymbol<*>>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
|
||||
|
||||
+7
@@ -17,6 +17,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.JS_MODULE_PR
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NESTED_JS_MODULE_PROHIBITED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS_WITH_FAKE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.RUNTIME_ANNOTATION_NOT_SUPPORTED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_JS_QUALIFIER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_MULTIPLE_INHERITANCE
|
||||
|
||||
@@ -44,6 +46,11 @@ object FirJsErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
|
||||
FirDiagnosticRenderers.SYMBOL
|
||||
)
|
||||
map.put(CALL_TO_DEFINED_EXTERNALLY_FROM_NON_EXTERNAL_DECLARATION, "This property can only be used from external declarations")
|
||||
map.put(RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION, "Runtime annotation can't be put on external declaration")
|
||||
map.put(
|
||||
RUNTIME_ANNOTATION_NOT_SUPPORTED,
|
||||
"Reflection is not supported in JavaScript target, therefore you won't be able to read this annotation in run-time"
|
||||
)
|
||||
|
||||
map.checkMissingMessages(FirJsErrors)
|
||||
}
|
||||
|
||||
+1
@@ -23,6 +23,7 @@ object JsDeclarationCheckers : DeclarationCheckers() {
|
||||
override val basicDeclarationCheckers: Set<FirBasicDeclarationChecker>
|
||||
get() = setOf(
|
||||
FirJsModuleChecker,
|
||||
FirJsRuntimeAnnotationChecker,
|
||||
)
|
||||
|
||||
override val classCheckers: Set<FirClassChecker>
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.js.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.getExplicitAnnotationRetention
|
||||
import org.jetbrains.kotlin.fir.analysis.js.checkers.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
|
||||
import org.jetbrains.kotlin.fir.expressions.coneClassLikeType
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
|
||||
object FirJsRuntimeAnnotationChecker : FirBasicDeclarationChecker() {
|
||||
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
for (annotation in declaration.annotations) {
|
||||
val annotationClass = annotation.coneClassLikeType?.lookupTag?.toSymbol(context.session) ?: continue
|
||||
if (annotationClass.getExplicitAnnotationRetention(context.session) != AnnotationRetention.RUNTIME) continue
|
||||
|
||||
if (declaration is FirMemberDeclaration && declaration.symbol.isEffectivelyExternal(context)) {
|
||||
reporter.reportOn(annotation.source, FirJsErrors.RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION, context)
|
||||
} else {
|
||||
reporter.reportOn(annotation.source, FirJsErrors.RUNTIME_ANNOTATION_NOT_SUPPORTED, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
-10
@@ -10,19 +10,18 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
|
||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.FirAnnotationContainer
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.findClosest
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirPrimaryConstructor
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.references.resolved
|
||||
import org.jetbrains.kotlin.fir.references.toResolvedEnumEntrySymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
@@ -36,12 +35,15 @@ fun FirRegularClass.getRetention(session: FirSession): AnnotationRetention {
|
||||
return getRetentionAnnotation(session)?.getRetention() ?: AnnotationRetention.RUNTIME
|
||||
}
|
||||
|
||||
fun FirAnnotation.getRetention(): AnnotationRetention {
|
||||
val retentionArgument = findArgumentByName(ParameterNames.retentionValue) as? FirQualifiedAccessExpression
|
||||
?: return AnnotationRetention.RUNTIME
|
||||
val retentionName = retentionArgument.calleeReference.resolved?.name?.asString()
|
||||
?: return AnnotationRetention.RUNTIME
|
||||
return AnnotationRetention.values().firstOrNull { it.name == retentionName } ?: AnnotationRetention.RUNTIME
|
||||
private fun FirAnnotation.getRetention(): AnnotationRetention? {
|
||||
val propertyAccess = findArgumentByName(ParameterNames.retentionValue) as? FirQualifiedAccessExpression
|
||||
val callableId = propertyAccess?.calleeReference?.toResolvedEnumEntrySymbol()?.callableId ?: return null
|
||||
|
||||
if (callableId.classId != StandardClassIds.AnnotationRetention) {
|
||||
return null
|
||||
}
|
||||
|
||||
return AnnotationRetention.values().firstOrNull { it.name == callableId.callableName.asString() }
|
||||
}
|
||||
|
||||
private val defaultAnnotationTargets = KotlinTarget.DEFAULT_TARGET_SET
|
||||
@@ -141,8 +143,12 @@ fun FirClassLikeSymbol<*>.containsRepeatableAnnotation(session: FirSession): Boo
|
||||
return false
|
||||
}
|
||||
|
||||
fun FirClassLikeSymbol<*>.getExplicitAnnotationRetention(session: FirSession): AnnotationRetention? {
|
||||
return getAnnotationByClassId(StandardClassIds.Annotations.Retention, session)?.getRetention()
|
||||
}
|
||||
|
||||
fun FirClassLikeSymbol<*>.getAnnotationRetention(session: FirSession): AnnotationRetention {
|
||||
return getAnnotationByClassId(StandardClassIds.Annotations.Retention, session)?.getRetention() ?: AnnotationRetention.RUNTIME
|
||||
return getExplicitAnnotationRetention(session) ?: AnnotationRetention.RUNTIME
|
||||
}
|
||||
|
||||
fun FirAnnotationContainer.getDefaultUseSiteTarget(
|
||||
|
||||
@@ -56,6 +56,10 @@ fun FirReference.toResolvedConstructorSymbol(discardErrorReference: Boolean = fa
|
||||
return this.toResolvedSymbol<FirConstructorSymbol>(discardErrorReference)
|
||||
}
|
||||
|
||||
fun FirReference.toResolvedEnumEntrySymbol(discardErrorReference: Boolean = false): FirEnumEntrySymbol? {
|
||||
return this.toResolvedSymbol<FirEnumEntrySymbol>(discardErrorReference)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
fun FirReference.isError(): Boolean {
|
||||
contract {
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class X
|
||||
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class Y
|
||||
|
||||
@X
|
||||
external class A {
|
||||
@X
|
||||
fun f()
|
||||
|
||||
@X
|
||||
val p: Int
|
||||
|
||||
@get:X
|
||||
val r: Int
|
||||
}
|
||||
|
||||
@Y
|
||||
external class B {
|
||||
@Y
|
||||
fun f()
|
||||
|
||||
@Y
|
||||
val p: Int
|
||||
|
||||
@get:Y
|
||||
val r: Int
|
||||
}
|
||||
|
||||
@X
|
||||
class C {
|
||||
@X
|
||||
fun f() {}
|
||||
|
||||
@X
|
||||
val p: Int = 0
|
||||
|
||||
val q: Int
|
||||
@X get() = 0
|
||||
|
||||
@get:X
|
||||
val r: Int = 0
|
||||
}
|
||||
|
||||
@Y
|
||||
class D {
|
||||
@Y
|
||||
fun f() {}
|
||||
|
||||
@Y
|
||||
val p: Int = 0
|
||||
|
||||
val q: Int
|
||||
@Y get() = 0
|
||||
|
||||
@get:Y
|
||||
val r: Int = 0
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
// FIR_IDENTICAL
|
||||
// !DIAGNOSTICS: +RUNTIME_ANNOTATION_NOT_SUPPORTED
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class X
|
||||
|
||||
|
||||
Reference in New Issue
Block a user