[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
+13
@@ -5063,6 +5063,19 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirJsErrors.SPREAD_OPERATOR_IN_DYNAMIC_CALL) { firDiagnostic ->
|
||||
SpreadOperatorInDynamicCallImpl(
|
||||
firDiagnostic as KtPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirJsErrors.WRONG_OPERATION_WITH_DYNAMIC) { firDiagnostic ->
|
||||
WrongOperationWithDynamicImpl(
|
||||
firDiagnostic.a,
|
||||
firDiagnostic as KtPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirSyntaxErrors.SYNTAX) { firDiagnostic ->
|
||||
SyntaxImpl(
|
||||
firDiagnostic as KtPsiDiagnostic,
|
||||
|
||||
+9
@@ -3523,6 +3523,15 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
override val diagnosticClass get() = DelegationByDynamic::class
|
||||
}
|
||||
|
||||
abstract class SpreadOperatorInDynamicCall : KtFirDiagnostic<KtElement>() {
|
||||
override val diagnosticClass get() = SpreadOperatorInDynamicCall::class
|
||||
}
|
||||
|
||||
abstract class WrongOperationWithDynamic : KtFirDiagnostic<KtElement>() {
|
||||
override val diagnosticClass get() = WrongOperationWithDynamic::class
|
||||
abstract val operation: String
|
||||
}
|
||||
|
||||
abstract class Syntax : KtFirDiagnostic<PsiElement>() {
|
||||
override val diagnosticClass get() = Syntax::class
|
||||
}
|
||||
|
||||
+11
@@ -4262,6 +4262,17 @@ internal class DelegationByDynamicImpl(
|
||||
override val token: KtLifetimeToken,
|
||||
) : KtFirDiagnostic.DelegationByDynamic(), KtAbstractFirDiagnostic<KtElement>
|
||||
|
||||
internal class SpreadOperatorInDynamicCallImpl(
|
||||
override val firDiagnostic: KtPsiDiagnostic,
|
||||
override val token: KtLifetimeToken,
|
||||
) : KtFirDiagnostic.SpreadOperatorInDynamicCall(), KtAbstractFirDiagnostic<KtElement>
|
||||
|
||||
internal class WrongOperationWithDynamicImpl(
|
||||
override val operation: String,
|
||||
override val firDiagnostic: KtPsiDiagnostic,
|
||||
override val token: KtLifetimeToken,
|
||||
) : KtFirDiagnostic.WrongOperationWithDynamic(), KtAbstractFirDiagnostic<KtElement>
|
||||
|
||||
internal class SyntaxImpl(
|
||||
override val firDiagnostic: KtPsiDiagnostic,
|
||||
override val token: KtLifetimeToken,
|
||||
|
||||
+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()
|
||||
|
||||
+3
-3
@@ -1,17 +1,17 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
fun foo() {
|
||||
for ((x, y) in A()) {
|
||||
for ((<!WRONG_OPERATION_WITH_DYNAMIC!>x<!>, <!WRONG_OPERATION_WITH_DYNAMIC!>y<!>) in A()) {
|
||||
println(x + y)
|
||||
}
|
||||
|
||||
bar { (x, y) ->
|
||||
bar { (<!WRONG_OPERATION_WITH_DYNAMIC!>x<!>, <!WRONG_OPERATION_WITH_DYNAMIC!>y<!>) ->
|
||||
println(x + y)
|
||||
}
|
||||
|
||||
val x: dynamic = Any()
|
||||
|
||||
val (y, z) = x
|
||||
val (<!WRONG_OPERATION_WITH_DYNAMIC!>y<!>, <!WRONG_OPERATION_WITH_DYNAMIC!>z<!>) = x
|
||||
println(y + z)
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -1,12 +1,12 @@
|
||||
fun foo() {
|
||||
val a: dynamic = Any()
|
||||
println(a in setOf(1, 2))
|
||||
println(1 in a)
|
||||
println(1 !in a)
|
||||
println(<!WRONG_OPERATION_WITH_DYNAMIC!>1 in a<!>)
|
||||
println(<!WRONG_OPERATION_WITH_DYNAMIC!>1 !in a<!>)
|
||||
when (2) {
|
||||
in a -> println("ok")
|
||||
<!WRONG_OPERATION_WITH_DYNAMIC!>in a<!> -> println("ok")
|
||||
}
|
||||
when (3) {
|
||||
!in a -> println("ok")
|
||||
<!WRONG_OPERATION_WITH_DYNAMIC!>!in a<!> -> println("ok")
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
fun foo() {
|
||||
val a: dynamic = Any()
|
||||
println(a[0])
|
||||
println(a[0, 1])
|
||||
println(<!WRONG_OPERATION_WITH_DYNAMIC!>a[0, 1]<!>)
|
||||
|
||||
a[0] = 23
|
||||
a[0, 1] = 42
|
||||
<!WRONG_OPERATION_WITH_DYNAMIC!>a[0, 1] = 42<!>
|
||||
}
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ fun foo() {
|
||||
val a: dynamic = Any()
|
||||
val b: dynamic = Any()
|
||||
val c = C()
|
||||
println(a..b)
|
||||
println(<!WRONG_OPERATION_WITH_DYNAMIC!>a..b<!>)
|
||||
println(c..a)
|
||||
println(a.rangeTo(b))
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ fun foo() {
|
||||
val a: dynamic = Any()
|
||||
val b: dynamic = Any()
|
||||
val c = C()
|
||||
println(a..<b)
|
||||
println(<!WRONG_OPERATION_WITH_DYNAMIC!>a..<b<!>)
|
||||
println(c..<a)
|
||||
println(a.rangeUntil(b))
|
||||
}
|
||||
|
||||
+10
-10
@@ -1,22 +1,22 @@
|
||||
fun test(d: dynamic) {
|
||||
val a = arrayOf(1, 2, 3)
|
||||
|
||||
d.foo(*d)
|
||||
d.foo(*a)
|
||||
d.foo(1, "2", *a)
|
||||
d.foo(<!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>d)
|
||||
d.foo(<!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a)
|
||||
d.foo(1, "2", <!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a)
|
||||
d.foo(1, *a) <!VARARG_OUTSIDE_PARENTHESES!>{ }<!>
|
||||
d.foo(*a) <!VARARG_OUTSIDE_PARENTHESES!>{ "" }<!>
|
||||
d.foo(*a, *a)
|
||||
d.foo(<!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a, <!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a)
|
||||
d.foo(*a, *a) <!VARARG_OUTSIDE_PARENTHESES!>{ "" }<!>
|
||||
d.foo(*a, 1, { "" }, *a)
|
||||
d.foo(*a, 1)
|
||||
d.foo(*a, *a, { "" })
|
||||
d.foo(<!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a, 1, { "" }, <!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a)
|
||||
d.foo(<!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a, 1)
|
||||
d.foo(<!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a, <!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a, { "" })
|
||||
|
||||
bar(d)
|
||||
bar(d, d)
|
||||
bar(*d)
|
||||
bar(*d, *d)
|
||||
bar(*d, 23, *d)
|
||||
bar(<!WRONG_OPERATION_WITH_DYNAMIC!>*d<!>)
|
||||
bar(<!WRONG_OPERATION_WITH_DYNAMIC!>*d<!>, <!WRONG_OPERATION_WITH_DYNAMIC!>*d<!>)
|
||||
bar(<!WRONG_OPERATION_WITH_DYNAMIC!>*d<!>, 23, <!WRONG_OPERATION_WITH_DYNAMIC!>*d<!>)
|
||||
}
|
||||
|
||||
fun bar(vararg x: Int): Unit = TODO("$x")
|
||||
|
||||
Reference in New Issue
Block a user