From 334d0a8b5a0fda2b097d75026bc33fe26f9747ca Mon Sep 17 00:00:00 2001 From: zadorotskas Date: Fri, 5 Mar 2021 12:51:26 +0300 Subject: [PATCH] FIR: introduce three diagnostics for const val --- ...TouchedTilContractsPhaseTestGenerated.java | 5 + .../testData/resolve/constVal/const.fir.txt | 60 +++++ .../testData/resolve/constVal/const.kt | 36 +++ .../runners/FirDiagnosticTestGenerated.java | 6 + ...DiagnosticsWithLightTreeTestGenerated.java | 6 + .../diagnostics/FirDiagnosticsList.kt | 5 + .../fir/analysis/diagnostics/FirErrors.kt | 3 + .../fir/analysis/checkers/FirConstChecks.kt | 207 ++++++++++++++++++ .../FirAnnotationArgumentChecker.kt | 161 +------------- .../declaration/FirConstPropertyChecker.kt | 31 ++- .../diagnostics/FirDefaultErrorMessages.kt | 13 ++ ...ExtensionOverKotlinExtensionInConst.fir.kt | 7 - ...stomExtensionOverKotlinExtensionInConst.kt | 1 + .../tests/evaluate/divisionByZero.fir.kt | 2 +- .../modifiers/const/applicability.fir.kt | 30 +-- .../const/noDivisionByZeroFeature.fir.kt | 2 +- .../tests/modifiers/const/types.fir.kt | 17 -- .../tests/modifiers/const/types.kt | 1 + .../differentKindsOfProperties.fir.kt | 2 +- .../useOfNonConstVal.fir.kt | 2 +- .../diagnostics/KtFirDataClassConverters.kt | 19 ++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 13 ++ .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 22 ++ 23 files changed, 451 insertions(+), 200 deletions(-) create mode 100644 compiler/fir/analysis-tests/testData/resolve/constVal/const.fir.txt create mode 100644 compiler/fir/analysis-tests/testData/resolve/constVal/const.kt create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirConstChecks.kt delete mode 100644 compiler/testData/diagnostics/tests/evaluate/customExtensionOverKotlinExtensionInConst.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/modifiers/const/types.fir.kt diff --git a/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java b/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java index ac6e065508d..21b61937088 100644 --- a/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java +++ b/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java @@ -946,6 +946,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/constVal"), Pattern.compile("^([^.]+)\\.kt$"), null, true); } + @TestMetadata("const.kt") + public void testConst() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/constVal/const.kt"); + } + @TestMetadata("constValNotTopLevelOrObject.kt") public void testConstValNotTopLevelOrObject() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/constVal/constValNotTopLevelOrObject.kt"); diff --git a/compiler/fir/analysis-tests/testData/resolve/constVal/const.fir.txt b/compiler/fir/analysis-tests/testData/resolve/constVal/const.fir.txt new file mode 100644 index 00000000000..b68f82f6128 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/constVal/const.fir.txt @@ -0,0 +1,60 @@ +FILE: const.kt + public final const val a: R|kotlin/String| = String(something) + public get(): R|kotlin/String| + public final const val b: + public get(): + public final const val c: R|kotlin/Nothing?| = Null(null) + public get(): R|kotlin/Nothing?| + public final const val d: R|ForConst.Companion| = Q|ForConst| + public get(): R|ForConst.Companion| + public final const val e: R|kotlin/String| = Q|ForConst|.R|/ForConst.Companion.one|() + public get(): R|kotlin/String| + public final const val f: R|kotlin/Int| = Int(1).R|kotlin/Int.plus|(Int(2)).R|kotlin/Int.times|(Int(3)).R|kotlin/Int.div|(Int(4)).R|kotlin/Int.rem|(Int(5)).R|kotlin/Int.minus|(Int(1)) + public get(): R|kotlin/Int| + public final const val g: R|kotlin/String| = (String(string ), R|/f|.R|kotlin/Any.toString|()) + public get(): R|kotlin/String| + public final const val h: R|kotlin/String| = String(string).R|kotlin/String.plus|(R|/g|) + public get(): R|kotlin/String| + public final const val i: R|kotlin/String| = Q|ForConst|.R|/ForConst.Companion.one|().R|kotlin/String.plus|(String(one)) + public get(): R|kotlin/String| + public final const val j: R|kotlin/Int| = Int(4).R|kotlin/Int.times|(Q|ForConst|.R|/ForConst.Companion.two|()) + public get(): R|kotlin/Int| + public final val k: R|kotlin/Int| = Int(3).R|kotlin/Int.minus|(Q|ForConst|.R|/ForConst.Companion.two|()) + public get(): R|kotlin/Int| + public final const val l: R|kotlin/Int| = R|/k| + public get(): R|kotlin/Int| + public final class ForConst : R|kotlin/Any| { + public constructor(): R|ForConst| { + super() + } + + public final companion object Companion : R|kotlin/Any| { + private constructor(): R|ForConst.Companion| { + super() + } + + public final fun one(): R|kotlin/String| { + ^one String(1) + } + + public final fun two(): R|kotlin/Int| { + ^two Int(2) + } + + } + + } + private final const val MAJOR_BITS: R|kotlin/Int| = Int(3) + private get(): R|kotlin/Int| + private final const val MINOR_BITS: R|kotlin/Int| = Int(4) + private get(): R|kotlin/Int| + private final const val PATCH_BITS: R|kotlin/Int| = Int(7) + private get(): R|kotlin/Int| + private final const val MAJOR_MASK: R|kotlin/Int| = Int(1).R|kotlin/Int.shl|(R|/MAJOR_BITS|).R|kotlin/Int.minus|(Int(1)) + private get(): R|kotlin/Int| + private final const val MINOR_MASK: R|kotlin/Int| = Int(1).R|kotlin/Int.shl|(R|/MINOR_BITS|).R|kotlin/Int.minus|(Int(1)) + private get(): R|kotlin/Int| + private final const val PATCH_MASK: R|kotlin/Int| = Int(1).R|kotlin/Int.shl|(R|/PATCH_BITS|).R|kotlin/Int.minus|(Int(1)) + private get(): R|kotlin/Int| + private final const val stringFromJava: R|kotlin/String| = Q|Constants|.R|/Constants.FIRST|.R|kotlin/String.plus|(String(+)).R|kotlin/String.plus|(Q|Constants|.R|/Constants.SECOND|) + private get(): R|kotlin/String| diff --git a/compiler/fir/analysis-tests/testData/resolve/constVal/const.kt b/compiler/fir/analysis-tests/testData/resolve/constVal/const.kt new file mode 100644 index 00000000000..21cf60e5466 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/constVal/const.kt @@ -0,0 +1,36 @@ +// FILE: Constants.java + +public class Constants { + public static final String FIRST = "1st"; + public static final String SECOND = "2nd"; +} + +// FILE: const.kt +const val a = "something" +const val b +const val c = null +const val d = ForConst +const val e = ForConst.one() +const val f = ((1 + 2) * 3) / 4 % 5 - 1 +const val g = "string $f" +const val h = "string" + g +const val i = ForConst.one() + "one" +const val j = 4 * ForConst.two() +val k = 3 - ForConst.two() +const val l = k + +class ForConst{ + companion object { + fun one(): String = "1" + fun two(): Int = 2 + } +} + +private const val MAJOR_BITS = 3 +private const val MINOR_BITS = 4 +private const val PATCH_BITS = 7 +private const val MAJOR_MASK = (1 shl MAJOR_BITS) - 1 // False positive error +private const val MINOR_MASK = (1 shl MINOR_BITS) - 1 // False positive error +private const val PATCH_MASK = (1 shl PATCH_BITS) - 1 // False positive error + +private const val stringFromJava = Constants.FIRST + "+" + Constants.SECOND \ No newline at end of file diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java index 48fd47a05da..98af733d5a3 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java @@ -1093,6 +1093,12 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/constVal"), Pattern.compile("^([^.]+)\\.kt$"), null, true); } + @Test + @TestMetadata("const.kt") + public void testConst() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/constVal/const.kt"); + } + @Test @TestMetadata("constValNotTopLevelOrObject.kt") public void testConstValNotTopLevelOrObject() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java index 4e8a00c2ae2..cadf68deae1 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java @@ -1102,6 +1102,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/constVal"), Pattern.compile("^([^.]+)\\.kt$"), null, true); } + @Test + @TestMetadata("const.kt") + public void testConst() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/constVal/const.kt"); + } + @Test @TestMetadata("constValNotTopLevelOrObject.kt") public void testConstValNotTopLevelOrObject() throws Exception { diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 29fbda69899..dddff64f8a0 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -496,6 +496,11 @@ object DIAGNOSTICS_LIST : DiagnosticList() { val CONST_VAL_NOT_TOP_LEVEL_OR_OBJECT by error(PositioningStrategy.CONST_MODIFIER) val CONST_VAL_WITH_GETTER by error() val CONST_VAL_WITH_DELEGATE by error() + val TYPE_CANT_BE_USED_FOR_CONST_VAL by error(PositioningStrategy.CONST_MODIFIER) { + parameter("constValType") + } + val CONST_VAL_WITHOUT_INITIALIZER by error(PositioningStrategy.CONST_MODIFIER) + val CONST_VAL_WITH_NON_CONST_INITIALIZER by error() val WRONG_SETTER_PARAMETER_TYPE by error { parameter("expectedType") parameter("actualType") diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index a31d87df42a..5cc33fb4b19 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -312,6 +312,9 @@ object FirErrors { val CONST_VAL_NOT_TOP_LEVEL_OR_OBJECT by error0(SourceElementPositioningStrategies.CONST_MODIFIER) val CONST_VAL_WITH_GETTER by error0() val CONST_VAL_WITH_DELEGATE by error0() + val TYPE_CANT_BE_USED_FOR_CONST_VAL by error1(SourceElementPositioningStrategies.CONST_MODIFIER) + val CONST_VAL_WITHOUT_INITIALIZER by error0(SourceElementPositioningStrategies.CONST_MODIFIER) + val CONST_VAL_WITH_NON_CONST_INITIALIZER by error0() val WRONG_SETTER_PARAMETER_TYPE by error2() val INITIALIZER_TYPE_MISMATCH by error2(SourceElementPositioningStrategies.ASSIGNMENT_VALUE) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirConstChecks.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirConstChecks.kt new file mode 100644 index 00000000000..b5c49d0b40d --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirConstChecks.kt @@ -0,0 +1,207 @@ +/* + * Copyright 2010-2021 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 + +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.FirSymbolOwner +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.expressions.* +import org.jetbrains.kotlin.fir.references.FirErrorNamedReference +import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference +import org.jetbrains.kotlin.fir.resolve.toSymbol +import org.jetbrains.kotlin.fir.scopes.impl.FirIntegerOperatorCall +import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.StandardClassIds +import org.jetbrains.kotlin.util.OperatorNameConventions + +fun ConeKotlinType.canBeUsedForConstVal(): Boolean { + val classId = if (this is ConeFlexibleType) { + val lb = this.lowerBoundIfFlexible() + if (lb is ConeClassLikeType) lb.lookupTag.classId + else this.classId + } else this.classId + return (classId in StandardClassIds.primitiveTypes || classId in StandardClassIds.unsignedTypes) && !this.isNullable || + classId == StandardClassIds.String +} + +internal fun checkConstantArguments( + expression: FirExpression, + session: FirSession, +): ConstantArgumentKind? { + val expressionSymbol = expression.toResolvedCallableSymbol() + ?.fir + val classKindOfParent = (expressionSymbol + ?.getReferencedClass(session) as? FirRegularClass) + ?.classKind + + when { + expression is FirTypeOperatorCall -> { + if (expression.operation == FirOperation.AS) return ConstantArgumentKind.NOT_CONST + } + 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 ConstantArgumentKind.ENUM_NOT_CONST + } + expression is FirComparisonExpression -> { + return checkConstantArguments(expression.compareToCall, session) + } + expression is FirIntegerOperatorCall -> { + for (exp in (expression as FirCall).arguments.plus(expression.dispatchReceiver)) + checkConstantArguments(exp, session).let { return it } + } + expression is FirStringConcatenationCall || expression is FirEqualityOperatorCall -> { + for (exp in (expression as FirCall).arguments) + checkConstantArguments(exp, session).let { return it } + } + (expression is FirGetClassCall) -> { + var coneType = (expression as? FirCall) + ?.argument + ?.typeRef + ?.coneType + + if (coneType is ConeClassErrorType) + return ConstantArgumentKind.NOT_CONST + + while (coneType?.classId == StandardClassIds.Array) + coneType = (coneType.lowerBoundIfFlexible().typeArguments.first() as? ConeKotlinTypeProjection)?.type ?: break + + return when { + coneType is ConeTypeParameterType -> + ConstantArgumentKind.KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR + (expression as FirCall).argument !is FirResolvedQualifier -> + ConstantArgumentKind.NOT_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 ConstantArgumentKind.NOT_CONST + } + expression is FirFunctionCall -> { + val calleeReference = expression.calleeReference + if (calleeReference is FirErrorNamedReference) { + return null + } + if (expression.typeRef.coneType.classId == StandardClassIds.KClass) { + return ConstantArgumentKind.NOT_KCLASS_LITERAL + } + + //TODO: UNRESOLVED REFERENCE + if (expression.dispatchReceiver is FirThisReceiverExpression) { + return null + } + + when (calleeReference.name) { + in OperatorNameConventions.BINARY_OPERATION_NAMES, in OperatorNameConventions.UNARY_OPERATION_NAMES, + OperatorNameConventions.SHL, OperatorNameConventions.SHR, OperatorNameConventions.USHR, + OperatorNameConventions.OR, OperatorNameConventions.AND -> { + val coneType = + expression.dispatchReceiver.typeRef.coneTypeSafe() ?: return ConstantArgumentKind.NOT_CONST + val receiverClassId = coneType.lowerBoundIfFlexible().classId + + + if ((calleeReference.name == OperatorNameConventions.DIV || calleeReference.name == OperatorNameConventions.REM) + && expression.typeRef.coneType.classId == StandardClassIds.Int + ) { + val value = expression.arguments.first() as? FirConstExpression<*> + if (value?.value == 0L) { + return ConstantArgumentKind.NOT_CONST + } + } + + for (exp in (expression as FirCall).arguments.plus(expression.dispatchReceiver)) { + val expClassId = exp.typeRef.coneType.lowerBoundIfFlexible().classId + + if (calleeReference.name == OperatorNameConventions.PLUS + && expClassId != receiverClassId + && (expClassId !in StandardClassIds.primitiveTypesAndString || receiverClassId !in StandardClassIds.primitiveTypesAndString) + ) { + return ConstantArgumentKind.NOT_CONST + } + checkConstantArguments(exp, session)?.let { return it } + } + } + else -> { + if (expression.arguments.isNotEmpty() || calleeReference !is FirResolvedNamedReference) { + return ConstantArgumentKind.NOT_CONST + } + val symbol = calleeReference.resolvedSymbol as? FirCallableSymbol + if (calleeReference.name == OperatorNameConventions.TO_STRING || + calleeReference.name in CONVERSION_NAMES && symbol?.callableId?.packageName?.asString() == "kotlin" + ) { + return checkConstantArguments(expression.dispatchReceiver, session) + } + return ConstantArgumentKind.NOT_CONST + } + } + } + expression is FirQualifiedAccessExpression -> { + + when { + (expressionSymbol as FirProperty).isLocal || expressionSymbol.symbol.callableId.className?.isRoot == false -> + return ConstantArgumentKind.NOT_CONST + expression.typeRef.coneType.classId == StandardClassIds.KClass -> + return ConstantArgumentKind.NOT_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) + ConstantArgumentKind.NOT_CONST_VAL_IN_CONST_EXPRESSION + else + ConstantArgumentKind.NOT_CONST + } + is FirGetClassCall -> + ConstantArgumentKind.NOT_KCLASS_LITERAL + else -> + ConstantArgumentKind.NOT_CONST + } + } + else -> + return ConstantArgumentKind.NOT_CONST + } + return null +} + +private fun FirTypedDeclaration?.getReferencedClass(session: FirSession): FirSymbolOwner<*>? = + this?.returnTypeRef + ?.coneTypeSafe() + ?.lookupTag + ?.toSymbol(session) + ?.fir + +private val CONVERSION_NAMES = listOf( + "toInt", "toLong", "toShort", "toByte", "toFloat", "toDouble", "toChar", "toBoolean" +).mapTo(hashSetOf()) { Name.identifier(it) } + +internal enum class ConstantArgumentKind { + NOT_CONST, + ENUM_NOT_CONST, + NOT_KCLASS_LITERAL, + NOT_CONST_VAL_IN_CONST_EXPRESSION, + KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirAnnotationArgumentChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirAnnotationArgumentChecker.kt index 11ff5a01732..19ef7294905 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirAnnotationArgumentChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirAnnotationArgumentChecker.kt @@ -11,6 +11,8 @@ import org.jetbrains.kotlin.fir.FirAnnotationContainer import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSourceElement import org.jetbrains.kotlin.fir.FirSymbolOwner +import org.jetbrains.kotlin.fir.analysis.checkers.ConstantArgumentKind +import org.jetbrains.kotlin.fir.analysis.checkers.checkConstantArguments 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 @@ -78,158 +80,15 @@ object FirAnnotationArgumentChecker : FirBasicDeclarationChecker() { ?.let { reporter.reportOn(arg.source, it, context) } } else -> - return checkAnnotationArgument(expression, session) + return when (checkConstantArguments(expression, session)) { + ConstantArgumentKind.NOT_CONST -> FirErrors.ANNOTATION_ARGUMENT_MUST_BE_CONST + ConstantArgumentKind.ENUM_NOT_CONST -> FirErrors.ANNOTATION_ARGUMENT_MUST_BE_ENUM_CONST + ConstantArgumentKind.NOT_KCLASS_LITERAL -> FirErrors.ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL + ConstantArgumentKind.KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR -> FirErrors.ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR + ConstantArgumentKind.NOT_CONST_VAL_IN_CONST_EXPRESSION -> FirErrors.NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION + null -> null + } } return null } - - private fun checkAnnotationArgument( - expression: FirExpression, - session: FirSession, - ): FirDiagnosticFactory0? { - 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) { - 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 primitiveTypesAndString || receiverClassId !in primitiveTypesAndString) - ) - return FirErrors.ANNOTATION_ARGUMENT_MUST_BE_CONST - - checkAnnotationArgument(exp, session)?.let { return it } - } - } - else -> { - if (expression.arguments.isNotEmpty() || calleeReference !is FirResolvedNamedReference) { - return FirErrors.ANNOTATION_ARGUMENT_MUST_BE_CONST - } - val symbol = calleeReference.resolvedSymbol as? FirCallableSymbol - if (calleeReference.name == TO_STRING || - calleeReference.name in CONVERSION_NAMES && symbol?.callableId?.packageName?.asString() == "kotlin" - ) { - return checkAnnotationArgument(expression.dispatchReceiver, session) - } - 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() - ?.lookupTag - ?.toSymbol(session) - ?.fir - - private val CONVERSION_NAMES = listOf( - "toInt", "toLong", "toShort", "toByte", "toFloat", "toDouble", "toChar", "toBoolean" - ).mapTo(hashSetOf()) { Name.identifier(it) } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConstPropertyChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConstPropertyChecker.kt index f9455a87124..d0766ef9ee4 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConstPropertyChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConstPropertyChecker.kt @@ -7,6 +7,8 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.fir.FirFakeSourceElementKind +import org.jetbrains.kotlin.fir.analysis.checkers.canBeUsedForConstVal +import org.jetbrains.kotlin.fir.analysis.checkers.checkConstantArguments import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors @@ -14,6 +16,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.declarations.FirRegularClass import org.jetbrains.kotlin.fir.declarations.isConst +import org.jetbrains.kotlin.fir.types.ConeClassErrorType +import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.lexer.KtTokens object FirConstPropertyChecker : FirPropertyChecker() { @@ -30,19 +34,34 @@ object FirConstPropertyChecker : FirPropertyChecker() { val classKind = (context.containingDeclarations.lastOrNull() as? FirRegularClass)?.classKind if (classKind != ClassKind.OBJECT && context.containingDeclarations.size > 1) { reporter.reportOn(declaration.source, FirErrors.CONST_VAL_NOT_TOP_LEVEL_OR_OBJECT, context) + return } - if (declaration.getter?.source?.kind !is FirFakeSourceElementKind) { - reporter.reportOn(declaration.getter?.source, FirErrors.CONST_VAL_WITH_GETTER, context) + val source = declaration.getter?.source + if (source != null && source.kind !is FirFakeSourceElementKind) { + reporter.reportOn(source, FirErrors.CONST_VAL_WITH_GETTER, context) + return } if (declaration.delegate != null) { reporter.reportOn(declaration.delegate?.source, FirErrors.CONST_VAL_WITH_DELEGATE, context) + return } - // TODO: Implement checkers for these errors (see ConstModifierChecker in FE1.0): - // - TYPE_CANT_BE_USED_FOR_CONST_VAL - // - CONST_VAL_WITHOUT_INITIALIZER - // - CONST_VAL_WITH_NON_CONST_INITIALIZER + val initializer = declaration.initializer + if (initializer == null) { + reporter.reportOn(declaration.source, FirErrors.CONST_VAL_WITHOUT_INITIALIZER, context) + return + } + + val type = declaration.returnTypeRef.coneType + if ((type !is ConeClassErrorType) && !type.canBeUsedForConstVal()) { + reporter.reportOn(declaration.source, FirErrors.TYPE_CANT_BE_USED_FOR_CONST_VAL, declaration.returnTypeRef.coneType, context) + return + } + + if (checkConstantArguments(initializer, context.session) != null) { + reporter.reportOn(initializer.source, FirErrors.CONST_VAL_WITH_NON_CONST_INITIALIZER, context) + } } } \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index 19af32f4bc0..daec1b7fb01 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -63,6 +63,11 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONFLICTING_PROJE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONFLICTING_UPPER_BOUNDS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONSTRUCTOR_IN_INTERFACE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONSTRUCTOR_IN_OBJECT +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONST_VAL_NOT_TOP_LEVEL_OR_OBJECT +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONST_VAL_WITHOUT_INITIALIZER +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONST_VAL_WITH_DELEGATE +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONST_VAL_WITH_GETTER +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONST_VAL_WITH_NON_CONST_INITIALIZER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CYCLIC_CONSTRUCTOR_DELEGATION_CALL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_CLASS_NOT_PROPERTY_PARAMETER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_CLASS_VARARG_PARAMETER @@ -214,6 +219,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SYNTAX import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TOO_MANY_ARGUMENTS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TOPLEVEL_TYPEALIASES_ONLY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_ARGUMENTS_NOT_ALLOWED +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_CANT_BE_USED_FOR_CONST_VAL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_MISMATCH import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETERS_IN_ENUM import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETERS_IN_OBJECT @@ -693,6 +699,13 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension { ) map.put(INITIALIZER_TYPE_MISMATCH, "Initializer type mismatch: expected {0}, actual {1}", RENDER_TYPE, RENDER_TYPE) + map.put(CONST_VAL_NOT_TOP_LEVEL_OR_OBJECT, "Const 'val' are only allowed on top level or in objects") + map.put(CONST_VAL_WITH_GETTER, "Const 'val' should not have a getter") + map.put(CONST_VAL_WITH_DELEGATE, "Const 'val' should not have a delegate") + map.put(TYPE_CANT_BE_USED_FOR_CONST_VAL, "Const ''val'' has type ''{0}''. Only primitives and String are allowed", RENDER_TYPE) + map.put(CONST_VAL_WITHOUT_INITIALIZER, "Const 'val' should have an initializer") + map.put(CONST_VAL_WITH_NON_CONST_INITIALIZER, "Const 'val' initializer should be a constant value") + // Multi-platform projects map.put(EXPECTED_DECLARATION_WITH_BODY, "Expected declaration must not have a body") map.put(EXPECTED_PROPERTY_INITIALIZER, "Expected property cannot have an initializer") diff --git a/compiler/testData/diagnostics/tests/evaluate/customExtensionOverKotlinExtensionInConst.fir.kt b/compiler/testData/diagnostics/tests/evaluate/customExtensionOverKotlinExtensionInConst.fir.kt deleted file mode 100644 index a35cc4426f0..00000000000 --- a/compiler/testData/diagnostics/tests/evaluate/customExtensionOverKotlinExtensionInConst.fir.kt +++ /dev/null @@ -1,7 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_VARIABLE, -UNUSED_PARAMETER - -fun Int.mod(other: Int) = 10 -fun Int.floorDiv(other: Int): Int = 20 - -const val a1 = (-5).mod(2) -const val b1 = 5.floorDiv(3) diff --git a/compiler/testData/diagnostics/tests/evaluate/customExtensionOverKotlinExtensionInConst.kt b/compiler/testData/diagnostics/tests/evaluate/customExtensionOverKotlinExtensionInConst.kt index 349cab4cefb..2699227b8be 100644 --- a/compiler/testData/diagnostics/tests/evaluate/customExtensionOverKotlinExtensionInConst.kt +++ b/compiler/testData/diagnostics/tests/evaluate/customExtensionOverKotlinExtensionInConst.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_VARIABLE, -UNUSED_PARAMETER fun Int.mod(other: Int) = 10 diff --git a/compiler/testData/diagnostics/tests/evaluate/divisionByZero.fir.kt b/compiler/testData/diagnostics/tests/evaluate/divisionByZero.fir.kt index f99b1402648..d29ce21387c 100644 --- a/compiler/testData/diagnostics/tests/evaluate/divisionByZero.fir.kt +++ b/compiler/testData/diagnostics/tests/evaluate/divisionByZero.fir.kt @@ -17,6 +17,6 @@ val a11 = 1 / 0.0 val a12 = 1L / 0 val b1: Byte = 1 / 0 -@Ann(1 / 0) val b2 = 1 +@Ann(1 / 0) val b2 = 1 annotation class Ann(val i : Int) diff --git a/compiler/testData/diagnostics/tests/modifiers/const/applicability.fir.kt b/compiler/testData/diagnostics/tests/modifiers/const/applicability.fir.kt index 0915c220c1d..3d2ee44a173 100644 --- a/compiler/testData/diagnostics/tests/modifiers/const/applicability.fir.kt +++ b/compiler/testData/diagnostics/tests/modifiers/const/applicability.fir.kt @@ -31,7 +31,7 @@ abstract class C { final const val final = 11 - const val withoutInitializer: Int + const val withoutInitializer: Int init { withoutInitializer = 12 @@ -70,20 +70,20 @@ class Outer { const val defaultGetter = 19 get -const val nonConstInitializer1 = foo() -const val nonConstInitializer2 = 1 as String -const val nonConstInitializer3 = 1.0 as String -const val nonConstInitializer4 = 1 as Double -const val nonConstInitializer5 = "2" as Int -const val nonConstInitializer6 = 1/0 -const val nonConstInitializer7 = -1/0 -const val nonConstInitializer8 = 1/0 - 1/0 -const val nonConstInitializer9 = 1.0/0.0 - 1/0 -const val nonConstInitializer10 = 0/0 -const val nonConstInitializer11 = 1 % 0 -const val nonConstInitializer12 = 0 % 0 -const val nonConstInitializer14 = 0.rem(0) -const val nonConstInitializer15 = 0.div(0) +const val nonConstInitializer1 = foo() +const val nonConstInitializer2 = 1 as String +const val nonConstInitializer3 = 1.0 as String +const val nonConstInitializer4 = 1 as Double +const val nonConstInitializer5 = "2" as Int +const val nonConstInitializer6 = 1/0 +const val nonConstInitializer7 = -1/0 +const val nonConstInitializer8 = 1/0 - 1/0 +const val nonConstInitializer9 = 1.0/0.0 - 1/0 +const val nonConstInitializer10 = 0/0 +const val nonConstInitializer11 = 1 % 0 +const val nonConstInitializer12 = 0 % 0 +const val nonConstInitializer14 = 0.rem(0) +const val nonConstInitializer15 = 0.div(0) const val constInitializer1 = 1.0/0 const val constInitializer2 = 1/0.0 diff --git a/compiler/testData/diagnostics/tests/modifiers/const/noDivisionByZeroFeature.fir.kt b/compiler/testData/diagnostics/tests/modifiers/const/noDivisionByZeroFeature.fir.kt index 02944e2ca7a..ac0f05a19a4 100644 --- a/compiler/testData/diagnostics/tests/modifiers/const/noDivisionByZeroFeature.fir.kt +++ b/compiler/testData/diagnostics/tests/modifiers/const/noDivisionByZeroFeature.fir.kt @@ -9,7 +9,7 @@ const val e = 0.0 % 0 const val g = 0.0.rem(0) const val h = 0.0.div(0) -const val i = 1 / 0 +const val i = 1 / 0 val nonConst1 = 1.0 / 0 val nonConst2 = 1 / 0 diff --git a/compiler/testData/diagnostics/tests/modifiers/const/types.fir.kt b/compiler/testData/diagnostics/tests/modifiers/const/types.fir.kt deleted file mode 100644 index a47b3b67a78..00000000000 --- a/compiler/testData/diagnostics/tests/modifiers/const/types.fir.kt +++ /dev/null @@ -1,17 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_PARAMETER - -const val intConst = 1 -const val longConst: Long = 1 -const val boolConst = true -const val stringConst = "empty" - -enum class MyEnum { A } - -const val enumConst: MyEnum = MyEnum.A -const val arrayConst: Array = arrayOf("1") -const val intArrayConst: IntArray = intArrayOf() - -const val unresolvedConst1 = Unresolved -const var unresolvedConst2 = Unresolved -const val unresolvedConst3 = Unresolved -get() = 10 diff --git a/compiler/testData/diagnostics/tests/modifiers/const/types.kt b/compiler/testData/diagnostics/tests/modifiers/const/types.kt index dd7b10405fb..5c0bac5ab85 100644 --- a/compiler/testData/diagnostics/tests/modifiers/const/types.kt +++ b/compiler/testData/diagnostics/tests/modifiers/const/types.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_PARAMETER const val intConst = 1 diff --git a/compiler/testData/diagnostics/tests/multiplatform/topLevelProperty/differentKindsOfProperties.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/topLevelProperty/differentKindsOfProperties.fir.kt index 2b39f974b7c..10a21f89a09 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/topLevelProperty/differentKindsOfProperties.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/topLevelProperty/differentKindsOfProperties.fir.kt @@ -26,7 +26,7 @@ expect var customAccessorVar: String get() = "no" set(value) {} -expect const val constVal: Int +expect const val constVal: Int expect lateinit var lateinitVar: String diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/annotationParameterMustBeConstant/useOfNonConstVal.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/annotationParameterMustBeConstant/useOfNonConstVal.fir.kt index bb32f0b8cb2..ae4cd577d53 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/annotations/annotationParameterMustBeConstant/useOfNonConstVal.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/annotationParameterMustBeConstant/useOfNonConstVal.fir.kt @@ -1,6 +1,6 @@ val nonConst = 1 -const val constConst = nonConst * nonConst + 2 +const val constConst = nonConst * nonConst + 2 annotation class Ann(val x: Int, val y: String) diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 1c469a623f3..c3aa379c620 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -1430,6 +1430,25 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.TYPE_CANT_BE_USED_FOR_CONST_VAL) { firDiagnostic -> + TypeCantBeUsedForConstValImpl( + firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } + add(FirErrors.CONST_VAL_WITHOUT_INITIALIZER) { firDiagnostic -> + ConstValWithoutInitializerImpl( + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } + add(FirErrors.CONST_VAL_WITH_NON_CONST_INITIALIZER) { firDiagnostic -> + ConstValWithNonConstInitializerImpl( + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } add(FirErrors.WRONG_SETTER_PARAMETER_TYPE) { firDiagnostic -> WrongSetterParameterTypeImpl( firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index d83386cfd87..18381637feb 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -1011,6 +1011,19 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = ConstValWithDelegate::class } + abstract class TypeCantBeUsedForConstVal : KtFirDiagnostic() { + override val diagnosticClass get() = TypeCantBeUsedForConstVal::class + abstract val constValType: KtType + } + + abstract class ConstValWithoutInitializer : KtFirDiagnostic() { + override val diagnosticClass get() = ConstValWithoutInitializer::class + } + + abstract class ConstValWithNonConstInitializer : KtFirDiagnostic() { + override val diagnosticClass get() = ConstValWithNonConstInitializer::class + } + abstract class WrongSetterParameterType : KtFirDiagnostic() { override val diagnosticClass get() = WrongSetterParameterType::class abstract val expectedType: KtType diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index ce988e34c0a..06a45e19801 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -1636,6 +1636,28 @@ internal class ConstValWithDelegateImpl( override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) } +internal class TypeCantBeUsedForConstValImpl( + override val constValType: KtType, + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.TypeCantBeUsedForConstVal(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + +internal class ConstValWithoutInitializerImpl( + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.ConstValWithoutInitializer(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + +internal class ConstValWithNonConstInitializerImpl( + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.ConstValWithNonConstInitializer(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + internal class WrongSetterParameterTypeImpl( override val expectedType: KtType, override val actualType: KtType,