JS: prohibit spread operator and destructuring declaration in dynamic values. See KT-15283
This commit is contained in:
+3
@@ -68,7 +68,10 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy {
|
||||
put(ErrorsJs.UNCHECKED_CAST_TO_NATIVE_INTERFACE, "Unchecked cast to native interface: {0} to {1}", RENDER_TYPE, RENDER_TYPE)
|
||||
put(ErrorsJs.NATIVE_INTERFACE_AS_REIFIED_TYPE_ARGUMENT, "Cannot pass native interface {0} for reified type parameter", RENDER_TYPE)
|
||||
put(ErrorsJs.EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE, "External type extends non-external type")
|
||||
|
||||
put(ErrorsJs.WRONG_OPERATION_WITH_DYNAMIC, "Wrong operation with dynamic value: {0}", Renderers.STRING)
|
||||
put(ErrorsJs.SPREAD_OPERATOR_IN_DYNAMIC_CALL, "Can't apply spread operator in dynamic call")
|
||||
|
||||
put(ErrorsJs.RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION, "Runtime annotation can't be put on external declaration")
|
||||
put(ErrorsJs.RUNTIME_ANNOTATION_NOT_SUPPORTED, "Reflection is not supported in JavaScript target, therefore you won't be able " +
|
||||
"to read this annotation in run-time")
|
||||
|
||||
@@ -68,7 +68,10 @@ public interface ErrorsJs {
|
||||
DiagnosticFactory1<PsiElement, KotlinType> NATIVE_INTERFACE_AS_REIFIED_TYPE_ARGUMENT = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory0<KtElement> EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE = DiagnosticFactory0.create(
|
||||
ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||
|
||||
DiagnosticFactory1<PsiElement, String> WRONG_OPERATION_WITH_DYNAMIC = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> SPREAD_OPERATOR_IN_DYNAMIC_CALL = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<PsiElement> RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION = DiagnosticFactory0.create(
|
||||
ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||
DiagnosticFactory0<PsiElement> RUNTIME_ANNOTATION_NOT_SUPPORTED = DiagnosticFactory0.create(WARNING, DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||
|
||||
+47
-17
@@ -18,37 +18,64 @@ package org.jetbrains.kotlin.js.resolve.diagnostics
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtArrayAccessExpression
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||
import org.jetbrains.kotlin.psi.KtWhenConditionInRange
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
import org.jetbrains.kotlin.types.isDynamic
|
||||
|
||||
object JsDynamicCallChecker : CallChecker {
|
||||
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
||||
val callee = resolvedCall.resultingDescriptor
|
||||
if (callee.dispatchReceiverParameter?.type?.isDynamic() != true) return
|
||||
if (!callee.isDynamic()) {
|
||||
return checkSpreadOperator(resolvedCall, context)
|
||||
}
|
||||
|
||||
val element = resolvedCall.call.callElement
|
||||
if (element is KtArrayAccessExpression && element.indexExpressions.size > 1) {
|
||||
context.trace.report(ErrorsJs.WRONG_OPERATION_WITH_DYNAMIC.on(reportOn, "indexed access with more than one index"))
|
||||
when (element) {
|
||||
is KtArrayAccessExpression -> {
|
||||
if (element.indexExpressions.size > 1) {
|
||||
context.trace.report(ErrorsJs.WRONG_OPERATION_WITH_DYNAMIC.on(reportOn, "indexed access with more than one index"))
|
||||
}
|
||||
}
|
||||
|
||||
is KtWhenConditionInRange -> {
|
||||
reportInOperation(context, reportOn)
|
||||
}
|
||||
|
||||
is KtBinaryExpression -> {
|
||||
val token = element.operationToken
|
||||
when (token) {
|
||||
in OperatorConventions.IN_OPERATIONS -> {
|
||||
reportInOperation(context, reportOn)
|
||||
}
|
||||
KtTokens.RANGE -> {
|
||||
context.trace.report(ErrorsJs.WRONG_OPERATION_WITH_DYNAMIC.on(reportOn, "`..` operation"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is KtDestructuringDeclarationEntry -> {
|
||||
if (!reportedOn(context, element.node.treeParent.psi)) {
|
||||
context.trace.report(ErrorsJs.WRONG_OPERATION_WITH_DYNAMIC.on(element.parent, "destructuring declaration"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (element is KtWhenConditionInRange) {
|
||||
reportInOperation(context, reportOn)
|
||||
for (argument in resolvedCall.call.valueArguments) {
|
||||
argument.getSpreadElement()?.let {
|
||||
context.trace.report(ErrorsJs.SPREAD_OPERATOR_IN_DYNAMIC_CALL.on(it))
|
||||
}
|
||||
}
|
||||
else if (element is KtBinaryExpression) {
|
||||
val token = element.operationToken
|
||||
when (token) {
|
||||
in OperatorConventions.IN_OPERATIONS -> {
|
||||
reportInOperation(context, reportOn)
|
||||
}
|
||||
KtTokens.RANGE -> {
|
||||
context.trace.report(ErrorsJs.WRONG_OPERATION_WITH_DYNAMIC.on(reportOn, "`..` operation"))
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkSpreadOperator(resolvedCall: ResolvedCall<*>, context: CallCheckerContext) {
|
||||
for (arg in resolvedCall.call.valueArguments) {
|
||||
val argExpression = arg.getArgumentExpression() ?: continue
|
||||
if (context.trace.bindingContext.getType(argExpression)?.isDynamic() == true && arg.getSpreadElement() != null) {
|
||||
context.trace.report(ErrorsJs.WRONG_OPERATION_WITH_DYNAMIC.on(arg.asElement(), "spread operator"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,4 +83,7 @@ object JsDynamicCallChecker : CallChecker {
|
||||
private fun reportInOperation(context: CallCheckerContext, reportOn: PsiElement) {
|
||||
context.trace.report(ErrorsJs.WRONG_OPERATION_WITH_DYNAMIC.on(reportOn, "`in` operation"))
|
||||
}
|
||||
|
||||
private fun reportedOn(context: CallCheckerContext, element: PsiElement) =
|
||||
context.trace.bindingContext.diagnostics.forElement(element).any { it.factory == ErrorsJs.WRONG_OPERATION_WITH_DYNAMIC }
|
||||
}
|
||||
Reference in New Issue
Block a user