[FIR] Report UNSUPPORTED on array literals not from annotation classes ^KT-50750 Fixed

This commit is contained in:
Ivan Kochurkin
2022-02-09 17:14:23 +03:00
committed by teamcity
parent 19285d0569
commit 73b45a1254
12 changed files with 139 additions and 94 deletions
@@ -147,4 +147,9 @@ object CommonExpressionCheckers : ExpressionCheckers() {
get() = setOf(
FirEqualityCompatibilityChecker,
)
override val arrayOfCallCheckers: Set<FirArrayOfCallChecker>
get() = setOf(
FirUnsupportedArrayLiteralChecker
)
}
@@ -0,0 +1,90 @@
/*
* Copyright 2010-2022 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.checkers.expression
import org.jetbrains.kotlin.descriptors.ClassKind
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.toRegularClassSymbol
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.declarations.impl.FirPrimaryConstructor
import org.jetbrains.kotlin.fir.declarations.utils.isCompanion
import org.jetbrains.kotlin.fir.expressions.*
object FirUnsupportedArrayLiteralChecker : FirArrayOfCallChecker() {
override fun check(expression: FirArrayOfCall, context: CheckerContext, reporter: DiagnosticReporter) {
if (!isInsideAnnotationCall(expression, context) &&
(context.qualifiedAccessOrAnnotationCalls.isNotEmpty() || !isInsideAnnotationClass(context))
) {
reporter.reportOn(
expression.source,
FirErrors.UNSUPPORTED,
"Collection literals outside of annotations",
context
)
}
}
private fun isInsideAnnotationCall(expression: FirArrayOfCall, context: CheckerContext): Boolean {
context.qualifiedAccessOrAnnotationCalls.lastOrNull()?.let {
val arguments = when (it) {
is FirFunctionCall ->
if (it.typeRef.toRegularClassSymbol(context.session)?.classKind == ClassKind.ANNOTATION_CLASS) {
it.arguments
} else {
return false
}
is FirAnnotationCall -> it.arguments
else -> return false
}
return arguments.any { argument ->
val unwrappedArguments =
if (argument is FirVarargArgumentsExpression) {
argument.arguments.map { arg -> arg.unwrapArgument() }
} else {
listOf(argument.unwrapArgument())
}
for (unwrapped in unwrappedArguments) {
if (unwrapped == expression ||
unwrapped is FirArrayOfCall &&
unwrapped.arguments.any { arrayOfCallElement -> arrayOfCallElement.unwrapArgument() == expression }
) {
return@any true
}
}
return@any false
}
}
return false
}
private fun isInsideAnnotationClass(context: CheckerContext): Boolean {
for (declaration in context.containingDeclarations.asReversed()) {
if (declaration is FirRegularClass) {
if (declaration.isCompanion) {
continue
}
if (declaration.classKind == ClassKind.ANNOTATION_CLASS) {
return true
}
} else if (declaration is FirValueParameter || declaration is FirPrimaryConstructor) {
continue
}
break
}
return false
}
}
@@ -0,0 +1,18 @@
annotation class AnnE(val i: String)
enum class MyEnum {
A
}
@AnnE(<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>"1" + MyEnum.A<!>)
class Test
@AnnE(<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>"1" + MyEnum::class<!>)
class Test2
@AnnE(<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>"1" + AnnE("23")<!>)
class Test3
@AnnE(<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>"1" + <!UNSUPPORTED!>arrayOf("23", "34")<!><!>)
class Test4
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
annotation class AnnE(val i: String)
enum class MyEnum {
@@ -1,48 +0,0 @@
// LANGUAGE: +ProhibitArrayLiteralsInCompanionOfAnnotation
// ISSUE: KT-39041
annotation class Ann(val x: IntArray = [1, 2, 3]) { // OK
companion object {
val y1: IntArray = [1, 2, 3] // Error
val z1: IntArray
get() = [1, 2, 3] // Error
fun test_1(): IntArray {
return [1, 2, 3] // Error
}
class Nested {
val y2: IntArray = [1, 2, 3] // Error
val z2: IntArray
get() = [1, 2, 3] // Error
fun test_2(): IntArray {
return [1, 2, 3] // Error
}
}
}
object Foo {
val y3: IntArray = [1, 2, 3] // Error
val z3: IntArray
get() = [1, 2, 3] // Error
fun test_3(): IntArray {
return [1, 2, 3] // Error
}
}
class Nested {
val y4: IntArray = [1, 2, 3] // Error
val z4: IntArray
get() = [1, 2, 3] // Error
fun test_4(): IntArray {
return [1, 2, 3] // Error
}
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// LANGUAGE: +ProhibitArrayLiteralsInCompanionOfAnnotation
// ISSUE: KT-39041
@@ -3,46 +3,46 @@
annotation class Ann(val x: IntArray = [1, 2, 3]) { // OK
companion object {
val y1: IntArray = [1, 2, 3] // Error
val y1: IntArray = <!UNSUPPORTED!>[1, 2, 3]<!> // Error
val z1: IntArray
get() = [1, 2, 3] // Error
get() = <!UNSUPPORTED!>[1, 2, 3]<!> // Error
fun test_1(): IntArray {
return [1, 2, 3] // Error
return <!UNSUPPORTED!>[1, 2, 3]<!> // Error
}
class Nested {
val y2: IntArray = [1, 2, 3] // Error
val y2: IntArray = <!UNSUPPORTED!>[1, 2, 3]<!> // Error
val z2: IntArray
get() = [1, 2, 3] // Error
get() = <!UNSUPPORTED!>[1, 2, 3]<!> // Error
fun test_2(): IntArray {
return [1, 2, 3] // Error
return <!UNSUPPORTED!>[1, 2, 3]<!> // Error
}
}
}
object Foo {
val y3: IntArray = [1, 2, 3] // Error
val y3: IntArray = <!UNSUPPORTED!>[1, 2, 3]<!> // Error
val z3: IntArray
get() = [1, 2, 3] // Error
get() = <!UNSUPPORTED!>[1, 2, 3]<!> // Error
fun test_3(): IntArray {
return [1, 2, 3] // Error
return <!UNSUPPORTED!>[1, 2, 3]<!> // Error
}
}
class Nested {
val y4: IntArray = [1, 2, 3] // Error
val y4: IntArray = <!UNSUPPORTED!>[1, 2, 3]<!> // Error
val z4: IntArray
get() = [1, 2, 3] // Error
get() = <!UNSUPPORTED!>[1, 2, 3]<!> // Error
fun test_4(): IntArray {
return [1, 2, 3] // Error
return <!UNSUPPORTED!>[1, 2, 3]<!> // Error
}
}
}
}
@@ -1,21 +0,0 @@
// !LANGUAGE: +NewInference
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
fun takeArray(array: Array<String>) {}
fun test() {
"foo bar".<!UNRESOLVED_REFERENCE!>split<!>([""])
<!UNRESOLVED_REFERENCE!>unresolved<!>([""])
takeArray([""])
val v = [""]
[""]
[1, 2, 3].size
}
fun baz(arg: Array<Int> = []) {
if (true) ["yes"] else {["no"]}
}
class Foo(
val v: Array<Int> = []
)
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !LANGUAGE: +NewInference
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
@@ -1,15 +1,15 @@
fun test(): Array<Int> {
[1, 2]
[1, 2][0]
[1, 2].get(0)
<!UNSUPPORTED!>[1, 2]<!>
<!UNSUPPORTED!>[1, 2]<!>[0]
<!UNSUPPORTED!>[1, 2]<!>.get(0)
foo([""])
foo(<!UNSUPPORTED!>[""]<!>)
val p = [1, 2] <!UNRESOLVED_REFERENCE!>+<!> [3, 4]
val p = <!UNSUPPORTED!>[1, 2]<!> <!UNRESOLVED_REFERENCE!>+<!> <!UNSUPPORTED!>[3, 4]<!>
return [1, 2]
return <!UNSUPPORTED!>[1, 2]<!>
}
fun foo(a: Array<String> = [""]) {}
fun foo(a: Array<String> = <!UNSUPPORTED!>[""]<!>) {}
class A(val a: Array<Int> = [])
class A(val a: Array<Int> = <!UNSUPPORTED!>[]<!>)
@@ -3,5 +3,5 @@
fun foo(l: () -> Unit) {}
fun bar(l: () -> String) {}
val a = foo { [] }
val b = bar { [] }
val a = foo { <!UNSUPPORTED!>[]<!> }
val b = bar { <!UNSUPPORTED!>[]<!> }
@@ -6,7 +6,7 @@ annotation class Ann(val i: IntArray)
@Ann(intArrayOf(i2))
@Ann(<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>intArrayOf(<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>i3<!>)<!>)
@Ann(<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>intArrayOf(<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>i<!>, i2, <!ANNOTATION_ARGUMENT_MUST_BE_CONST!>i3<!>)<!>)
@Ann(<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>intArrayOf(<!ARGUMENT_TYPE_MISMATCH, NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>intArrayOf(<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>i<!>, i2, <!ANNOTATION_ARGUMENT_MUST_BE_CONST!>i3<!>)<!>)<!>)
@Ann(<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>intArrayOf(<!ARGUMENT_TYPE_MISMATCH, NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION, UNSUPPORTED!>intArrayOf(<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>i<!>, i2, <!ANNOTATION_ARGUMENT_MUST_BE_CONST!>i3<!>)<!>)<!>)
class Test
var i = 1