diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt index a04be32db72..fa9b13366f8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt @@ -98,7 +98,7 @@ private val DEFAULT_CALL_CHECKERS = listOf( ConstructorHeaderCallChecker, ProtectedConstructorCallChecker, ApiVersionCallChecker, CoroutineSuspendCallChecker, BuilderFunctionsCallChecker, DslScopeViolationCallChecker, MissingDependencyClassChecker, CallableReferenceCompatibilityChecker(), LateinitIntrinsicApplicabilityChecker, - UnderscoreUsageChecker + UnderscoreUsageChecker, AssigningNamedArgumentToVarargChecker() ) private val DEFAULT_TYPE_CHECKERS = emptyList() private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/AssigningNamedArgumentToVarargChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/AssigningNamedArgumentToVarargChecker.kt new file mode 100644 index 00000000000..a42dfdcd479 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/AssigningNamedArgumentToVarargChecker.kt @@ -0,0 +1,86 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.checkers + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.ValueArgument +import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isArrayOrArrayLiteral +import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isParameterOfAnnotation +import org.jetbrains.kotlin.resolve.calls.components.isVararg +import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall + +class AssigningNamedArgumentToVarargChecker : CallChecker { + override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { + for ((parameterDescriptor, resolvedArgument) in resolvedCall.valueArguments) { + for (argument in resolvedArgument.arguments) { + checkAssignmentOfSingleElementToVararg(argument, parameterDescriptor, context.resolutionContext) + } + } + } + + private fun checkAssignmentOfSingleElementToVararg( + argument: ValueArgument, + parameterDescriptor: ValueParameterDescriptor, + context: ResolutionContext<*> + ) { + if (!context.languageVersionSettings.supportsFeature(LanguageFeature.AssigningArraysToVarargsInNamedFormInAnnotations)) return + + if (!argument.isNamed()) return + if (!parameterDescriptor.isVararg) return + + val argumentExpression = argument.getArgumentExpression() ?: return + + if (isParameterOfAnnotation(parameterDescriptor)) { + checkAssignmentOfSingleElementInAnnotation(argument, argumentExpression, context) + } + else { + checkAssignmentOfSingleElementInFunction(argument, argumentExpression, context) + } + } + + private fun checkAssignmentOfSingleElementInAnnotation( + argument: ValueArgument, + argumentExpression: KtExpression, + context: ResolutionContext<*> + ) { + if (isArrayOrArrayLiteral(argument, context)) { + if (argument.hasSpread()) { + context.trace.report(Errors.ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM.on(argumentExpression)) + } + } + else { + context.trace.report(Errors.ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM.on(argumentExpression)) + } + } + + private fun checkAssignmentOfSingleElementInFunction( + argument: ValueArgument, + argumentExpression: KtExpression, + context: ResolutionContext<*> + ) { + if (!argument.hasSpread()) { + context.trace.report(Errors.ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM.on(argumentExpression)) + } + } + + private fun ValueArgument.hasSpread() = getSpreadElement() != null +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/varargs/assigningSingleElementsInNamedFormAnnDeprecation.kt b/compiler/testData/diagnostics/tests/varargs/assigningSingleElementsInNamedFormAnnDeprecation.kt new file mode 100644 index 00000000000..eabb5fc4add --- /dev/null +++ b/compiler/testData/diagnostics/tests/varargs/assigningSingleElementsInNamedFormAnnDeprecation.kt @@ -0,0 +1,30 @@ +// !LANGUAGE: +ArrayLiteralsInAnnotations, +AssigningArraysToVarargsInNamedFormInAnnotations + +// FILE: JavaAnn.java + +@interface JavaAnn { + String[] value() default {}; + String[] path() default {}; +} + +// FILE: test.kt + +annotation class Ann(vararg val s: String) + +@Ann(s = "value") +fun test1() {} + +@Ann(s = *arrayOf("value")) +fun test2() {} + +@Ann(s = *["value"]) +fun test3() {} + +@JavaAnn(value = "value") +fun test4() {} + +@JavaAnn("value", path = arrayOf("path")) +fun test5() {} + +@JavaAnn("value", path = ["path"]) +fun test6() {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/varargs/assigningSingleElementsInNamedFormAnnDeprecation.txt b/compiler/testData/diagnostics/tests/varargs/assigningSingleElementsInNamedFormAnnDeprecation.txt new file mode 100644 index 00000000000..40d972d6350 --- /dev/null +++ b/compiler/testData/diagnostics/tests/varargs/assigningSingleElementsInNamedFormAnnDeprecation.txt @@ -0,0 +1,25 @@ +package + +@Ann(s = {"value"}) public fun test1(): kotlin.Unit +@Ann(s = {"value"}) public fun test2(): kotlin.Unit +@Ann(s = {"value"}) public fun test3(): kotlin.Unit +@JavaAnn(value = {"value"}) public fun test4(): kotlin.Unit +@JavaAnn(path = {"path"}, value = {"value"}) public fun test5(): kotlin.Unit +@JavaAnn(path = {"path"}, value = {"value"}) public fun test6(): kotlin.Unit + +public final annotation class Ann : kotlin.Annotation { + public constructor Ann(/*0*/ vararg s: kotlin.String /*kotlin.Array*/) + public final val s: kotlin.Array + 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 open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public/*package*/ final annotation class JavaAnn : kotlin.Annotation { + public/*package*/ constructor JavaAnn(/*0*/ vararg value: kotlin.String /*kotlin.Array*/ = ..., /*1*/ path: kotlin.Array = ...) + public final val path: kotlin.Array + public final val value: kotlin.Array + 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 open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/varargs/assigningSingleElementsInNamedFormFunDeprecation.kt b/compiler/testData/diagnostics/tests/varargs/assigningSingleElementsInNamedFormFunDeprecation.kt new file mode 100644 index 00000000000..1c3fca774c4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/varargs/assigningSingleElementsInNamedFormFunDeprecation.kt @@ -0,0 +1,59 @@ +// !LANGUAGE: +AssigningArraysToVarargsInNamedFormInAnnotations +// !DIAGNOSTICS: -UNUSED_PARAMETER, -UNUSED_VARIABLE + +fun foo(vararg s: Int) {} + +open class Cls(vararg p: Long) + +fun test(i: IntArray) { + foo(s = 1) + foo(s = i) + foo(s = *i) + foo(s = intArrayOf(1)) + foo(s = *intArrayOf(1)) + foo(1) + + Cls(p = 1) + + class Sub : Cls(p = 1) + + val c = object : Cls(p = 1) {} + + foo(s = *intArrayOf(elements = 1)) +} + + +fun anyFoo(vararg a: Any) {} + +fun testAny() { + anyFoo(a = "") + anyFoo(a = arrayOf("")) + anyFoo(a = *arrayOf("")) +} + +fun genFoo(vararg t: T) {} + +fun testGen() { + genFoo(t = 1) + genFoo(t = null) + genFoo>(t = arrayOf()) + genFoo>(t = *arrayOf(arrayOf())) + + genFoo(t = "") + genFoo(t = arrayOf("")) + genFoo(t = *arrayOf("")) +} + +fun manyFoo(vararg v: Int) {} +fun manyFoo(vararg s: String) {} + +fun testMany(a: Any) { + manyFoo(v = 1) + manyFoo(s = "") + + manyFoo(a) + manyFoo(v = a) + manyFoo(s = a) + manyFoo(v = a as Int) + manyFoo(s = a as String) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/varargs/assigningSingleElementsInNamedFormFunDeprecation.txt b/compiler/testData/diagnostics/tests/varargs/assigningSingleElementsInNamedFormFunDeprecation.txt new file mode 100644 index 00000000000..edd84af1bfa --- /dev/null +++ b/compiler/testData/diagnostics/tests/varargs/assigningSingleElementsInNamedFormFunDeprecation.txt @@ -0,0 +1,18 @@ +package + +public fun anyFoo(/*0*/ vararg a: kotlin.Any /*kotlin.Array*/): kotlin.Unit +public fun foo(/*0*/ vararg s: kotlin.Int /*kotlin.IntArray*/): kotlin.Unit +public fun genFoo(/*0*/ vararg t: T /*kotlin.Array*/): kotlin.Unit +public fun manyFoo(/*0*/ vararg s: kotlin.String /*kotlin.Array*/): kotlin.Unit +public fun manyFoo(/*0*/ vararg v: kotlin.Int /*kotlin.IntArray*/): kotlin.Unit +public fun test(/*0*/ i: kotlin.IntArray): kotlin.Unit +public fun testAny(): kotlin.Unit +public fun testGen(): kotlin.Unit +public fun testMany(/*0*/ a: kotlin.Any): kotlin.Unit + +public open class Cls { + public constructor Cls(/*0*/ vararg p: kotlin.Long /*kotlin.LongArray*/) + 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 open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +}