[JS FIR] Implement JSCODE_ARGUMENT_NON_CONST_EXPRESSION FIR checker
The meaning of this check is the same as K1 JSCODE_ARGUMENT_SHOULD_BE_CONSTANT and JSCODE_ARGUMENT_NON_CONST_EXPRESSION diagnostics. The main difference is that K2 JSCODE_ARGUMENT_NON_CONST_EXPRESSION diagnostic checks the js() argument in the same way as const val initializers or annotation arguments are checked. This means that, the K2 diagnostic is stricter than original K1 JSCODE_ARGUMENT_SHOULD_BE_CONSTANT diagnostic, which allows the use of non-constant vals. ^KT-59435 Fixed
This commit is contained in:
committed by
Space Team
parent
2d934f6357
commit
71eaf651e8
+1
@@ -22,6 +22,7 @@ object JsExpressionCheckers : ExpressionCheckers() {
|
||||
|
||||
override val functionCallCheckers: Set<FirFunctionCallChecker>
|
||||
get() = setOf(
|
||||
FirJsCodeConstantArgumentChecker,
|
||||
FirJsReifiedExternalChecker
|
||||
)
|
||||
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.canBeEvaluatedAtCompileTime
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirFunctionCallChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isConst
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.arguments
|
||||
import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.types.isString
|
||||
import org.jetbrains.kotlin.fir.types.resolvedType
|
||||
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
|
||||
import org.jetbrains.kotlin.name.JsStandardClassIds
|
||||
|
||||
object FirJsCodeConstantArgumentChecker : FirFunctionCallChecker() {
|
||||
override fun check(expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
if (expression.calleeReference.toResolvedCallableSymbol()?.callableId != JsStandardClassIds.Callables.JsCode) {
|
||||
return
|
||||
}
|
||||
|
||||
val jsCodeExpression = expression.arguments.firstOrNull()
|
||||
if (jsCodeExpression == null || !jsCodeExpression.resolvedType.isString) {
|
||||
reporter.reportOn(jsCodeExpression?.source ?: expression.source, FirJsErrors.JSCODE_ARGUMENT_NON_CONST_EXPRESSION, context)
|
||||
return
|
||||
}
|
||||
|
||||
jsCodeExpression.accept(object : FirVisitorVoid() {
|
||||
var lastReportedElement: FirElement? = null
|
||||
|
||||
override fun visitElement(element: FirElement) {
|
||||
val lastReported = lastReportedElement
|
||||
element.acceptChildren(this)
|
||||
if (lastReported == lastReportedElement && !canBeEvaluatedAtCompileTime(element as? FirExpression, context.session)) {
|
||||
lastReportedElement = element
|
||||
val source = element.source ?: jsCodeExpression.source
|
||||
reporter.reportOn(source, FirJsErrors.JSCODE_ARGUMENT_NON_CONST_EXPRESSION, context)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitPropertyAccessExpression(propertyAccessExpression: FirPropertyAccessExpression) {
|
||||
if (propertyAccessExpression.calleeReference.toResolvedCallableSymbol()?.isConst != true) {
|
||||
super.visitPropertyAccessExpression(propertyAccessExpression)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,10 @@ import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
fun ConeKotlinType.canBeUsedForConstVal(): Boolean = with(lowerBoundIfFlexible()) { isPrimitive || isString || isUnsignedType }
|
||||
|
||||
fun canBeEvaluatedAtCompileTime(expression: FirExpression?, session: FirSession): Boolean {
|
||||
return checkConstantArguments(expression, session) == null
|
||||
}
|
||||
|
||||
internal fun checkConstantArguments(
|
||||
expression: FirExpression?,
|
||||
session: FirSession,
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ object FirAnnotationClassDeclarationChecker : FirRegularClassChecker() {
|
||||
reporter.reportOn(source, FirErrors.VAR_ANNOTATION_PARAMETER, context)
|
||||
}
|
||||
val defaultValue = parameter.defaultValue
|
||||
if (defaultValue != null && checkConstantArguments(defaultValue, context.session) != null) {
|
||||
if (defaultValue != null && !canBeEvaluatedAtCompileTime(defaultValue, context.session)) {
|
||||
reporter.reportOn(defaultValue.source, FirErrors.ANNOTATION_PARAMETER_DEFAULT_VALUE_MUST_BE_CONSTANT, context)
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -8,12 +8,12 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.canBeUsedForConstVal
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.checkConstantArguments
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.getModifier
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.canBeEvaluatedAtCompileTime
|
||||
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isConst
|
||||
@@ -62,7 +62,7 @@ object FirConstPropertyChecker : FirPropertyChecker() {
|
||||
return
|
||||
}
|
||||
|
||||
if (checkConstantArguments(initializer, context.session) != null) {
|
||||
if (!canBeEvaluatedAtCompileTime(initializer, context.session)) {
|
||||
reporter.reportOn(initializer.source, FirErrors.CONST_VAL_WITH_NON_CONST_INITIALIZER, context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,9 @@ object JsStandardClassIds {
|
||||
}
|
||||
|
||||
object Callables {
|
||||
@JvmField
|
||||
val JsCode = "js".callableId(BASE_JS_PACKAGE)
|
||||
|
||||
@JvmField
|
||||
val JsDefinedExternally = "definedExternally".callableId(BASE_JS_PACKAGE)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user