[FIR JS] Implement FirJsNativeAnnotationChecker

This commit is contained in:
Nikolay Lunyak
2023-01-04 16:47:50 +02:00
committed by Space Team
parent a20e29e8b7
commit 5b3a73f7cd
56 changed files with 330 additions and 1044 deletions
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticL
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.PositioningStrategy
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.psi.*
@Suppress("UNUSED_VARIABLE", "LocalVariableName", "ClassName", "unused")
@@ -23,6 +24,21 @@ object JS_DIAGNOSTICS_LIST : DiagnosticList("FirJsErrors") {
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 NATIVE_ANNOTATIONS_ALLOWED_ONLY_ON_MEMBER_OR_EXTENSION_FUN by error<KtElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) {
parameter<ConeKotlinType>("type")
}
val NATIVE_INDEXER_KEY_SHOULD_BE_STRING_OR_NUMBER by error<KtElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) {
parameter<String>("kind")
}
val NATIVE_INDEXER_WRONG_PARAMETER_COUNT by error<KtElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) {
parameter<Int>("parametersCount")
parameter<String>("kind")
}
val NATIVE_INDEXER_CAN_NOT_HAVE_DEFAULT_ARGUMENTS by error<KtElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) {
parameter<String>("kind")
}
val NATIVE_GETTER_RETURN_TYPE_SHOULD_BE_NULLABLE by error<KtDeclaration>(PositioningStrategy.DECLARATION_RETURN_TYPE)
val NATIVE_SETTER_WRONG_RETURN_TYPE by error<KtDeclaration>(PositioningStrategy.DECLARATION_RETURN_TYPE)
}
val SUPERTYPES by object : DiagnosticGroup("Supertypes") {
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.psi.KtAnonymousInitializer
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration
@@ -32,6 +33,12 @@ object FirJsErrors {
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)
val NATIVE_ANNOTATIONS_ALLOWED_ONLY_ON_MEMBER_OR_EXTENSION_FUN by error1<KtElement, ConeKotlinType>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val NATIVE_INDEXER_KEY_SHOULD_BE_STRING_OR_NUMBER by error1<KtElement, String>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val NATIVE_INDEXER_WRONG_PARAMETER_COUNT by error2<KtElement, Int, String>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val NATIVE_INDEXER_CAN_NOT_HAVE_DEFAULT_ARGUMENTS by error1<KtElement, String>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val NATIVE_GETTER_RETURN_TYPE_SHOULD_BE_NULLABLE by error0<KtDeclaration>(SourceElementPositioningStrategies.DECLARATION_RETURN_TYPE)
val NATIVE_SETTER_WRONG_RETURN_TYPE by error0<KtDeclaration>(SourceElementPositioningStrategies.DECLARATION_RETURN_TYPE)
// Supertypes
val WRONG_MULTIPLE_INHERITANCE by error1<KtElement, FirCallableSymbol<*>>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.analysis.diagnostics.js
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap
import org.jetbrains.kotlin.diagnostics.KtDiagnosticRenderers
import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory
import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers
@@ -26,6 +27,12 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.INLINE_EXTER
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.JS_MODULE_PROHIBITED_ON_NON_NATIVE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.JS_MODULE_PROHIBITED_ON_VAR
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NESTED_JS_MODULE_PROHIBITED
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NATIVE_ANNOTATIONS_ALLOWED_ONLY_ON_MEMBER_OR_EXTENSION_FUN
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NATIVE_GETTER_RETURN_TYPE_SHOULD_BE_NULLABLE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NATIVE_INDEXER_CAN_NOT_HAVE_DEFAULT_ARGUMENTS
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NATIVE_INDEXER_KEY_SHOULD_BE_STRING_OR_NUMBER
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NATIVE_INDEXER_WRONG_PARAMETER_COUNT
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NATIVE_SETTER_WRONG_RETURN_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NESTED_CLASS_IN_EXTERNAL_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NESTED_EXTERNAL_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NON_ABSTRACT_MEMBER_OF_EXTERNAL_INTERFACE
@@ -101,6 +108,27 @@ object FirJsErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
NON_ABSTRACT_MEMBER_OF_EXTERNAL_INTERFACE,
"Only nullable properties of external interfaces are allowed to be non-abstract"
)
map.put(
NATIVE_ANNOTATIONS_ALLOWED_ONLY_ON_MEMBER_OR_EXTENSION_FUN,
"Annotation ''{0}'' is allowed only on member functions of declaration annotated as ''kotlin.js.native'' or on toplevel extension functions",
FirDiagnosticRenderers.RENDER_TYPE
)
map.put(
NATIVE_INDEXER_KEY_SHOULD_BE_STRING_OR_NUMBER,
"Native {0}''s first parameter type should be ''kotlin.String'' or subtype of ''kotlin.Number''",
CommonRenderers.STRING
)
map.put(NATIVE_INDEXER_CAN_NOT_HAVE_DEFAULT_ARGUMENTS, "Native {0}''s parameter can not have default value", CommonRenderers.STRING)
map.put(NATIVE_GETTER_RETURN_TYPE_SHOULD_BE_NULLABLE, "Native getter's return type should be nullable")
map.put(
NATIVE_SETTER_WRONG_RETURN_TYPE,
"Native setter's return type should be 'Unit' or a supertype of the second parameter's type"
)
map.put(
NATIVE_INDEXER_WRONG_PARAMETER_COUNT, "Expected {0} parameters for native {1}",
KtDiagnosticRenderers.TO_STRING,
CommonRenderers.STRING
)
map.checkMissingMessages(FirJsErrors)
}
@@ -33,4 +33,11 @@ object JsDeclarationCheckers : DeclarationCheckers() {
FirJsDynamicDeclarationChecker,
FirJsInheritanceClassChecker,
)
override val simpleFunctionCheckers: Set<FirSimpleFunctionChecker>
get() = setOf(
FirJsNativeInvokeChecker,
FirJsNativeGetterChecker,
FirJsNativeSetterChecker,
)
}
@@ -0,0 +1,132 @@
/*
* 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.descriptors.Visibilities
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.FirSimpleFunctionChecker
import org.jetbrains.kotlin.fir.analysis.js.checkers.isNativeObject
import org.jetbrains.kotlin.fir.analysis.checkers.isTopLevel
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.declarations.hasAnnotation
import org.jetbrains.kotlin.fir.declarations.utils.isExtension
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.expressions.classId
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.JsStandardClassIds
internal abstract class FirJsAbstractNativeAnnotationChecker(private val requiredAnnotation: ClassId) : FirSimpleFunctionChecker() {
protected fun FirFunction.hasRequiredAnnotation(context: CheckerContext) = hasAnnotation(requiredAnnotation, context.session)
override fun check(declaration: FirSimpleFunction, context: CheckerContext, reporter: DiagnosticReporter) {
val annotation = declaration.annotations.find { it.classId == requiredAnnotation } ?: return
val isMember = !context.isTopLevel && declaration.visibility != Visibilities.Local
val isExtension = declaration.isExtension
if (isMember && (isExtension || !declaration.symbol.isNativeObject(context)) || !isMember && !isExtension) {
reporter.reportOn(
declaration.source,
FirJsErrors.NATIVE_ANNOTATIONS_ALLOWED_ONLY_ON_MEMBER_OR_EXTENSION_FUN,
annotation.typeRef.coneType,
context
)
}
}
}
internal object FirJsNativeInvokeChecker : FirJsAbstractNativeAnnotationChecker(JsStandardClassIds.Annotations.JsNativeInvoke)
internal abstract class FirJsAbstractNativeIndexerChecker(
requiredAnnotation: ClassId,
private val indexerKind: String,
private val requiredParametersCount: Int,
) : FirJsAbstractNativeAnnotationChecker(requiredAnnotation) {
override fun check(declaration: FirSimpleFunction, context: CheckerContext, reporter: DiagnosticReporter) {
super.check(declaration, context, reporter)
val parameters = declaration.valueParameters
val builtIns = context.session.builtinTypes
if (parameters.isNotEmpty()) {
val firstParameterDeclaration = parameters.first()
val firstParameter = firstParameterDeclaration.returnTypeRef.coneType
if (
firstParameter !is ConeErrorType &&
!firstParameter.isString &&
!firstParameter.isSubtypeOf(builtIns.numberType.coneType, context.session)
) {
reporter.reportOn(
firstParameterDeclaration.source,
FirJsErrors.NATIVE_INDEXER_KEY_SHOULD_BE_STRING_OR_NUMBER,
indexerKind,
context
)
}
}
if (parameters.size != requiredParametersCount) {
reporter.reportOn(
declaration.source,
FirJsErrors.NATIVE_INDEXER_WRONG_PARAMETER_COUNT,
requiredParametersCount,
indexerKind,
context
)
}
for (parameter in parameters) {
if (parameter.defaultValue != null) {
reporter.reportOn(
parameter.source,
FirJsErrors.NATIVE_INDEXER_CAN_NOT_HAVE_DEFAULT_ARGUMENTS,
indexerKind,
context
)
}
}
}
}
internal object FirJsNativeGetterChecker : FirJsAbstractNativeIndexerChecker(JsStandardClassIds.Annotations.JsNativeGetter, "getter", 1) {
override fun check(declaration: FirSimpleFunction, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.hasRequiredAnnotation(context)) return
super.check(declaration, context, reporter)
if (!declaration.returnTypeRef.coneType.isNullable) {
reporter.reportOn(declaration.source, FirJsErrors.NATIVE_GETTER_RETURN_TYPE_SHOULD_BE_NULLABLE, context)
}
}
}
internal object FirJsNativeSetterChecker : FirJsAbstractNativeIndexerChecker(JsStandardClassIds.Annotations.JsNativeSetter, "setter", 2) {
override fun check(declaration: FirSimpleFunction, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.hasRequiredAnnotation(context)) return
super.check(declaration, context, reporter)
val returnType = declaration.returnTypeRef.coneType
if (returnType.isUnit) {
return
}
if (declaration.valueParameters.size < 2) {
return
}
val secondParameterType = declaration.valueParameters[1].returnTypeRef.coneType
if (secondParameterType.isSubtypeOf(returnType, context.session)) {
return
}
reporter.reportOn(declaration.source, FirJsErrors.NATIVE_SETTER_WRONG_RETURN_TYPE, context)
}
}
@@ -59,6 +59,7 @@ class BuiltinTypes {
val enumType: FirImplicitBuiltinTypeRef = FirImplicitEnumTypeRef(null)
val annotationType: FirImplicitBuiltinTypeRef = FirImplicitAnnotationTypeRef(null)
val booleanType: FirImplicitBuiltinTypeRef = FirImplicitBooleanTypeRef(null)
val numberType: FirImplicitBuiltinTypeRef = FirImplicitNumberTypeRef(null)
val byteType: FirImplicitBuiltinTypeRef = FirImplicitByteTypeRef(null)
val shortType: FirImplicitBuiltinTypeRef = FirImplicitShortTypeRef(null)
val intType: FirImplicitBuiltinTypeRef = FirImplicitIntTypeRef(null)
@@ -76,6 +76,10 @@ class FirImplicitBooleanTypeRef(
source: KtSourceElement?
) : FirImplicitBuiltinTypeRef(source, StandardClassIds.Boolean)
class FirImplicitNumberTypeRef(
source: KtSourceElement?
) : FirImplicitBuiltinTypeRef(source, StandardClassIds.Number)
class FirImplicitByteTypeRef(
source: KtSourceElement?
) : FirImplicitBuiltinTypeRef(source, StandardClassIds.Byte)
@@ -194,6 +198,7 @@ fun FirImplicitBuiltinTypeRef.withNewSource(newSource: KtSourceElement?): FirImp
is FirImplicitAnnotationTypeRef -> FirImplicitAnnotationTypeRef(newSource)
is FirImplicitBooleanTypeRef -> FirImplicitBooleanTypeRef(newSource)
is FirImplicitByteTypeRef -> FirImplicitByteTypeRef(newSource)
is FirImplicitNumberTypeRef -> FirImplicitNumberTypeRef(newSource)
is FirImplicitShortTypeRef -> FirImplicitShortTypeRef(newSource)
is FirImplicitIntTypeRef -> FirImplicitIntTypeRef(newSource)
is FirImplicitLongTypeRef -> FirImplicitLongTypeRef(newSource)