[FIR JS] Support 2 JS diagnostics
The change in `FirDiagnosticsHandler` ensures `DEBUG_INFO_DYNAMIC` is still reported in `FirPsiJsOldFrontendDiagnosticsTestGenerated.testConventions`. Support `SPREAD_OPERATOR_IN_DYNAMIC_CALL` and `WRONG_OPERATION_WITH_DYNAMIC`
This commit is contained in:
committed by
Space Team
parent
57568770b4
commit
03e2152957
+4
@@ -117,5 +117,9 @@ object JS_DIAGNOSTICS_LIST : DiagnosticList("FirJsErrors") {
|
||||
|
||||
val DYNAMICS by object : DiagnosticGroup("Dynamics") {
|
||||
val DELEGATION_BY_DYNAMIC by error<KtElement>()
|
||||
val SPREAD_OPERATOR_IN_DYNAMIC_CALL by error<KtElement>(PositioningStrategy.SPREAD_OPERATOR)
|
||||
val WRONG_OPERATION_WITH_DYNAMIC by error<KtElement> {
|
||||
parameter<String>("operation")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -89,6 +89,8 @@ object FirJsErrors {
|
||||
|
||||
// Dynamics
|
||||
val DELEGATION_BY_DYNAMIC by error0<KtElement>()
|
||||
val SPREAD_OPERATOR_IN_DYNAMIC_CALL by error0<KtElement>(SourceElementPositioningStrategies.SPREAD_OPERATOR)
|
||||
val WRONG_OPERATION_WITH_DYNAMIC by error1<KtElement, String>()
|
||||
|
||||
init {
|
||||
RootDiagnosticRendererFactory.registerFactory(FirJsErrorsDefaultMessages)
|
||||
|
||||
+4
@@ -53,6 +53,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.OVERRIDING_E
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS_WITH_FAKE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.RUNTIME_ANNOTATION_NOT_SUPPORTED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.SPREAD_OPERATOR_IN_DYNAMIC_CALL
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.UNCHECKED_CAST_TO_EXTERNAL_INTERFACE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_BODY_OF_EXTERNAL_DECLARATION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_DEFAULT_VALUE_FOR_EXTERNAL_FUN_PARAMETER
|
||||
@@ -61,6 +62,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_EXTERN
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_INITIALIZER_OF_EXTERNAL_DECLARATION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_JS_QUALIFIER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_MULTIPLE_INHERITANCE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_OPERATION_WITH_DYNAMIC
|
||||
|
||||
@Suppress("unused")
|
||||
object FirJsErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
|
||||
@@ -78,6 +80,8 @@ object FirJsErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
|
||||
FirDiagnosticRenderers.SYMBOL
|
||||
)
|
||||
map.put(DELEGATION_BY_DYNAMIC, "Can't delegate to dynamic value")
|
||||
map.put(SPREAD_OPERATOR_IN_DYNAMIC_CALL, "Can't apply spread operator in dynamic call")
|
||||
map.put(WRONG_OPERATION_WITH_DYNAMIC, "Wrong operation with dynamic value: {0}", CommonRenderers.STRING)
|
||||
map.put(IMPLEMENTING_FUNCTION_INTERFACE, "Implementing function interface is prohibited in JavaScript")
|
||||
map.put(OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS, "Overriding `external` function with optional parameters")
|
||||
map.put(
|
||||
|
||||
+6
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.fir.analysis.js.checkers
|
||||
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.expression.*
|
||||
import org.jetbrains.kotlin.fir.analysis.js.checkers.declaration.*
|
||||
import org.jetbrains.kotlin.fir.analysis.js.checkers.expression.*
|
||||
|
||||
object JsExpressionCheckers : ExpressionCheckers() {
|
||||
@@ -19,4 +20,9 @@ object JsExpressionCheckers : ExpressionCheckers() {
|
||||
FirJsDefinedExternallyCallChecker,
|
||||
FirJsNativeRttiChecker,
|
||||
)
|
||||
|
||||
override val functionCallCheckers: Set<FirFunctionCallChecker>
|
||||
get() = setOf(
|
||||
FirJsDynamicCallChecker,
|
||||
)
|
||||
}
|
||||
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.KtFakeSourceElementKind
|
||||
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.FirFunctionCallChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.references.FirNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.resolved
|
||||
import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeDynamicType
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
private val nameToOperator = mapOf(
|
||||
OperatorNameConventions.CONTAINS to "in",
|
||||
OperatorNameConventions.RANGE_TO to "..",
|
||||
OperatorNameConventions.RANGE_UNTIL to "..<",
|
||||
)
|
||||
|
||||
object FirJsDynamicCallChecker : FirFunctionCallChecker() {
|
||||
override fun check(expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val callee = expression.calleeReference.resolved ?: return
|
||||
|
||||
if (callee.resolvedSymbol.origin !is FirDeclarationOrigin.DynamicScope) {
|
||||
return checkSpreadOperator(expression, context, reporter)
|
||||
}
|
||||
|
||||
val symbol = callee.toResolvedCallableSymbol()
|
||||
?: error("Resolved call callee without a callable symbol")
|
||||
|
||||
when {
|
||||
expression.isArrayAccessWithMultipleIndices -> reporter.reportOn(
|
||||
expression.source, FirJsErrors.WRONG_OPERATION_WITH_DYNAMIC, "indexed access with more than one index", context
|
||||
)
|
||||
expression.isInOperator -> reporter.reportOn(
|
||||
expression.source, FirJsErrors.WRONG_OPERATION_WITH_DYNAMIC, "`in` operation", context
|
||||
)
|
||||
expression.isRangeOperator -> reporter.reportOn(
|
||||
expression.source, FirJsErrors.WRONG_OPERATION_WITH_DYNAMIC, "`${nameToOperator[symbol.name]}` operation", context
|
||||
)
|
||||
expression is FirComponentCall -> reporter.reportOn(
|
||||
expression.source, FirJsErrors.WRONG_OPERATION_WITH_DYNAMIC, "`destructuring declaration", context
|
||||
)
|
||||
}
|
||||
|
||||
forAllSpreadArgumentsOf(expression) {
|
||||
reporter.reportOn(it.source, FirJsErrors.SPREAD_OPERATOR_IN_DYNAMIC_CALL, context)
|
||||
}
|
||||
}
|
||||
|
||||
private val FirCall.isArrayAccessWithMultipleIndices: Boolean
|
||||
get() {
|
||||
val callee = calleeReference as? FirNamedReference
|
||||
?: return false
|
||||
|
||||
if (callee.source?.kind != KtFakeSourceElementKind.ArrayAccessNameReference) {
|
||||
return false
|
||||
}
|
||||
|
||||
val arguments = (arguments.singleOrNull() as? FirVarargArgumentsExpression)?.arguments
|
||||
?: return false
|
||||
|
||||
return callee.name == OperatorNameConventions.GET && arguments.size >= 2
|
||||
|| callee.name == OperatorNameConventions.SET && arguments.size >= 3
|
||||
}
|
||||
|
||||
private val FirFunctionCall.isInOperator
|
||||
get() = calleeReference.resolved?.name == OperatorNameConventions.CONTAINS && origin == FirFunctionCallOrigin.Operator
|
||||
|
||||
private val FirFunctionCall.isRangeOperator
|
||||
get(): Boolean {
|
||||
val name = calleeReference.resolved?.name
|
||||
return (name == OperatorNameConventions.RANGE_TO || name == OperatorNameConventions.RANGE_UNTIL)
|
||||
&& origin == FirFunctionCallOrigin.Operator
|
||||
}
|
||||
|
||||
private fun checkSpreadOperator(expression: FirCall, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
forAllSpreadArgumentsOf(expression) {
|
||||
if (it.typeRef.coneType is ConeDynamicType) {
|
||||
reporter.reportOn(it.source, FirJsErrors.WRONG_OPERATION_WITH_DYNAMIC, "spread operator", context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun forAllSpreadArgumentsOf(call: FirCall, callback: (FirExpression) -> Unit) {
|
||||
for (argument in call.argumentList.arguments) {
|
||||
if (argument !is FirVarargArgumentsExpression) {
|
||||
continue
|
||||
}
|
||||
|
||||
for (it in argument.arguments) {
|
||||
if (it is FirSpreadArgumentExpression) {
|
||||
callback(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2399,11 +2399,13 @@ open class RawFirBuilder(
|
||||
return leftArgument.generateLazyLogicalOperation(rightArgument, operationToken == ANDAND, source)
|
||||
in OperatorConventions.IN_OPERATIONS ->
|
||||
return rightArgument.generateContainsOperation(
|
||||
leftArgument, operationToken == NOT_IN, source, expression.operationReference.toFirSourceElement(),
|
||||
leftArgument, operationToken == NOT_IN, source,
|
||||
expression.operationReference.toFirSourceElement(),
|
||||
)
|
||||
in OperatorConventions.COMPARISON_OPERATIONS ->
|
||||
return leftArgument.generateComparisonExpression(
|
||||
rightArgument, operationToken, source, expression.operationReference.toFirSourceElement(),
|
||||
rightArgument, operationToken, source,
|
||||
expression.operationReference.toFirSourceElement(),
|
||||
)
|
||||
}
|
||||
val conventionCallName = operationToken.toBinaryName()
|
||||
|
||||
Reference in New Issue
Block a user