diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsExpressionCheckers.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsExpressionCheckers.kt index 623f8f59135..a26b3413619 100644 --- a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsExpressionCheckers.kt +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsExpressionCheckers.kt @@ -20,6 +20,7 @@ object JsExpressionCheckers : ExpressionCheckers() { get() = setOf( FirJsDefinedExternallyCallChecker, FirJsNativeRttiChecker, + FirJsReflectionAPICallChecker ) override val functionCallCheckers: Set diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/expression/FirJsReflectionAPICallChecker.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/expression/FirJsReflectionAPICallChecker.kt new file mode 100644 index 00000000000..61aa9d28a49 --- /dev/null +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/expression/FirJsReflectionAPICallChecker.kt @@ -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.expression + +import org.jetbrains.kotlin.KtSourceElement +import org.jetbrains.kotlin.builtins.StandardNames +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.expression.AbstractFirReflectionApiCallChecker +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.StandardClassIds + +object FirJsReflectionAPICallChecker : AbstractFirReflectionApiCallChecker() { + override fun isWholeReflectionApiAvailable(context: CheckerContext): Boolean { + return false + } + + override fun isAllowedReflectionApi(name: Name, containingClassId: ClassId, context: CheckerContext): Boolean { + return super.isAllowedReflectionApi(name, containingClassId, context) || + containingClassId in StandardClassIds.associatedObjectAnnotations || + name == StandardNames.FqNames.findAssociatedObject.shortName() + } + + override fun report(source: KtSourceElement?, context: CheckerContext, reporter: DiagnosticReporter) { + reporter.reportOn(source, FirErrors.UNSUPPORTED, "This reflection API is not supported yet in JavaScript", context) + } +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/AbstractFirReflectionApiCallChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/AbstractFirReflectionApiCallChecker.kt index 4e0cc60dd8d..dc03a375a97 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/AbstractFirReflectionApiCallChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/AbstractFirReflectionApiCallChecker.kt @@ -51,7 +51,7 @@ abstract class AbstractFirReflectionApiCallChecker : FirBasicExpressionChecker() } } - private fun isAllowedReflectionApi(name: Name, containingClassId: ClassId, context: CheckerContext): Boolean = + protected open fun isAllowedReflectionApi(name: Name, containingClassId: ClassId, context: CheckerContext): Boolean = name in ALLOWED_MEMBER_NAMES || containingClassId == K_CLASS && isAllowedKClassMember(name, context) || (name.asString() == "get" || name.asString() == "set") && containingClassId in K_PROPERTY_CLASSES || diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/reflection/reflectionApi.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/reflection/reflectionApi.fir.kt deleted file mode 100644 index 9f83736cd35..00000000000 --- a/compiler/testData/diagnostics/testsWithJsStdLib/reflection/reflectionApi.fir.kt +++ /dev/null @@ -1,50 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_EXPRESSION -import kotlin.reflect.* - -class Foo(val prop: Any) { - fun func() {} -} - -fun testSomeValidCases(p0: KProperty0, pm0: KMutableProperty0, f: KFunction, p1: KProperty1, p2: KProperty2) { - Foo::prop - Foo::func - Foo::class - p0.get() - p0.name - pm0.set("") - f.name - p1.get("") - p2.get("", "") - (Foo::func).invoke(Foo("")) - (Foo::func)(Foo("")) - - p0 == pm0 - p1.equals(p2) - p0.hashCode() - f.toString() -} - -fun kclass(k: KClass<*>, kt: KClass) { - k.simpleName - k.qualifiedName - k.members - k.constructors - k.nestedClasses - k.objectInstance - k.typeParameters - k.supertypes - k.visibility - k.isFinal - k.isOpen - k.isAbstract - k.isSealed - k.isData - k.isInner - k.isCompanion - - k.annotations - - k == kt - k.hashCode() - k.toString() -} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/reflection/reflectionApi.kt b/compiler/testData/diagnostics/testsWithJsStdLib/reflection/reflectionApi.kt index 5174cc73fe2..1d27761e774 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/reflection/reflectionApi.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/reflection/reflectionApi.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_EXPRESSION import kotlin.reflect.* diff --git a/core/compiler.common/src/org/jetbrains/kotlin/builtins/StandardNames.kt b/core/compiler.common/src/org/jetbrains/kotlin/builtins/StandardNames.kt index f17e5fc9a85..11cadc7bb76 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/builtins/StandardNames.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/builtins/StandardNames.kt @@ -182,6 +182,7 @@ object StandardNames { @JvmField val kMutablePropertyFqName: FqNameUnsafe = reflect("KMutableProperty") @JvmField val kProperty: ClassId = ClassId.topLevel(kPropertyFqName.toSafe()) @JvmField val kDeclarationContainer: FqNameUnsafe = reflect("KDeclarationContainer") + @JvmField val findAssociatedObject: FqNameUnsafe = reflect("findAssociatedObject") @JvmField val uByteFqName: FqName = fqName("UByte") @JvmField val uShortFqName: FqName = fqName("UShort") diff --git a/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt b/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt index 6827ba572f8..d1b1b104365 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt @@ -93,6 +93,8 @@ object StandardClassIds { val constantAllowedTypes = primitiveTypes + unsignedTypes + String + val associatedObjectAnnotations = hashSetOf(Annotations.AssociatedObjectKey, Annotations.ExperimentalAssociatedObjects) + val Continuation = "Continuation".coroutinesId() @Suppress("FunctionName") @@ -190,6 +192,9 @@ object StandardClassIds { val OptionalExpectation = "OptionalExpectation".baseId() val ImplicitlyActualizedByJvmDeclaration = "ImplicitlyActualizedByJvmDeclaration".jvmId() + val AssociatedObjectKey = "AssociatedObjectKey".reflectId() + val ExperimentalAssociatedObjects = "ExperimentalAssociatedObjects".reflectId() + object ParameterNames { val value = Name.identifier("value") diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsReflectionAPICallChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsReflectionAPICallChecker.kt index 84a63bc9027..4b80d603702 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsReflectionAPICallChecker.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsReflectionAPICallChecker.kt @@ -18,20 +18,16 @@ package org.jetbrains.kotlin.js.resolve.diagnostics import com.intellij.psi.PsiElement import org.jetbrains.kotlin.builtins.ReflectionTypes +import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.diagnostics.Errors.UNSUPPORTED -import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.resolve.calls.checkers.AbstractReflectionApiCallChecker import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.storage.StorageManager -private val ADDITIONAL_ALLOWED_CLASSES = setOf( - FqName("kotlin.reflect.AssociatedObjectKey"), - FqName("kotlin.reflect.ExperimentalAssociatedObjects") -) - class JsReflectionAPICallChecker( reflectionTypes: ReflectionTypes, storageManager: StorageManager @@ -45,8 +41,8 @@ class JsReflectionAPICallChecker( context: CallCheckerContext ): Boolean { return super.isAllowedReflectionApi(descriptor, containingClass, context) || - containingClass.fqNameSafe in ADDITIONAL_ALLOWED_CLASSES || - descriptor.name.asString() == "findAssociatedObject" + containingClass.classId in StandardClassIds.associatedObjectAnnotations || + descriptor.name == StandardNames.FqNames.findAssociatedObject.shortName() } override fun report(element: PsiElement, context: CallCheckerContext) {