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 compareTo(/*0*/ p0: dynamic): dynamic
|
public final fun compareTo(/*0*/ p0: dynamic): dynamic
|
||||||
public final fun foo(): 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 inc(): dynamic
|
public final fun inc(): dynamic
|
||||||
public final fun dec(): dynamic
|
public final fun dec(): dynamic
|
||||||
|
|||||||
@@ -55,11 +55,6 @@ fun test(d: dynamic) {
|
|||||||
i.<!DEBUG_INFO_DYNAMIC!>foo<!>()
|
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
|
var dVar = d
|
||||||
dVar<!DEBUG_INFO_DYNAMIC!>++<!>
|
dVar<!DEBUG_INFO_DYNAMIC!>++<!>
|
||||||
<!DEBUG_INFO_DYNAMIC!>++<!>dVar
|
<!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) {
|
fun test(d: dynamic) {
|
||||||
val a = arrayOf(1, 2, 3)
|
val a = arrayOf(1, 2, 3)
|
||||||
|
|
||||||
d.foo(*d)
|
d.foo(<!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>d)
|
||||||
d.foo(*a)
|
d.foo(<!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a)
|
||||||
d.foo(1, "2", *a)
|
d.foo(1, "2", <!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a)
|
||||||
d.foo(1, *a) { }
|
d.foo(1, <!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a) { }
|
||||||
d.foo(*a) { "" }
|
d.foo(<!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a) { "" }
|
||||||
d.foo(*a, *a)
|
d.foo(<!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a, <!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a)
|
||||||
d.foo(*a, *a) { "" }
|
d.foo(<!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a, <!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a) { "" }
|
||||||
d.foo(*a, 1, { "" }, *a)
|
d.foo(<!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a, 1, { "" }, <!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a)
|
||||||
d.foo(*a, 1)
|
d.foo(<!SPREAD_OPERATOR_IN_DYNAMIC_CALL!>*<!>a, 1)
|
||||||
d.foo(*a, *a, { "" })
|
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
|
package
|
||||||
|
|
||||||
|
public fun bar(/*0*/ vararg x: kotlin.Int /*kotlin.IntArray*/): kotlin.Unit
|
||||||
public fun test(/*0*/ d: dynamic): kotlin.Unit
|
public fun test(/*0*/ d: dynamic): kotlin.Unit
|
||||||
|
|||||||
@@ -110,6 +110,12 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("dynamicCalls.kt")
|
||||||
public void testDynamicCalls() throws Exception {
|
public void testDynamicCalls() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/dynamicCalls.kt");
|
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.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.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.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.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_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 " +
|
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")
|
"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);
|
DiagnosticFactory1<PsiElement, KotlinType> NATIVE_INTERFACE_AS_REIFIED_TYPE_ARGUMENT = DiagnosticFactory1.create(ERROR);
|
||||||
DiagnosticFactory0<KtElement> EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE = DiagnosticFactory0.create(
|
DiagnosticFactory0<KtElement> EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE = DiagnosticFactory0.create(
|
||||||
ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT);
|
ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||||
|
|
||||||
DiagnosticFactory1<PsiElement, String> WRONG_OPERATION_WITH_DYNAMIC = DiagnosticFactory1.create(ERROR);
|
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(
|
DiagnosticFactory0<PsiElement> RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION = DiagnosticFactory0.create(
|
||||||
ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||||
DiagnosticFactory0<PsiElement> RUNTIME_ANNOTATION_NOT_SUPPORTED = DiagnosticFactory0.create(WARNING, 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 com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.KtArrayAccessExpression
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
|
||||||
import org.jetbrains.kotlin.psi.KtWhenConditionInRange
|
|
||||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
|
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
|
||||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
|
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
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.expressions.OperatorConventions
|
||||||
import org.jetbrains.kotlin.types.isDynamic
|
import org.jetbrains.kotlin.types.isDynamic
|
||||||
|
|
||||||
object JsDynamicCallChecker : CallChecker {
|
object JsDynamicCallChecker : CallChecker {
|
||||||
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
||||||
val callee = resolvedCall.resultingDescriptor
|
val callee = resolvedCall.resultingDescriptor
|
||||||
if (callee.dispatchReceiverParameter?.type?.isDynamic() != true) return
|
if (!callee.isDynamic()) {
|
||||||
|
return checkSpreadOperator(resolvedCall, context)
|
||||||
|
}
|
||||||
|
|
||||||
val element = resolvedCall.call.callElement
|
val element = resolvedCall.call.callElement
|
||||||
if (element is KtArrayAccessExpression && element.indexExpressions.size > 1) {
|
when (element) {
|
||||||
context.trace.report(ErrorsJs.WRONG_OPERATION_WITH_DYNAMIC.on(reportOn, "indexed access with more than one index"))
|
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) {
|
for (argument in resolvedCall.call.valueArguments) {
|
||||||
reportInOperation(context, reportOn)
|
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) {
|
private fun checkSpreadOperator(resolvedCall: ResolvedCall<*>, context: CallCheckerContext) {
|
||||||
in OperatorConventions.IN_OPERATIONS -> {
|
for (arg in resolvedCall.call.valueArguments) {
|
||||||
reportInOperation(context, reportOn)
|
val argExpression = arg.getArgumentExpression() ?: continue
|
||||||
}
|
if (context.trace.bindingContext.getType(argExpression)?.isDynamic() == true && arg.getSpreadElement() != null) {
|
||||||
KtTokens.RANGE -> {
|
context.trace.report(ErrorsJs.WRONG_OPERATION_WITH_DYNAMIC.on(arg.asElement(), "spread operator"))
|
||||||
context.trace.report(ErrorsJs.WRONG_OPERATION_WITH_DYNAMIC.on(reportOn, "`..` operation"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,4 +83,7 @@ object JsDynamicCallChecker : CallChecker {
|
|||||||
private fun reportInOperation(context: CallCheckerContext, reportOn: PsiElement) {
|
private fun reportInOperation(context: CallCheckerContext, reportOn: PsiElement) {
|
||||||
context.trace.report(ErrorsJs.WRONG_OPERATION_WITH_DYNAMIC.on(reportOn, "`in` operation"))
|
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