From 71eaf651e87394c9c0f4dfe3fde8da701ec34c57 Mon Sep 17 00:00:00 2001 From: Alexander Korepanov Date: Tue, 17 Oct 2023 14:58:10 +0200 Subject: [PATCH] [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 --- .../js/checkers/JsExpressionCheckers.kt | 1 + .../FirJsCodeConstantArgumentChecker.kt | 58 +++++++++++++++++++ .../fir/analysis/checkers/FirConstChecks.kt | 4 ++ .../FirAnnotationClassDeclarationChecker.kt | 2 +- .../declaration/FirConstPropertyChecker.kt | 4 +- .../kotlin/name/JsStandardClassIds.kt | 3 + 6 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/expression/FirJsCodeConstantArgumentChecker.kt 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 ec6619ec04d..075472e3dfa 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 @@ -22,6 +22,7 @@ object JsExpressionCheckers : ExpressionCheckers() { override val functionCallCheckers: Set get() = setOf( + FirJsCodeConstantArgumentChecker, FirJsReifiedExternalChecker ) diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/expression/FirJsCodeConstantArgumentChecker.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/expression/FirJsCodeConstantArgumentChecker.kt new file mode 100644 index 00000000000..309570a2ba1 --- /dev/null +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/expression/FirJsCodeConstantArgumentChecker.kt @@ -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) + } + } + }) + } +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirConstChecks.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirConstChecks.kt index 274e2bd53b5..bf36de56d5d 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirConstChecks.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirConstChecks.kt @@ -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, diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirAnnotationClassDeclarationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirAnnotationClassDeclarationChecker.kt index 1213e0330e8..21042f7c9e5 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirAnnotationClassDeclarationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirAnnotationClassDeclarationChecker.kt @@ -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) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConstPropertyChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConstPropertyChecker.kt index 7ce00125474..2738f71266a 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConstPropertyChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConstPropertyChecker.kt @@ -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) } } diff --git a/core/compiler.common.js/src/org/jetbrains/kotlin/name/JsStandardClassIds.kt b/core/compiler.common.js/src/org/jetbrains/kotlin/name/JsStandardClassIds.kt index 39885bb4e49..72b00ba6977 100644 --- a/core/compiler.common.js/src/org/jetbrains/kotlin/name/JsStandardClassIds.kt +++ b/core/compiler.common.js/src/org/jetbrains/kotlin/name/JsStandardClassIds.kt @@ -58,6 +58,9 @@ object JsStandardClassIds { } object Callables { + @JvmField + val JsCode = "js".callableId(BASE_JS_PACKAGE) + @JvmField val JsDefinedExternally = "definedExternally".callableId(BASE_JS_PACKAGE)