JS: prohibit spread operator and destructuring declaration in dynamic values. See KT-15283
This commit is contained in:
-6
@@ -28,12 +28,6 @@ public final fun compareTo(/*0*/ p0: dynamic): dynamic
|
||||
public final fun compareTo(/*0*/ p0: dynamic): dynamic
|
||||
public final fun compareTo(/*0*/ p0: dynamic): dynamic
|
||||
public final fun foo(): dynamic
|
||||
public final fun component1(): dynamic
|
||||
public final fun component2(): dynamic
|
||||
public final fun component3(): dynamic
|
||||
public final fun foo(): dynamic
|
||||
public final fun foo(): dynamic
|
||||
public final fun foo(): dynamic
|
||||
public final fun inc(): dynamic
|
||||
public final fun inc(): dynamic
|
||||
public final fun dec(): dynamic
|
||||
|
||||
@@ -55,11 +55,6 @@ fun test(d: dynamic) {
|
||||
i.<!DEBUG_INFO_DYNAMIC!>foo<!>()
|
||||
}
|
||||
|
||||
val (<!DEBUG_INFO_DYNAMIC!>a<!>, <!DEBUG_INFO_DYNAMIC!>b<!>, <!DEBUG_INFO_DYNAMIC!>c<!>) = d
|
||||
a.<!DEBUG_INFO_DYNAMIC!>foo<!>()
|
||||
b.<!DEBUG_INFO_DYNAMIC!>foo<!>()
|
||||
c.<!DEBUG_INFO_DYNAMIC!>foo<!>()
|
||||
|
||||
var dVar = d
|
||||
dVar<!DEBUG_INFO_DYNAMIC!>++<!>
|
||||
<!DEBUG_INFO_DYNAMIC!>++<!>dVar
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
public final fun component1(): dynamic
|
||||
public final fun component2(): dynamic
|
||||
public final fun plus(/*0*/ p0: dynamic): dynamic
|
||||
public final fun component1(): dynamic
|
||||
public final fun component2(): dynamic
|
||||
public final fun plus(/*0*/ p0: dynamic): dynamic
|
||||
public final fun component1(): dynamic
|
||||
public final fun component2(): dynamic
|
||||
public final fun plus(/*0*/ p0: dynamic): dynamic
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
fun foo() {
|
||||
for (<!WRONG_OPERATION_WITH_DYNAMIC!>(x, y)<!> in A()) {
|
||||
println(x + y)
|
||||
}
|
||||
|
||||
bar { <!WRONG_OPERATION_WITH_DYNAMIC!>(x, y)<!> ->
|
||||
println(x + y)
|
||||
}
|
||||
|
||||
val x: dynamic = Any()
|
||||
|
||||
<!WRONG_OPERATION_WITH_DYNAMIC!>val (y, z) = x<!>
|
||||
println(y + z)
|
||||
}
|
||||
|
||||
class A {
|
||||
operator fun iterator(): Iterator<dynamic> = TODO("")
|
||||
}
|
||||
|
||||
fun bar(f: (dynamic) -> Unit): Unit = TODO("")
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package
|
||||
|
||||
public fun bar(/*0*/ f: (dynamic) -> kotlin.Unit): kotlin.Unit
|
||||
public fun foo(): kotlin.Unit
|
||||
|
||||
public final class A {
|
||||
public constructor A()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public final operator fun iterator(): kotlin.collections.Iterator<dynamic>
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
+19
-11
@@ -1,14 +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(1, *a) { }
|
||||
d.foo(*a) { "" }
|
||||
d.foo(*a, *a)
|
||||
d.foo(*a, *a) { "" }
|
||||
d.foo(*a, 1, { "" }, *a)
|
||||
d.foo(*a, 1)
|
||||
d.foo(*a, *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, <!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a) { }
|
||||
d.foo(<!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a) { "" }
|
||||
d.foo(<!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a, <!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a)
|
||||
d.foo(<!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a, <!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>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(<!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")
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
package
|
||||
|
||||
public fun bar(/*0*/ vararg x: kotlin.Int /*kotlin.IntArray*/): kotlin.Unit
|
||||
public fun test(/*0*/ d: dynamic): kotlin.Unit
|
||||
|
||||
@@ -110,6 +110,12 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("destructuring.kt")
|
||||
public void testDestructuring() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/destructuring.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("dynamicCalls.kt")
|
||||
public void testDynamicCalls() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/dynamicCalls.kt");
|
||||
|
||||
+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