提交 cfc1ebb4 编写于 作者: Л Лихолетов Михаил 提交者: Mikhail Glukhikh

[FIR] Support several annotation argument diagnostics

上级 0c13d319
import kotlin.reflect.KClass
annotation class Ann(val a: Array<KClass<*>>)
inline val <reified T> T.test
get() = @Ann(
<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>arrayOf(
<!ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR!>T::class<!>,
<!ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR!>Array<Array<Array<Array<T>>>>::class<!>
)<!>
) object {}
FILE: annotationArgumentKClassLiteralTypeError.kt
public final annotation class Ann : R|kotlin/Annotation| {
public constructor(a: R|kotlin/Array<kotlin/reflect/KClass<*>>|): R|Ann| {
super<R|kotlin/Any|>()
}
public final val a: R|kotlin/Array<kotlin/reflect/KClass<*>>| = R|<local>/a|
public get(): R|kotlin/Array<kotlin/reflect/KClass<*>>|
}
public final val <reified T> R|T|.test: R|<anonymous><T>|
public get(): R|<anonymous><T>| {
^ @R|Ann|(<implicitArrayOf>(<getClass>(R|T|), <getClass>(Q|kotlin/Array|))) object : R|kotlin/Any| {
private constructor(): R|<anonymous><T>| {
super<R|kotlin/Any|>()
}
}
}
annotation class Ann1(vararg val a: String)
annotation class Ann2(val a: IntArray)
annotation class Ann3(val a: Array<String>)
var foo = "a"
var bar = 1
fun baz() = 2
val arr = arrayOf("a", "b")
val two = 2
const val cnst = 3
class Class {
val a = 1
}
@Ann1(
<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>foo<!>,
<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>foo + bar<!>,
<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>"$foo $bar"<!>,
<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>"${baz()} "<!>
)
@Ann2(
<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>intArrayOf(
<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>bar<!>,
<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>baz()<!>,
<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>bar + cnst<!>
)<!>
)
@Ann3(<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>arr<!>)
fun test() {}
\ No newline at end of file
FILE: annotationArgumentMustBeConst.kt
public final annotation class Ann1 : R|kotlin/Annotation| {
public constructor(vararg a: R|kotlin/Array<out kotlin/String>|): R|Ann1| {
super<R|kotlin/Any|>()
}
public final val a: R|kotlin/Array<out kotlin/String>| = R|<local>/a|
public get(): R|kotlin/Array<out kotlin/String>|
}
public final annotation class Ann2 : R|kotlin/Annotation| {
public constructor(a: R|kotlin/IntArray|): R|Ann2| {
super<R|kotlin/Any|>()
}
public final val a: R|kotlin/IntArray| = R|<local>/a|
public get(): R|kotlin/IntArray|
}
public final annotation class Ann3 : R|kotlin/Annotation| {
public constructor(a: R|kotlin/Array<kotlin/String>|): R|Ann3| {
super<R|kotlin/Any|>()
}
public final val a: R|kotlin/Array<kotlin/String>| = R|<local>/a|
public get(): R|kotlin/Array<kotlin/String>|
}
public final var foo: R|kotlin/String| = String(a)
public get(): R|kotlin/String|
public set(value: R|kotlin/String|): R|kotlin/Unit|
public final var bar: R|kotlin/Int| = Int(1)
public get(): R|kotlin/Int|
public set(value: R|kotlin/Int|): R|kotlin/Unit|
public final fun baz(): R|kotlin/Int| {
^baz Int(2)
}
public final val arr: R|kotlin/Array<kotlin/String>| = R|kotlin/arrayOf|<R|kotlin/String|>(vararg(String(a), String(b)))
public get(): R|kotlin/Array<kotlin/String>|
public final val two: R|kotlin/Int| = Int(2)
public get(): R|kotlin/Int|
public final const val cnst: R|kotlin/Int| = Int(3)
public get(): R|kotlin/Int|
public final class Class : R|kotlin/Any| {
public constructor(): R|Class| {
super<R|kotlin/Any|>()
}
public final val a: R|kotlin/Int| = Int(1)
public get(): R|kotlin/Int|
}
@R|Ann1|(vararg(R|/foo|, R|/foo|.R|kotlin/String.plus|(R|/bar|), <strcat>(R|/foo|.R|kotlin/Any.toString|(), String( ), R|/bar|.R|kotlin/Any.toString|()), <strcat>(R|/baz|().R|kotlin/Any.toString|(), String( )))) @R|Ann2|(<implicitArrayOf>(R|/bar|, R|/baz|(), R|/bar|.R|kotlin/Int.plus|(R|/cnst|))) @R|Ann3|(R|/arr|) public final fun test(): R|kotlin/Unit| {
}
enum class TestEnum {
Foo
}
annotation class Ann(vararg val a: TestEnum)
val foo = TestEnum.Foo
var bar = TestEnum.Foo
@Ann(<!ANNOTATION_ARGUMENT_MUST_BE_ENUM_CONST!>foo<!>, <!ANNOTATION_ARGUMENT_MUST_BE_ENUM_CONST!>bar<!>)
fun test() {}
\ No newline at end of file
FILE: annotationArgumentMustBeEnumConst.kt
public final enum class TestEnum : R|kotlin/Enum<TestEnum>| {
private constructor(): R|TestEnum| {
super<R|kotlin/Enum<TestEnum>|>()
}
public final static enum entry Foo: R|TestEnum|
public final static fun values(): R|kotlin/Array<TestEnum>| {
}
public final static fun valueOf(value: R|kotlin/String|): R|TestEnum| {
}
}
public final annotation class Ann : R|kotlin/Annotation| {
public constructor(vararg a: R|kotlin/Array<out TestEnum>|): R|Ann| {
super<R|kotlin/Any|>()
}
public final val a: R|kotlin/Array<out TestEnum>| = R|<local>/a|
public get(): R|kotlin/Array<out TestEnum>|
}
public final val foo: R|TestEnum| = Q|TestEnum|.R|/TestEnum.Foo|
public get(): R|TestEnum|
public final var bar: R|TestEnum| = Q|TestEnum|.R|/TestEnum.Foo|
public get(): R|TestEnum|
public set(value: R|TestEnum|): R|kotlin/Unit|
@R|Ann|(vararg(R|/foo|, R|/bar|)) public final fun test(): R|kotlin/Unit| {
}
import kotlin.reflect.KClass
@Repeatable
annotation class Ann(val a: Array<KClass<*>>)
class Foo
val foo = Foo::class
fun bar() = Foo::class
@Ann(
[
<!ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL!>""::class<!>,
<!ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL!>true::class<!>,
<!ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL!>1::class<!>
]
)
@Ann(
[
<!ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL!>foo<!>,
<!ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL!>bar()<!>
]
)
fun test1() {}
\ No newline at end of file
FILE: annotationArgumentMustBeKClassLiteral.kt
@R|kotlin/annotation/Repeatable|() public final annotation class Ann : R|kotlin/Annotation| {
public constructor(a: R|kotlin/Array<kotlin/reflect/KClass<*>>|): R|Ann| {
super<R|kotlin/Any|>()
}
public final val a: R|kotlin/Array<kotlin/reflect/KClass<*>>| = R|<local>/a|
public get(): R|kotlin/Array<kotlin/reflect/KClass<*>>|
}
public final class Foo : R|kotlin/Any| {
public constructor(): R|Foo| {
super<R|kotlin/Any|>()
}
}
public final val foo: R|kotlin/reflect/KClass<Foo>| = <getClass>(Q|Foo|)
public get(): R|kotlin/reflect/KClass<Foo>|
public final fun bar(): R|kotlin/reflect/KClass<Foo>| {
^bar <getClass>(Q|Foo|)
}
@R|Ann|(<implicitArrayOf>(<getClass>(String()), <getClass>(Boolean(true)), <getClass>(Int(1)))) @R|Ann|(<implicitArrayOf>(R|/foo|, R|/bar|())) public final fun test1(): R|kotlin/Unit| {
}
annotation class Ann(val a: Array<String>)
val foo = ""
var bar = 1
const val cnst = 2
@Ann(
<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>arrayOf(
<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>foo<!>,
<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>foo + cnst.toString()<!>
)<!>
)
fun test() {}
\ No newline at end of file
FILE: nonConstValInAnnotationArgument.kt
public final annotation class Ann : R|kotlin/Annotation| {
public constructor(a: R|kotlin/Array<kotlin/String>|): R|Ann| {
super<R|kotlin/Any|>()
}
public final val a: R|kotlin/Array<kotlin/String>| = R|<local>/a|
public get(): R|kotlin/Array<kotlin/String>|
}
public final val foo: R|kotlin/String| = String()
public get(): R|kotlin/String|
public final var bar: R|kotlin/Int| = Int(1)
public get(): R|kotlin/Int|
public set(value: R|kotlin/Int|): R|kotlin/Unit|
public final const val cnst: R|kotlin/Int| = Int(2)
public get(): R|kotlin/Int|
@R|Ann|(<implicitArrayOf>(R|/foo|, R|/foo|.R|kotlin/String.plus|(R|/cnst|.R|kotlin/Any.toString|()))) public final fun test(): R|kotlin/Unit| {
}
......@@ -916,6 +916,26 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/diagnostics"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@TestMetadata("annotationArgumentKClassLiteralTypeError.kt")
public void testAnnotationArgumentKClassLiteralTypeError() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/annotationArgumentKClassLiteralTypeError.kt");
}
@TestMetadata("annotationArgumentMustBeConst.kt")
public void testAnnotationArgumentMustBeConst() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/annotationArgumentMustBeConst.kt");
}
@TestMetadata("annotationArgumentMustBeEnumConst.kt")
public void testAnnotationArgumentMustBeEnumConst() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/annotationArgumentMustBeEnumConst.kt");
}
@TestMetadata("annotationArgumentMustBeKClassLiteral.kt")
public void testAnnotationArgumentMustBeKClassLiteral() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/annotationArgumentMustBeKClassLiteral.kt");
}
@TestMetadata("annotationClassMember.kt")
public void testAnnotationClassMember() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/annotationClassMember.kt");
......@@ -1011,6 +1031,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/methodOfAnyImplementedInInterface.kt");
}
@TestMetadata("nonConstValInAnnotationArgument.kt")
public void testNonConstValInAnnotationArgument() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/nonConstValInAnnotationArgument.kt");
}
@TestMetadata("notASupertype.kt")
public void testNotASupertype() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/notASupertype.kt");
......
......@@ -916,6 +916,26 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/diagnostics"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@TestMetadata("annotationArgumentKClassLiteralTypeError.kt")
public void testAnnotationArgumentKClassLiteralTypeError() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/annotationArgumentKClassLiteralTypeError.kt");
}
@TestMetadata("annotationArgumentMustBeConst.kt")
public void testAnnotationArgumentMustBeConst() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/annotationArgumentMustBeConst.kt");
}
@TestMetadata("annotationArgumentMustBeEnumConst.kt")
public void testAnnotationArgumentMustBeEnumConst() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/annotationArgumentMustBeEnumConst.kt");
}
@TestMetadata("annotationArgumentMustBeKClassLiteral.kt")
public void testAnnotationArgumentMustBeKClassLiteral() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/annotationArgumentMustBeKClassLiteral.kt");
}
@TestMetadata("annotationClassMember.kt")
public void testAnnotationClassMember() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/annotationClassMember.kt");
......@@ -1011,6 +1031,11 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/methodOfAnyImplementedInInterface.kt");
}
@TestMetadata("nonConstValInAnnotationArgument.kt")
public void testNonConstValInAnnotationArgument() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/nonConstValInAnnotationArgument.kt");
}
@TestMetadata("notASupertype.kt")
public void testNotASupertype() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/notASupertype.kt");
......
/*
* Copyright 2010-2020 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.declaration
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticFactory0
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.scopes.impl.FirIntegerOperatorCall
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.util.OperatorNameConventions.BINARY_OPERATION_NAMES
import org.jetbrains.kotlin.util.OperatorNameConventions.PLUS
import org.jetbrains.kotlin.util.OperatorNameConventions.TO_STRING
import org.jetbrains.kotlin.util.OperatorNameConventions.UNARY_OPERATION_NAMES
object FirAnnotationArgumentChecker : FirBasicDeclarationChecker() {
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration !is FirAnnotationContainer) return
for (declarationOfAnnotation in declaration.annotations) {
for ((arg, _) in declarationOfAnnotation.argumentMapping ?: continue) {
val expression = (arg as? FirNamedArgumentExpression)?.expression ?: arg
checkAnnotationArgumentWithSubElements(expression, context.session, reporter)
?.let { reporter.report(getFirSourceElement(expression), it) }
}
}
}
private fun checkAnnotationArgumentWithSubElements(
expression: FirExpression,
session: FirSession,
reporter: DiagnosticReporter
): FirDiagnosticFactory0<FirSourceElement, KtExpression>? {
when (expression) {
is FirArrayOfCall -> {
var usedNonConst = false
for (arg in expression.argumentList.arguments) {
val sourceForReport = getFirSourceElement(arg)
when (val err = checkAnnotationArgumentWithSubElements(arg, session, reporter)) {
null -> {
//DO NOTHING
}
else -> {
if (err != FirErrors.ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL) usedNonConst = true
reporter.report(sourceForReport, err)
}
}
}
if (usedNonConst) return FirErrors.NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION
}
is FirVarargArgumentsExpression -> {
for (arg in expression.arguments)
checkAnnotationArgumentWithSubElements(arg, session, reporter)
?.let { reporter.report(getFirSourceElement(arg), it) }
}
else ->
return checkAnnotationArgument(expression, session)
}
return null
}
private fun checkAnnotationArgument(
expression: FirExpression,
session: FirSession,
): FirDiagnosticFactory0<FirSourceElement, KtExpression>? {
val expressionSymbol = expression.toResolvedCallableSymbol()
?.fir
val classKindOfParent = (expressionSymbol
?.getReferencedClass(session) as? FirRegularClass)
?.classKind
when {
expression is FirConstExpression<*>
|| expressionSymbol is FirEnumEntry
|| (expressionSymbol as? FirMemberDeclaration)?.isConst == true
|| expressionSymbol is FirConstructor && classKindOfParent == ClassKind.ANNOTATION_CLASS -> {
//DO NOTHING
}
classKindOfParent == ClassKind.ENUM_CLASS -> {
return FirErrors.ANNOTATION_ARGUMENT_MUST_BE_ENUM_CONST
}
expression is FirComparisonExpression -> {
return checkAnnotationArgument(expression.compareToCall, session)
}
expression is FirIntegerOperatorCall -> {
for (exp in (expression as FirCall).arguments.plus(expression.dispatchReceiver))
checkAnnotationArgument(exp, session).let { return it }
}
expression is FirStringConcatenationCall || expression is FirEqualityOperatorCall -> {
for (exp in (expression as FirCall).arguments)
checkAnnotationArgument(exp, session).let { return it }
}
(expression is FirGetClassCall) -> {
var coneType = (expression as? FirCall)
?.argument
?.typeRef
?.coneType
if (coneType is ConeClassErrorType)
return FirErrors.ANNOTATION_ARGUMENT_MUST_BE_CONST
while (coneType?.classId == StandardClassIds.Array)
coneType = (coneType.lowerBoundIfFlexible().typeArguments.first() as? ConeKotlinTypeProjection)?.type ?: break
return when {
coneType is ConeTypeParameterType ->
FirErrors.ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR
(expression as FirCall).argument !is FirResolvedQualifier ->
FirErrors.ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL
else ->
null
}
}
expressionSymbol == null -> {
//DO NOTHING
}
expressionSymbol is FirField -> {
//TODO: fix checking of Java fields initializer
if (
!(expressionSymbol as FirMemberDeclaration).status.isStatic
|| (expressionSymbol as FirMemberDeclaration).status.modality != Modality.FINAL
)
return FirErrors.ANNOTATION_ARGUMENT_MUST_BE_CONST
}
expression is FirFunctionCall -> {
val calleeReference = expression.calleeReference
if (calleeReference is FirErrorNamedReference)
return null
if (expression.typeRef.coneType.classId == StandardClassIds.KClass)
return FirErrors.ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL
//TODO: UNRESOLVED REFERENCE
if (expression.dispatchReceiver is FirThisReceiverExpression)
return null
when (calleeReference.name) {
TO_STRING ->
return checkAnnotationArgument(expression.dispatchReceiver, session)
in BINARY_OPERATION_NAMES, in UNARY_OPERATION_NAMES -> {
val receiverClassId = expression.dispatchReceiver.typeRef.coneType.classId
for (exp in (expression as FirCall).arguments.plus(expression.dispatchReceiver)) {
val expClassId = exp.typeRef.coneType.classId
if (calleeReference.name == PLUS
&& expClassId != receiverClassId
&& (expClassId !in StandardClassIds.primitiveTypes || receiverClassId !in StandardClassIds.primitiveTypes)
)
return FirErrors.ANNOTATION_ARGUMENT_MUST_BE_CONST
checkAnnotationArgument(exp, session)?.let { return it }
}
}
else ->
return FirErrors.ANNOTATION_ARGUMENT_MUST_BE_CONST
}
}
expression is FirQualifiedAccessExpression -> {
when {
(expressionSymbol as FirProperty).isLocal || expressionSymbol.symbol.callableId.className?.isRoot == false ->
return FirErrors.ANNOTATION_ARGUMENT_MUST_BE_CONST
expression.typeRef.coneType.classId == StandardClassIds.KClass ->
return FirErrors.ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL
//TODO: UNRESOLVED REFERENCE
expression.dispatchReceiver is FirThisReceiverExpression ->
return null
}
return when ((expressionSymbol as? FirProperty)?.initializer) {
is FirConstExpression<*> -> {
if ((expressionSymbol as? FirVariable)?.isVal == true)
FirErrors.NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION
else
FirErrors.ANNOTATION_ARGUMENT_MUST_BE_CONST
}
is FirGetClassCall ->
FirErrors.ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL
else ->
FirErrors.ANNOTATION_ARGUMENT_MUST_BE_CONST
}
}
else ->
return FirErrors.ANNOTATION_ARGUMENT_MUST_BE_CONST
}
return null
}
private fun FirTypedDeclaration?.getReferencedClass(session: FirSession): FirSymbolOwner<*>? =
this?.returnTypeRef
?.coneTypeSafe<ConeLookupTagBasedType>()
?.lookupTag
?.toSymbol(session)
?.fir
private fun getFirSourceElement(expression: FirExpression): FirSourceElement? =
when {
expression is FirFunctionCall && expression.calleeReference.name == TO_STRING ->
getParentOfFirSourceElement(getParentOfFirSourceElement(expression.source))
expression is FirFunctionCall ->
expression.source
(expression as? FirQualifiedAccess)?.explicitReceiver != null ->
getParentOfFirSourceElement(expression.source)
else ->
expression.source
}
private fun getParentOfFirSourceElement(source: FirSourceElement?): FirSourceElement? =
when (source) {
is FirPsiSourceElement<*> ->
source.psi.parent.toFirPsiSourceElement()
is FirLightSourceElement -> {
val elementOfParent = source.tree.getParent(source.element)
?: source.element
elementOfParent.toFirLightSourceElement(elementOfParent.startOffset, elementOfParent.endOffset, source.tree)
}
else ->
source
}
private inline fun <reified T : FirSourceElement, P : PsiElement> DiagnosticReporter.report(
source: T?,
factory: FirDiagnosticFactory0<T, P>
) {
source?.let { report(factory.on(it)) }
}
}
\ No newline at end of file
......@@ -80,14 +80,19 @@ object FirErrors {
val SEALED_CLASS_CONSTRUCTOR_CALL by error0<FirSourceElement, PsiElement>()
// Annotations
val ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR by existing<FirSourceElement, KtExpression>(Errors.ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR)
val ANNOTATION_ARGUMENT_MUST_BE_CONST by existing<FirSourceElement, KtExpression>(Errors.ANNOTATION_ARGUMENT_MUST_BE_CONST)
val ANNOTATION_ARGUMENT_MUST_BE_ENUM_CONST by existing<FirSourceElement, KtExpression>(Errors.ANNOTATION_ARGUMENT_MUST_BE_ENUM_CONST)
val ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL by existing<FirSourceElement, KtExpression>(Errors.ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL)
val ANNOTATION_CLASS_MEMBER by existing<FirSourceElement, PsiElement>(Errors.ANNOTATION_CLASS_MEMBER)
val ANNOTATION_PARAMETER_DEFAULT_VALUE_MUST_BE_CONSTANT by existing<FirSourceElement, KtExpression>(Errors.ANNOTATION_PARAMETER_DEFAULT_VALUE_MUST_BE_CONSTANT)
val INVALID_TYPE_OF_ANNOTATION_MEMBER by existing<FirSourceElement, KtTypeReference>(Errors.INVALID_TYPE_OF_ANNOTATION_MEMBER)
val LOCAL_ANNOTATION_CLASS_ERROR by existing<FirSourceElement, KtClassOrObject>(Errors.LOCAL_ANNOTATION_CLASS_ERROR)
val MISSING_VAL_ON_ANNOTATION_PARAMETER by existing<FirSourceElement, KtParameter>(Errors.MISSING_VAL_ON_ANNOTATION_PARAMETER)
val NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION by existing<FirSourceElement, KtExpression>(Errors.NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION)
val NOT_AN_ANNOTATION_CLASS by error1<FirSourceElement, PsiElement, String>()
val NULLABLE_TYPE_OF_ANNOTATION_MEMBER by existing<FirSourceElement, KtTypeReference>(Errors.NULLABLE_TYPE_OF_ANNOTATION_MEMBER)
val INVALID_TYPE_OF_ANNOTATION_MEMBER by existing<FirSourceElement, KtTypeReference>(Errors.INVALID_TYPE_OF_ANNOTATION_MEMBER)
val VAR_ANNOTATION_PARAMETER by existing<FirSourceElement, KtParameter>(Errors.VAR_ANNOTATION_PARAMETER)
val NOT_AN_ANNOTATION_CLASS by error1<FirSourceElement, PsiElement, String>()
// Exposed visibility group
val EXPOSED_TYPEALIAS_EXPANDED_TYPE by error3<FirSourceElement, PsiElement, FirEffectiveVisibility, FirMemberDeclaration, FirEffectiveVisibility>()
......
......@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.fir.analysis.checkers.declaration.*
object CommonDeclarationCheckers : DeclarationCheckers() {
override val basicDeclarationCheckers: Set<FirBasicDeclarationChecker> = setOf(
FirAnnotationArgumentChecker,
FirAnnotationClassDeclarationChecker,
FirModifierChecker,
FirManyCompanionObjectsChecker,
......
// Functions can be recursively annotated
annotation class ann(val x: Int)
@ann(bar()) fun foo() = 1
@ann(foo()) fun bar() = 2
\ No newline at end of file
// FIR_IDENTICAL
// Functions can be recursively annotated
annotation class ann(val x: Int)
@ann(<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>bar()<!>) fun foo() = 1
......
// Function parameter CAN be recursively annotated
annotation class ann(val x: Int)
fun foo(@ann(foo(1)) x: Int): Int = x
\ No newline at end of file
// FIR_IDENTICAL
// Function parameter CAN be recursively annotated
annotation class ann(val x: Int)
fun foo(@ann(<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>foo(1)<!>) x: Int): Int = x
\ No newline at end of file
// Functions can be recursively annotated
annotation class ann(val x: Int)
@ann(foo()) fun foo() = 1
\ No newline at end of file
// FIR_IDENTICAL
// Functions can be recursively annotated
annotation class ann(val x: Int)
@ann(<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>foo()<!>) fun foo() = 1
\ No newline at end of file
// Properties can be recursively annotated
annotation class ann(val x: Int)
class My {
@ann(x) val x: Int = 1
}
\ No newline at end of file
// FIR_IDENTICAL
// Properties can be recursively annotated
annotation class ann(val x: Int)
class My {
......
......@@ -2,5 +2,5 @@ annotation class Ann(vararg val i: Boolean)
fun foo() {
val bool1 = true
@Ann(bool1) val a = bool1
@Ann(<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>bool1<!>) val a = bool1
}
\ No newline at end of file
// !DIAGNOSTICS: -UNUSED_VARIABLE
annotation class Ann(vararg val i: Boolean)
fun foo() {
val a1 = 1 > 2
val a2 = 1 == 2
val a3 = a1 == a2
val a4 = a1 > a2
@Ann(
a1,
a2,
a3,
a1 > a2,
a1 == a2
) val b = 1
}
\ No newline at end of file
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_VARIABLE
annotation class Ann(vararg val i: Boolean)
fun foo() {
......
......@@ -2,7 +2,7 @@
annotation class AnnE(val i: MyEnum)
@AnnE(e)
@AnnE(<!ANNOTATION_ARGUMENT_MUST_BE_ENUM_CONST!>e<!>)
class Test
val e: MyEnum = MyEnum.A
......
......@@ -2,7 +2,7 @@
annotation class AnnE(val i: MyEnum)
@AnnE(e)
@AnnE(<!ANNOTATION_ARGUMENT_MUST_BE_ENUM_CONST!>e<!>)
class Test
val e: MyEnum = MyEnum.A
......
......@@ -14,12 +14,12 @@ public class Test {
annotation class Ann(vararg val i: Int)
@Ann(
Test.i1,
<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>Test.i1<!>,
Test.i2,
Test.i3,
Test.i4,
Test.i5,
Test.i6,
Test().i7
<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>Test.i5<!>,
<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>Test.i6<!>,
<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>Test().i7<!>
)
class A
annotation class Ann(vararg val i: Int)
@Ann(
i1,
i2,
i3,
i4,
i5,
i6
)
class Test
var i1 = 1 // var
const val i2 = 1 // val
val i3 = i1 // val with var in initializer
const val i4 = i2 // val with val in initializer
var i5 = i1 // var with var in initializer
var i6 = i2 // var with val in initializer
annotation class AnnE(val i: String)
enum class MyEnum {
A
}
@AnnE("1" + MyEnum.A)
class Test
@AnnE("1" + MyEnum::class)
class Test2
@AnnE("1" + AnnE("23"))
class Test3
@AnnE("1" + arrayOf("23", "34"))
class Test4
// !DIAGNOSTICS: -UNUSED_VARIABLE
annotation class Ann(vararg val i: String)
const val topLevel = "topLevel"
fun foo() {
val a1 = "a"
val a2 = "b"
val a3 = a1 + a2
val a4 = 1
val a5 = 1.0
@Ann(
a1,
a2,
a3,
"$topLevel",
"$a1",
"$a1 $topLevel",
"$a4",
"$a5",
a1 + a2,
"a" + a2,
"a" + topLevel,
"a" + a4
) val b = 1
}
\ No newline at end of file
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_VARIABLE
annotation class Ann(vararg val i: String)
......
// !LANGUAGE: +ProhibitTypeParametersInClassLiteralsInAnnotationArguments
import kotlin.reflect.KClass
annotation class Ann(val k: KClass<*>)
annotation class AnnArray(val kk: Array<KClass<*>>)
object AnObject
class C {
companion object
}
fun foo() = "foo"
@Ann("foo"::class)
fun test1() {}
@Ann(String::class)
fun test2() {}
@Ann(AnObject::class)
fun test4() {}
@Ann(C::class)
fun test5() {}
@Ann(C.Companion::class)
fun test6() {}
@Ann(foo()::class)
fun test7() {}
@AnnArray(arrayOf(""::class, String::class, AnObject::class))
fun test8() {}
inline val <reified T> T.test9
get() = @AnnArray(arrayOf(
T::class,
Array<T>::class,
Array<Array<Array<T>>>::class
)) object {}
inline val <reified T> T.test10
get() = @AnnArray([T::class]) object {}
// FIR_IDENTICAL
// !LANGUAGE: +ProhibitTypeParametersInClassLiteralsInAnnotationArguments
import kotlin.reflect.KClass
......
......@@ -6,7 +6,7 @@ annotation class Ann(vararg val k: KClass<*>)
inline val <reified T> T.test
get() = @Ann(
T::class,
Array<T>::class,
Array<Array<Array<T>>>::class
<!ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR!>T::class<!>,
<!ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR!>Array<T>::class<!>,
<!ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR!>Array<Array<Array<T>>>::class<!>
) object {}
......@@ -24,8 +24,8 @@ val two = 2
@Foo([ONE], [], [])
fun test6() {}
@Foo([ONE + two], [], [])
@Foo(<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>[<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>ONE + two<!>]<!>, [], [])
fun test7() {}
@Foo([two], [], [])
@Foo(<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>[<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>two<!>]<!>, [], [])
fun test8() {}
\ No newline at end of file
......@@ -22,5 +22,5 @@ fun main1() {}
fun main2() {}
val q = A()
@Ann(q.z)
@Ann(<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>q.z<!>)
fun main3() {}
......@@ -22,7 +22,7 @@ annotation class Ann(
Companion.CONST,
Nested.CONST,
Interface.CONST,
a,
<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>a<!>,
b()
)
class A {
......
......@@ -23,7 +23,7 @@ class A
Companion.CONST,
Nested.CONST,
Interface.CONST,
a,
<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>a<!>,
b()
)
constructor() {
......@@ -36,7 +36,7 @@ constructor() {
Companion.CONST,
Nested.CONST,
Interface.CONST,
a,
<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>a<!>,
b()
)
constructor(dummy: Int) : this()
......
......@@ -2,10 +2,10 @@
@Repeatable
annotation class Ann(val i: IntArray)
@Ann(intArrayOf(i))
@Ann(<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>intArrayOf(<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>i<!>)<!>)
@Ann(intArrayOf(i2))
@Ann(intArrayOf(i3))
@Ann(intArrayOf(i, i2, i3))
@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(<!INAPPLICABLE_CANDIDATE!>intArrayOf<!>(intArrayOf(i, i2, i3)))
class Test
......@@ -19,6 +19,6 @@ fun foo(): Int = 1
@Repeatable
annotation class AnnAnn(val i: Array<Ann>)
@AnnAnn(arrayOf(Ann(intArrayOf(1))))
@AnnAnn(arrayOf(iAnn))
@AnnAnn(<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>arrayOf(<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>iAnn<!>)<!>)
class TestAnn
val iAnn = Ann(intArrayOf(1))
@Retention(AnnotationRetention.SOURCE)
@Repeatable
annotation class Ann(val i: Int)
annotation class AnnIA(val ia: IntArray)
annotation class AnnSA(val sa: Array<String>)
@Ann(MyClass().i)
@Ann(i)
@Ann(i2)
@AnnIA(ia)
@AnnSA(sa)
class Test {
val i = 1
@Ann(i) val i2 = 1
}
var i = 1
val i2 = foo()
fun foo(): Int = 1
@AnnSA(emptyArray())
class MyClass {
val i = 1
}
val ia: IntArray = intArrayOf(1, 2)
val sa: Array<String> = arrayOf("a", "b")
annotation class Ann2
// FIR_IDENTICAL
@Retention(AnnotationRetention.SOURCE)
@Repeatable
annotation class Ann(val i: Int)
......
......@@ -4,13 +4,13 @@ const val constConst = nonConst * nonConst + 2
annotation class Ann(val x: Int, val y: String)
@Ann(nonConst, "${nonConst}")
@Ann(<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>nonConst<!>, <!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>"${nonConst}"<!>)
fun foo1() {}
@Ann(nonConst + constConst, "${constConst}")
@Ann(<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>nonConst + constConst<!>, "${constConst}")
fun foo2() {}
annotation class ArrayAnn(val x: IntArray)
@ArrayAnn(intArrayOf(1, constConst, nonConst))
@ArrayAnn(<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>intArrayOf(1, constConst, <!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>nonConst<!>)<!>)
fun foo3() {}
......@@ -2,10 +2,10 @@
@Repeatable
annotation class Ann(vararg val i: Int)
@Ann(i)
@Ann(<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>i<!>)
@Ann(i2)
@Ann(i3)
@Ann(i, i2, i3)
@Ann(<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>i3<!>)
@Ann(<!ANNOTATION_ARGUMENT_MUST_BE_CONST!>i<!>, i2, <!ANNOTATION_ARGUMENT_MUST_BE_CONST!>i3<!>)
@Ann(*intArrayOf(i))
@Ann(*intArrayOf(i2))
@Ann(*intArrayOf(i3))
......
import kotlin.reflect.KClass
annotation class Ann1(val arg: KClass<*>)
annotation class Ann2(vararg val arg: KClass<*>)
annotation class Ann3(val arg: Array<KClass<*>>)
class A1
class A2
@Ann1(A1::class)
@Ann2(A1::class, A2::class)
@Ann3(arrayOf(A1::class, A2::class))
class MyClass1
@Ann1(<!UNRESOLVED_REFERENCE!>A3<!>::class)
class MyClass2
val x = A1::class
@Ann1(x)
class MyClass3
// FIR_IDENTICAL
import kotlin.reflect.KClass
annotation class Ann1(val arg: KClass<*>)
......
......@@ -33,6 +33,8 @@ object OperatorNameConventions {
@JvmField val NEXT = Name.identifier("next")
@JvmField val HAS_NEXT = Name.identifier("hasNext")
@JvmField val TO_STRING = Name.identifier("toString")
@JvmField val COMPONENT_REGEX = Regex("component\\d+")
@JvmField val AND = Name.identifier("and")
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册