[JS FIR] Implement FirJsReflectionAPICallChecker diagnostic
^KT-60899 Fixed
This commit is contained in:
committed by
Space Team
parent
af7eb8b7af
commit
d5aaa29a7f
+1
@@ -20,6 +20,7 @@ object JsExpressionCheckers : ExpressionCheckers() {
|
|||||||
get() = setOf(
|
get() = setOf(
|
||||||
FirJsDefinedExternallyCallChecker,
|
FirJsDefinedExternallyCallChecker,
|
||||||
FirJsNativeRttiChecker,
|
FirJsNativeRttiChecker,
|
||||||
|
FirJsReflectionAPICallChecker
|
||||||
)
|
)
|
||||||
|
|
||||||
override val functionCallCheckers: Set<FirFunctionCallChecker>
|
override val functionCallCheckers: Set<FirFunctionCallChecker>
|
||||||
|
|||||||
+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.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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -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 ||
|
name in ALLOWED_MEMBER_NAMES ||
|
||||||
containingClassId == K_CLASS && isAllowedKClassMember(name, context) ||
|
containingClassId == K_CLASS && isAllowedKClassMember(name, context) ||
|
||||||
(name.asString() == "get" || name.asString() == "set") && containingClassId in K_PROPERTY_CLASSES ||
|
(name.asString() == "get" || name.asString() == "set") && containingClassId in K_PROPERTY_CLASSES ||
|
||||||
|
|||||||
-50
@@ -1,50 +0,0 @@
|
|||||||
// !DIAGNOSTICS: -UNUSED_EXPRESSION
|
|
||||||
import kotlin.reflect.*
|
|
||||||
|
|
||||||
class Foo(val prop: Any) {
|
|
||||||
fun func() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testSomeValidCases(p0: KProperty0<Int>, pm0: KMutableProperty0<String>, f: KFunction<String>, p1: KProperty1<String, Int>, p2: KProperty2<String, String, Int>) {
|
|
||||||
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 <T : Any> kclass(k: KClass<*>, kt: KClass<T>) {
|
|
||||||
k.simpleName
|
|
||||||
k.qualifiedName
|
|
||||||
k.<!UNRESOLVED_REFERENCE!>members<!>
|
|
||||||
k.<!UNRESOLVED_REFERENCE!>constructors<!>
|
|
||||||
k.<!UNRESOLVED_REFERENCE!>nestedClasses<!>
|
|
||||||
k.<!UNRESOLVED_REFERENCE!>objectInstance<!>
|
|
||||||
k.<!UNRESOLVED_REFERENCE!>typeParameters<!>
|
|
||||||
k.<!UNRESOLVED_REFERENCE!>supertypes<!>
|
|
||||||
k.<!UNRESOLVED_REFERENCE!>visibility<!>
|
|
||||||
k.<!UNRESOLVED_REFERENCE!>isFinal<!>
|
|
||||||
k.<!UNRESOLVED_REFERENCE!>isOpen<!>
|
|
||||||
k.<!UNRESOLVED_REFERENCE!>isAbstract<!>
|
|
||||||
k.<!UNRESOLVED_REFERENCE!>isSealed<!>
|
|
||||||
k.<!UNRESOLVED_REFERENCE!>isData<!>
|
|
||||||
k.<!UNRESOLVED_REFERENCE!>isInner<!>
|
|
||||||
k.<!UNRESOLVED_REFERENCE!>isCompanion<!>
|
|
||||||
|
|
||||||
k.<!UNRESOLVED_REFERENCE!>annotations<!>
|
|
||||||
|
|
||||||
k == kt
|
|
||||||
k.hashCode()
|
|
||||||
k.toString()
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
// !DIAGNOSTICS: -UNUSED_EXPRESSION
|
// !DIAGNOSTICS: -UNUSED_EXPRESSION
|
||||||
import kotlin.reflect.*
|
import kotlin.reflect.*
|
||||||
|
|
||||||
|
|||||||
@@ -182,6 +182,7 @@ object StandardNames {
|
|||||||
@JvmField val kMutablePropertyFqName: FqNameUnsafe = reflect("KMutableProperty")
|
@JvmField val kMutablePropertyFqName: FqNameUnsafe = reflect("KMutableProperty")
|
||||||
@JvmField val kProperty: ClassId = ClassId.topLevel(kPropertyFqName.toSafe())
|
@JvmField val kProperty: ClassId = ClassId.topLevel(kPropertyFqName.toSafe())
|
||||||
@JvmField val kDeclarationContainer: FqNameUnsafe = reflect("KDeclarationContainer")
|
@JvmField val kDeclarationContainer: FqNameUnsafe = reflect("KDeclarationContainer")
|
||||||
|
@JvmField val findAssociatedObject: FqNameUnsafe = reflect("findAssociatedObject")
|
||||||
|
|
||||||
@JvmField val uByteFqName: FqName = fqName("UByte")
|
@JvmField val uByteFqName: FqName = fqName("UByte")
|
||||||
@JvmField val uShortFqName: FqName = fqName("UShort")
|
@JvmField val uShortFqName: FqName = fqName("UShort")
|
||||||
|
|||||||
@@ -93,6 +93,8 @@ object StandardClassIds {
|
|||||||
|
|
||||||
val constantAllowedTypes = primitiveTypes + unsignedTypes + String
|
val constantAllowedTypes = primitiveTypes + unsignedTypes + String
|
||||||
|
|
||||||
|
val associatedObjectAnnotations = hashSetOf(Annotations.AssociatedObjectKey, Annotations.ExperimentalAssociatedObjects)
|
||||||
|
|
||||||
val Continuation = "Continuation".coroutinesId()
|
val Continuation = "Continuation".coroutinesId()
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
@Suppress("FunctionName")
|
||||||
@@ -190,6 +192,9 @@ object StandardClassIds {
|
|||||||
val OptionalExpectation = "OptionalExpectation".baseId()
|
val OptionalExpectation = "OptionalExpectation".baseId()
|
||||||
val ImplicitlyActualizedByJvmDeclaration = "ImplicitlyActualizedByJvmDeclaration".jvmId()
|
val ImplicitlyActualizedByJvmDeclaration = "ImplicitlyActualizedByJvmDeclaration".jvmId()
|
||||||
|
|
||||||
|
val AssociatedObjectKey = "AssociatedObjectKey".reflectId()
|
||||||
|
val ExperimentalAssociatedObjects = "ExperimentalAssociatedObjects".reflectId()
|
||||||
|
|
||||||
object ParameterNames {
|
object ParameterNames {
|
||||||
val value = Name.identifier("value")
|
val value = Name.identifier("value")
|
||||||
|
|
||||||
|
|||||||
+5
-9
@@ -18,20 +18,16 @@ package org.jetbrains.kotlin.js.resolve.diagnostics
|
|||||||
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.builtins.ReflectionTypes
|
import org.jetbrains.kotlin.builtins.ReflectionTypes
|
||||||
|
import org.jetbrains.kotlin.builtins.StandardNames
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors.UNSUPPORTED
|
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.AbstractReflectionApiCallChecker
|
||||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
|
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
|
import org.jetbrains.kotlin.storage.StorageManager
|
||||||
|
|
||||||
private val ADDITIONAL_ALLOWED_CLASSES = setOf(
|
|
||||||
FqName("kotlin.reflect.AssociatedObjectKey"),
|
|
||||||
FqName("kotlin.reflect.ExperimentalAssociatedObjects")
|
|
||||||
)
|
|
||||||
|
|
||||||
class JsReflectionAPICallChecker(
|
class JsReflectionAPICallChecker(
|
||||||
reflectionTypes: ReflectionTypes,
|
reflectionTypes: ReflectionTypes,
|
||||||
storageManager: StorageManager
|
storageManager: StorageManager
|
||||||
@@ -45,8 +41,8 @@ class JsReflectionAPICallChecker(
|
|||||||
context: CallCheckerContext
|
context: CallCheckerContext
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return super.isAllowedReflectionApi(descriptor, containingClass, context) ||
|
return super.isAllowedReflectionApi(descriptor, containingClass, context) ||
|
||||||
containingClass.fqNameSafe in ADDITIONAL_ALLOWED_CLASSES ||
|
containingClass.classId in StandardClassIds.associatedObjectAnnotations ||
|
||||||
descriptor.name.asString() == "findAssociatedObject"
|
descriptor.name == StandardNames.FqNames.findAssociatedObject.shortName()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun report(element: PsiElement, context: CallCheckerContext) {
|
override fun report(element: PsiElement, context: CallCheckerContext) {
|
||||||
|
|||||||
Reference in New Issue
Block a user