FIR checker: report UNINITIALIZED_ENUM_(ENTRY|COMPANION)
This commit is contained in:
committed by
Mikhail Glukhikh
parent
ad9b962536
commit
619360fe4d
@@ -23,7 +23,7 @@ enum class Planet(val m: Double, internal val r: Double) {
|
||||
}
|
||||
};
|
||||
|
||||
val g: Double = G * m / (r * r)
|
||||
val g: Double = <!UNINITIALIZED_VARIABLE!>G<!> * m / (r * r)
|
||||
|
||||
abstract fun sayHello()
|
||||
|
||||
|
||||
+6
@@ -30900,6 +30900,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/elvisOnUnitInLet.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("enumEntryInitialization.kt")
|
||||
public void testEnumEntryInitialization() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/enumEntryInitialization.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exitProcess.kt")
|
||||
public void testExitProcess() throws Exception {
|
||||
|
||||
+7
-4
@@ -19,10 +19,7 @@ import org.jetbrains.kotlin.fir.PrivateForInline
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -529,6 +526,12 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
|
||||
val UNINITIALIZED_VARIABLE by error<FirSourceElement, KtSimpleNameExpression> {
|
||||
parameter<FirPropertySymbol>("variable")
|
||||
}
|
||||
val UNINITIALIZED_ENUM_ENTRY by error<FirSourceElement, KtSimpleNameExpression> {
|
||||
parameter<FirVariableSymbol<FirEnumEntry>>("enumEntry")
|
||||
}
|
||||
val UNINITIALIZED_ENUM_COMPANION by error<FirSourceElement, KtSimpleNameExpression>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
|
||||
parameter<FirRegularClassSymbol>("enumClass")
|
||||
}
|
||||
val VAL_REASSIGNMENT by error<FirSourceElement, KtExpression> {
|
||||
parameter<FirVariableSymbol<*>>("variable")
|
||||
}
|
||||
|
||||
@@ -19,12 +19,14 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.SourceElementPositioningStr
|
||||
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirEnumEntry
|
||||
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
@@ -322,6 +324,8 @@ object FirErrors {
|
||||
|
||||
// Control flow diagnostics
|
||||
val UNINITIALIZED_VARIABLE by error1<FirSourceElement, KtSimpleNameExpression, FirPropertySymbol>()
|
||||
val UNINITIALIZED_ENUM_ENTRY by error1<FirSourceElement, KtSimpleNameExpression, FirVariableSymbol<FirEnumEntry>>()
|
||||
val UNINITIALIZED_ENUM_COMPANION by error1<FirSourceElement, KtSimpleNameExpression, FirRegularClassSymbol>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
val VAL_REASSIGNMENT by error1<FirSourceElement, KtExpression, FirVariableSymbol<*>>()
|
||||
val VAL_REASSIGNMENT_VIA_BACKING_FIELD by warning1<FirSourceElement, KtExpression, FirPropertySymbol>()
|
||||
val VAL_REASSIGNMENT_VIA_BACKING_FIELD_ERROR by error1<FirSourceElement, KtExpression, FirPropertySymbol>()
|
||||
|
||||
@@ -21,11 +21,14 @@ import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyExpressionBlock
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.resolve.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.firClassLike
|
||||
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
|
||||
import org.jetbrains.kotlin.fir.scopes.processOverriddenFunctions
|
||||
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
@@ -158,6 +161,16 @@ inline fun <reified T : Any> FirQualifiedAccessExpression.getDeclaration(): T? {
|
||||
fun FirSymbolOwner<*>.getContainingClass(context: CheckerContext): FirClassLikeDeclaration<*>? =
|
||||
this.safeAs<FirCallableMemberDeclaration<*>>()?.containingClass()?.toSymbol(context.session)?.fir
|
||||
|
||||
fun FirClassLikeSymbol<*>.outerClass(context: CheckerContext): FirClassLikeSymbol<*>? {
|
||||
if (this !is FirClassSymbol<*>) return null
|
||||
val outerClassId = classId.outerClassId ?: return null
|
||||
return context.session.symbolProvider.getClassLikeSymbolByFqName(outerClassId)
|
||||
}
|
||||
|
||||
fun FirClass<*>.outerClass(context: CheckerContext): FirClass<*>? {
|
||||
return symbol.outerClass(context)?.fir as? FirClass<*>
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the FirClassLikeDeclaration that the
|
||||
* sequence of FirTypeAlias'es points to starting
|
||||
|
||||
+8
@@ -14,8 +14,10 @@ import org.jetbrains.kotlin.fir.analysis.checkers.modality
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.containingClassAttr
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLookupTagWithFixedSymbol
|
||||
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
|
||||
@@ -175,6 +177,12 @@ internal fun FirRegularClass.isInlineOrValueClass(): Boolean {
|
||||
return isInline || hasModifier(KtTokens.VALUE_KEYWORD)
|
||||
}
|
||||
|
||||
internal val FirDeclaration.isEnumEntryInitializer: Boolean
|
||||
get() {
|
||||
if (this !is FirConstructor || !this.isPrimary) return false
|
||||
return (containingClassAttr as? ConeClassLookupTagWithFixedSymbol)?.symbol?.fir?.classKind == ClassKind.ENUM_ENTRY
|
||||
}
|
||||
|
||||
internal val FirMemberDeclaration.isLocalMember: Boolean
|
||||
get() = when (this) {
|
||||
is FirProperty -> this.isLocal
|
||||
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* 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.expression
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.fir.FirSymbolOwner
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.isEnumEntryInitializer
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClass
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.outerClass
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirLambdaArgumentExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
|
||||
object FirUninitializedEnumChecker : FirQualifiedAccessChecker() {
|
||||
// Initialization order: member property initializers, enum entries, companion object (including members in it).
|
||||
//
|
||||
// When JVM loads a class, the corresponding class initializer, a.k.a. <clinit>, is executed first.
|
||||
// Kotlin (and Java as well) converts enum entries as static final field, which is initialized in that <clinit>:
|
||||
// enum class E(...) {
|
||||
// E1, E2, ...
|
||||
// }
|
||||
// ~>
|
||||
// class E {
|
||||
// final static E1, E2, ...
|
||||
// static { // <clinit>
|
||||
// E1 = new E(...)
|
||||
// ...
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Note that, when initializing enum entries, now we call the enum class's constructor, a.k.a. <init>, to initialize non-final
|
||||
// instance members. Therefore, if there is a member property in the enum class, and if that member has an access to enum entries,
|
||||
// that is an illegal access since enum entries are not yet initialized:
|
||||
// enum class E(...) {
|
||||
// E1, E2, ...
|
||||
// val m1 = E1
|
||||
// }
|
||||
// ~>
|
||||
// class E {
|
||||
// E m1 ...
|
||||
// E(...) { // <init>
|
||||
// m1 = E1
|
||||
// }
|
||||
// final static E1, E2, ...
|
||||
// static { // <clinit>
|
||||
// E1 = new E(...)
|
||||
// ...
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// A companion object is desugared to a static final singleton, and initialized in <clinit> too. However, enum lowering goes first,
|
||||
// or in other words, companion object lowering goes last. Thus, any other things initialized in <clinit>, including enum entries,
|
||||
// should not have access to companion object and members in it.
|
||||
//
|
||||
// See related discussions:
|
||||
// https://youtrack.jetbrains.com/issue/KT-6054
|
||||
// https://youtrack.jetbrains.com/issue/KT-11769
|
||||
override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val source = expression.source ?: return
|
||||
if (source.kind is FirFakeSourceElementKind) return
|
||||
|
||||
val reference = expression.calleeReference as? FirResolvedNamedReference ?: return
|
||||
val calleeDeclaration = reference.resolvedSymbol.fir
|
||||
val calleeContainingClass = calleeDeclaration.getContainingClass(context) as? FirRegularClass ?: return
|
||||
// We're looking for members/entries/companion object in an enum class or members in companion object of an enum class.
|
||||
val calleeIsInsideEnum = calleeContainingClass.isEnumClass
|
||||
val calleeIsInsideEnumCompanion =
|
||||
calleeContainingClass.isCompanion && (calleeContainingClass.outerClass(context) as? FirRegularClass)?.isEnumClass == true
|
||||
if (!calleeIsInsideEnum && !calleeIsInsideEnumCompanion) return
|
||||
|
||||
val enumClass =
|
||||
if (calleeIsInsideEnum) calleeContainingClass
|
||||
else calleeContainingClass.outerClass(context) as? FirRegularClass ?: return
|
||||
|
||||
// An accessed context within the enum class of interest. We should look up until either enum members or enum entries are found,
|
||||
// not just last containing declaration. For example,
|
||||
// enum class Fruit(...) {
|
||||
// APPLE(...);
|
||||
// companion object {
|
||||
// val common = ...
|
||||
// }
|
||||
// val score = ... <!>common<!>
|
||||
// val score2 = { ... <!>common<!> }()
|
||||
// }
|
||||
// companion object is not initialized for both member properties. The former has the property itself as the last containing
|
||||
// declaration, whereas the latter has an anonymous function instead. For both cases, we're looking for member properties as an
|
||||
// accessed context.
|
||||
val accessedContext = context.containingDeclarations.lastOrNull {
|
||||
// To not raise an error for an access from another enum class, e.g.,
|
||||
// enum class EnumCompanion4(...) {
|
||||
// INSTANCE(EnumCompanion2.foo())
|
||||
// }
|
||||
// find an accessed context within the same enum class.
|
||||
(it as? FirSymbolOwner<*>)?.getContainingClass(context) == enumClass
|
||||
} ?: return
|
||||
|
||||
val enumMemberProperties = enumClass.declarations.filterIsInstance<FirProperty>()
|
||||
val enumEntries = enumClass.declarations.filterIsInstance<FirEnumEntry>()
|
||||
|
||||
// When checking enum member properties, accesses to enum entries in lazy delegation is legitimate, e.g.,
|
||||
// enum JvmTarget(...) {
|
||||
// JVM_1_6, ...
|
||||
// val bytecodeVersion: ... by lazy {
|
||||
// when (this) {
|
||||
// JVM_1_6 -> ...
|
||||
// }
|
||||
// }
|
||||
if (accessedContext in enumMemberProperties) {
|
||||
val lazyDelegation = (accessedContext as FirProperty).lazyDelegation
|
||||
if (lazyDelegation != null && lazyDelegation == context.containingDeclarations.lastOrNull()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// When checking enum entries, only entry initializer matters. For example,
|
||||
// enum class EnumCompanion(...) {
|
||||
// ANOTHER {
|
||||
// override fun bar() = foo() // At this point, companion object is initialized
|
||||
// }
|
||||
// abstract bar(): ...
|
||||
// companion object {
|
||||
// fun foo() = ...
|
||||
// }
|
||||
// }
|
||||
if (accessedContext in enumEntries && context.containingDeclarations.lastOrNull()?.isEnumEntryInitializer != true) {
|
||||
return
|
||||
}
|
||||
|
||||
// Members inside the companion object of an enum class
|
||||
if (calleeContainingClass == enumClass.companionObject) {
|
||||
// Uninitialized from the point of view of members or enum entries of that enum class
|
||||
if (accessedContext in enumMemberProperties || accessedContext in enumEntries) {
|
||||
if (calleeDeclaration is FirProperty) {
|
||||
// From KT-11769
|
||||
// enum class Fruit(...) {
|
||||
// APPLE(...);
|
||||
// companion object {
|
||||
// val common = ...
|
||||
// }
|
||||
// val score = ... <!>common<!>
|
||||
// }
|
||||
reporter.reportOn(source, FirErrors.UNINITIALIZED_VARIABLE, calleeDeclaration.symbol, context)
|
||||
} else {
|
||||
// enum class EnumCompanion2(...) {
|
||||
// INSTANCE(<!>foo<!>())
|
||||
// companion object {
|
||||
// fun foo() = ...
|
||||
// }
|
||||
// }
|
||||
// <!>Companion<!>.foo() v.s. <!>foo<!>()
|
||||
if ((expression.explicitReceiver as? FirResolvedQualifier)?.symbol?.fir == enumClass.companionObject) {
|
||||
reporter.reportOn(
|
||||
expression.explicitReceiver!!.source,
|
||||
FirErrors.UNINITIALIZED_ENUM_COMPANION,
|
||||
enumClass.symbol,
|
||||
context
|
||||
)
|
||||
} else {
|
||||
reporter.reportOn(
|
||||
expression.calleeReference.source,
|
||||
FirErrors.UNINITIALIZED_ENUM_COMPANION,
|
||||
enumClass.symbol,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The enum entries of an enum class
|
||||
if (calleeDeclaration in enumEntries) {
|
||||
val calleeEnumEntry = calleeDeclaration as FirEnumEntry
|
||||
// Uninitialized from the point of view of members of that enum class
|
||||
if (accessedContext in enumMemberProperties) {
|
||||
// From KT-6054
|
||||
// enum class MyEnum {
|
||||
// A, B;
|
||||
// val x = when(this) {
|
||||
// <!>A<!> -> ...
|
||||
// <!>B<!> -> ...
|
||||
// }
|
||||
// }
|
||||
reporter.reportOn(source, FirErrors.UNINITIALIZED_ENUM_ENTRY, calleeEnumEntry.symbol, context)
|
||||
}
|
||||
// enum class A(...) {
|
||||
// A1(<!>A2<!>),
|
||||
// A2(...),
|
||||
// A3(<!>A3<!>)
|
||||
// }
|
||||
if (accessedContext in enumEntries) {
|
||||
// Technically, this is equal to `enumEntries.indexOf(accessedContext) <= enumEntries.indexOf(calleeDeclaration)`.
|
||||
// Instead of double `indexOf`, we can iterate entries just once until either one appears.
|
||||
var precedingEntry: FirEnumEntry? = null
|
||||
enumEntries.forEach {
|
||||
if (precedingEntry != null) return@forEach
|
||||
if (it == calleeEnumEntry || it == accessedContext) {
|
||||
precedingEntry = it
|
||||
}
|
||||
}
|
||||
if (precedingEntry == accessedContext) {
|
||||
reporter.reportOn(source, FirErrors.UNINITIALIZED_ENUM_ENTRY, calleeEnumEntry.symbol, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val FirProperty.lazyDelegation: FirAnonymousFunction?
|
||||
get() {
|
||||
if (delegate == null || delegate !is FirFunctionCall) return null
|
||||
val delegateCall = delegate as FirFunctionCall
|
||||
val callee =
|
||||
(delegateCall.calleeReference as? FirResolvedNamedReference)?.resolvedSymbol?.fir as? FirSimpleFunction ?: return null
|
||||
if (callee.symbol.callableId.asSingleFqName().asString() != "kotlin.lazy") return null
|
||||
val lazyCallArgument = delegateCall.argumentList.arguments.singleOrNull() as? FirLambdaArgumentExpression ?: return null
|
||||
return lazyCallArgument.expression as? FirAnonymousFunction
|
||||
}
|
||||
}
|
||||
+4
@@ -220,6 +220,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETER_IN
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETER_IS_NOT_AN_EXPRESSION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETER_ON_LHS_OF_DOT
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNEXPECTED_SAFE_CALL
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNINITIALIZED_ENUM_COMPANION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNINITIALIZED_ENUM_ENTRY
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNINITIALIZED_VARIABLE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNNECESSARY_LATEINIT
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNNECESSARY_NOT_NULL_ASSERTION
|
||||
@@ -722,6 +724,8 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
|
||||
|
||||
// Control flow diagnostics
|
||||
map.put(UNINITIALIZED_VARIABLE, "{0} must be initialized before access", VARIABLE_NAME)
|
||||
map.put(UNINITIALIZED_ENUM_ENTRY, "Enum entry ''{0}'' is uninitialized here", VARIABLE_NAME)
|
||||
map.put(UNINITIALIZED_ENUM_COMPANION, "Companion object of enum class ''{0}'' is uninitialized here", SYMBOL)
|
||||
map.put(VAL_REASSIGNMENT, "Val cannot be reassigned", VARIABLE_NAME)
|
||||
map.put(VAL_REASSIGNMENT_VIA_BACKING_FIELD, "Reassignment of read-only property via backing field is deprecated", VARIABLE_NAME)
|
||||
map.put(VAL_REASSIGNMENT_VIA_BACKING_FIELD_ERROR, "Reassignment of read-only property via backing field", VARIABLE_NAME)
|
||||
|
||||
+1
@@ -27,6 +27,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
|
||||
FirTypeArgumentsNotAllowedExpressionChecker,
|
||||
FirTypeParameterInQualifiedAccessChecker,
|
||||
FirSealedClassConstructorCallChecker,
|
||||
FirUninitializedEnumChecker,
|
||||
)
|
||||
|
||||
override val functionCallCheckers: Set<FirFunctionCallChecker> = setOf(
|
||||
|
||||
Vendored
+31
-5
@@ -9,7 +9,7 @@ enum class B(val x: Int) {
|
||||
}
|
||||
|
||||
enum class C(val x: Int) {
|
||||
C1(SUM),
|
||||
C1(<!UNINITIALIZED_VARIABLE!>SUM<!>),
|
||||
C2(1);
|
||||
|
||||
companion object {
|
||||
@@ -26,13 +26,14 @@ enum class Fruit(personal: Int) {
|
||||
val common = 20
|
||||
}
|
||||
|
||||
val score = personal + common
|
||||
val score = personal + <!UNINITIALIZED_VARIABLE!>common<!>
|
||||
val score2 = { personal + <!UNINITIALIZED_VARIABLE!>common<!> }()
|
||||
}
|
||||
|
||||
// Another example from KT-11769
|
||||
enum class EnumCompanion1(val x: Int) {
|
||||
INSTANCE(Companion.foo()),
|
||||
ANOTHER(foo());
|
||||
INSTANCE(<!UNINITIALIZED_ENUM_COMPANION!>Companion<!>.foo()),
|
||||
ANOTHER(<!UNINITIALIZED_ENUM_COMPANION!>foo<!>());
|
||||
|
||||
companion object {
|
||||
fun foo() = 42
|
||||
@@ -40,7 +41,7 @@ enum class EnumCompanion1(val x: Int) {
|
||||
}
|
||||
// Also should be reported for implicit receiver
|
||||
enum class EnumCompanion2(val x: Int) {
|
||||
INSTANCE(foo());
|
||||
INSTANCE(<!UNINITIALIZED_ENUM_COMPANION!>foo<!>());
|
||||
|
||||
companion object {
|
||||
fun foo() = 42
|
||||
@@ -53,3 +54,28 @@ enum class EnumCompanion3(val x: Int) {
|
||||
|
||||
companion object
|
||||
}
|
||||
|
||||
interface ExtractableCodeDescriptor {
|
||||
fun isInterface(): Boolean
|
||||
}
|
||||
|
||||
enum class ExtractionTarget(val targetName: String) {
|
||||
FUNCTION("function") {
|
||||
override fun isAvailable(descriptor: ExtractableCodeDescriptor) = true
|
||||
},
|
||||
|
||||
LAZY_PROPERTY("lazy property") {
|
||||
override fun isAvailable(descriptor: ExtractableCodeDescriptor): Boolean {
|
||||
// Should not report UNINITIALIZED_ENUM_COMPANION
|
||||
return checkNotTrait(descriptor)
|
||||
}
|
||||
};
|
||||
|
||||
abstract fun isAvailable(descriptor: ExtractableCodeDescriptor): Boolean
|
||||
|
||||
companion object {
|
||||
fun checkNotTrait(descriptor: ExtractableCodeDescriptor): Boolean {
|
||||
return !descriptor.isInterface()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+26
@@ -27,6 +27,7 @@ enum class Fruit(personal: Int) {
|
||||
}
|
||||
|
||||
val score = personal + <!UNINITIALIZED_VARIABLE!>common<!>
|
||||
val score2 = { personal + common }()
|
||||
}
|
||||
|
||||
// Another example from KT-11769
|
||||
@@ -53,3 +54,28 @@ enum class EnumCompanion3(val x: Int) {
|
||||
|
||||
companion object
|
||||
}
|
||||
|
||||
interface ExtractableCodeDescriptor {
|
||||
fun isInterface(): Boolean
|
||||
}
|
||||
|
||||
enum class ExtractionTarget(val targetName: String) {
|
||||
FUNCTION("function") {
|
||||
override fun isAvailable(descriptor: ExtractableCodeDescriptor) = true
|
||||
},
|
||||
|
||||
LAZY_PROPERTY("lazy property") {
|
||||
override fun isAvailable(descriptor: ExtractableCodeDescriptor): Boolean {
|
||||
// Should not report UNINITIALIZED_ENUM_COMPANION
|
||||
return checkNotTrait(descriptor)
|
||||
}
|
||||
};
|
||||
|
||||
abstract fun isAvailable(descriptor: ExtractableCodeDescriptor): Boolean
|
||||
|
||||
companion object {
|
||||
fun checkNotTrait(descriptor: ExtractableCodeDescriptor): Boolean {
|
||||
return !descriptor.isInterface()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+39
@@ -149,6 +149,44 @@ public final enum class EnumCompanion3 : kotlin.Enum<EnumCompanion3> {
|
||||
public final /*synthesized*/ fun values(): kotlin.Array<EnumCompanion3>
|
||||
}
|
||||
|
||||
public interface ExtractableCodeDescriptor {
|
||||
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 abstract fun isInterface(): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final enum class ExtractionTarget : kotlin.Enum<ExtractionTarget> {
|
||||
enum entry FUNCTION
|
||||
|
||||
enum entry LAZY_PROPERTY
|
||||
|
||||
private constructor ExtractionTarget(/*0*/ targetName: kotlin.String)
|
||||
public final override /*1*/ /*fake_override*/ val name: kotlin.String
|
||||
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
|
||||
public final val targetName: kotlin.String
|
||||
protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
|
||||
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: ExtractionTarget): kotlin.Int
|
||||
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
protected/*protected and package*/ final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun finalize(): kotlin.Unit
|
||||
public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class<ExtractionTarget!>!
|
||||
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public abstract fun isAvailable(/*0*/ descriptor: ExtractableCodeDescriptor): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
|
||||
public companion object Companion {
|
||||
private constructor Companion()
|
||||
public final fun checkNotTrait(/*0*/ descriptor: ExtractableCodeDescriptor): kotlin.Boolean
|
||||
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
|
||||
}
|
||||
|
||||
// Static members
|
||||
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): ExtractionTarget
|
||||
public final /*synthesized*/ fun values(): kotlin.Array<ExtractionTarget>
|
||||
}
|
||||
|
||||
public final enum class Fruit : kotlin.Enum<Fruit> {
|
||||
enum entry APPLE
|
||||
|
||||
@@ -156,6 +194,7 @@ public final enum class Fruit : kotlin.Enum<Fruit> {
|
||||
public final override /*1*/ /*fake_override*/ val name: kotlin.String
|
||||
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
|
||||
public final val score: kotlin.Int
|
||||
public final val score2: kotlin.Int
|
||||
protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
|
||||
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: Fruit): kotlin.Int
|
||||
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
|
||||
+6
-5
@@ -1,10 +1,11 @@
|
||||
enum class A(val v: A) {
|
||||
A1(A2),
|
||||
A2(A1)
|
||||
A1(<!UNINITIALIZED_ENUM_ENTRY!>A2<!>),
|
||||
A2(A1),
|
||||
A3(<!UNINITIALIZED_ENUM_ENTRY!>A3<!>)
|
||||
}
|
||||
|
||||
enum class D(val x: Int) {
|
||||
D1(D2.x),
|
||||
D1(<!UNINITIALIZED_ENUM_ENTRY!>D2<!>.x),
|
||||
D2(D1.x)
|
||||
}
|
||||
|
||||
@@ -28,8 +29,8 @@ object Object1 {
|
||||
enum class MyEnum {
|
||||
A, B;
|
||||
val x = when(this) {
|
||||
A -> 1
|
||||
B -> 2
|
||||
<!UNINITIALIZED_ENUM_ENTRY!>A<!> -> 1
|
||||
<!UNINITIALIZED_ENUM_ENTRY!>B<!> -> 2
|
||||
else -> 3
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
enum class A(val v: A) {
|
||||
A1(<!UNINITIALIZED_ENUM_ENTRY!>A2<!>),
|
||||
A2(A1)
|
||||
A2(A1),
|
||||
A3(A3)
|
||||
}
|
||||
|
||||
enum class D(val x: Int) {
|
||||
|
||||
+2
@@ -5,6 +5,8 @@ public final enum class A : kotlin.Enum<A> {
|
||||
|
||||
enum entry A2
|
||||
|
||||
enum entry A3
|
||||
|
||||
private constructor A(/*0*/ v: A)
|
||||
public final override /*1*/ /*fake_override*/ val name: kotlin.String
|
||||
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// FIR_IDENTICAL
|
||||
enum class JvmTarget(val description: String) {
|
||||
JVM_1_6("1.6"),
|
||||
JVM_1_8("1.8"),
|
||||
JVM_9("9"),
|
||||
JVM_10("10"),
|
||||
JVM_11("11"),
|
||||
JVM_12("12"),
|
||||
JVM_13("13"),
|
||||
JVM_14("14"),
|
||||
JVM_15("15"),
|
||||
;
|
||||
|
||||
// Should not report UNINITIALIZED_ENUM_ENTRY
|
||||
val bytecodeVersion: String by lazy {
|
||||
when (this) {
|
||||
JVM_1_6 -> "Opcodes.V1_6"
|
||||
JVM_1_8 -> "Opcodes.V1_8"
|
||||
JVM_9 -> "Opcodes.V9"
|
||||
JVM_10 -> "Opcodes.V10"
|
||||
JVM_11 -> "Opcodes.V11"
|
||||
JVM_12 -> "Opcodes.V12"
|
||||
JVM_13 -> "Opcodes.V12 + 1"
|
||||
JVM_14 -> "Opcodes.V12 + 2"
|
||||
JVM_15 -> "Opcodes.V12 + 3"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package
|
||||
|
||||
public final enum class JvmTarget : kotlin.Enum<JvmTarget> {
|
||||
enum entry JVM_1_6
|
||||
|
||||
enum entry JVM_1_8
|
||||
|
||||
enum entry JVM_9
|
||||
|
||||
enum entry JVM_10
|
||||
|
||||
enum entry JVM_11
|
||||
|
||||
enum entry JVM_12
|
||||
|
||||
enum entry JVM_13
|
||||
|
||||
enum entry JVM_14
|
||||
|
||||
enum entry JVM_15
|
||||
|
||||
private constructor JvmTarget(/*0*/ description: kotlin.String)
|
||||
public final val bytecodeVersion: kotlin.String
|
||||
public final val description: kotlin.String
|
||||
public final override /*1*/ /*fake_override*/ val name: kotlin.String
|
||||
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
|
||||
protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
|
||||
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: JvmTarget): kotlin.Int
|
||||
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
protected/*protected and package*/ final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun finalize(): kotlin.Unit
|
||||
public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class<JvmTarget!>!
|
||||
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
|
||||
// Static members
|
||||
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): JvmTarget
|
||||
public final /*synthesized*/ fun values(): kotlin.Array<JvmTarget>
|
||||
}
|
||||
Generated
+6
@@ -30996,6 +30996,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/elvisOnUnitInLet.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("enumEntryInitialization.kt")
|
||||
public void testEnumEntryInitialization() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/enumEntryInitialization.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exitProcess.kt")
|
||||
public void testExitProcess() throws Exception {
|
||||
|
||||
+7
-6
@@ -18,10 +18,7 @@ import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.DiagnosticParamet
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.FirTypeRef
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
|
||||
@@ -30,7 +27,6 @@ import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
|
||||
import org.jetbrains.kotlin.psi.KtParameter
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.full.createType
|
||||
@@ -147,6 +143,11 @@ private object FirToKtConversionCreator {
|
||||
KtClassLikeSymbol::class.createType(),
|
||||
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirClass")
|
||||
),
|
||||
FirRegularClassSymbol::class to HLFunctionCallConversion(
|
||||
"firSymbolBuilder.classifierBuilder.buildClassLikeSymbol({0}.fir)",
|
||||
KtClassLikeSymbol::class.createType(),
|
||||
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirRegularClass")
|
||||
),
|
||||
FirMemberDeclaration::class to HLFunctionCallConversion(
|
||||
"firSymbolBuilder.buildSymbol({0} as FirDeclaration)",
|
||||
KtSymbol::class.createType(),
|
||||
@@ -171,7 +172,7 @@ private object FirToKtConversionCreator {
|
||||
KtType::class.createType()
|
||||
),
|
||||
FirPropertySymbol::class to HLFunctionCallConversion(
|
||||
"firSymbolBuilder.variableLikeBuilder.buildVariableSymbol({0}.fir as FirProperty)",
|
||||
"firSymbolBuilder.variableLikeBuilder.buildVariableSymbol({0}.fir)",
|
||||
KtVariableSymbol::class.createType(),
|
||||
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirProperty")
|
||||
),
|
||||
|
||||
+18
-3
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
|
||||
import org.jetbrains.kotlin.fir.declarations.FirVariable
|
||||
import org.jetbrains.kotlin.fir.psi
|
||||
@@ -1471,7 +1472,21 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
}
|
||||
add(FirErrors.UNINITIALIZED_VARIABLE) { firDiagnostic ->
|
||||
UninitializedVariableImpl(
|
||||
firSymbolBuilder.variableLikeBuilder.buildVariableSymbol(firDiagnostic.a.fir as FirProperty),
|
||||
firSymbolBuilder.variableLikeBuilder.buildVariableSymbol(firDiagnostic.a.fir),
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.UNINITIALIZED_ENUM_ENTRY) { firDiagnostic ->
|
||||
UninitializedEnumEntryImpl(
|
||||
firSymbolBuilder.variableLikeBuilder.buildVariableLikeSymbol(firDiagnostic.a.fir),
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.UNINITIALIZED_ENUM_COMPANION) { firDiagnostic ->
|
||||
UninitializedEnumCompanionImpl(
|
||||
firSymbolBuilder.classifierBuilder.buildClassLikeSymbol(firDiagnostic.a.fir),
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
@@ -1485,14 +1500,14 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
}
|
||||
add(FirErrors.VAL_REASSIGNMENT_VIA_BACKING_FIELD) { firDiagnostic ->
|
||||
ValReassignmentViaBackingFieldImpl(
|
||||
firSymbolBuilder.variableLikeBuilder.buildVariableSymbol(firDiagnostic.a.fir as FirProperty),
|
||||
firSymbolBuilder.variableLikeBuilder.buildVariableSymbol(firDiagnostic.a.fir),
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.VAL_REASSIGNMENT_VIA_BACKING_FIELD_ERROR) { firDiagnostic ->
|
||||
ValReassignmentViaBackingFieldErrorImpl(
|
||||
firSymbolBuilder.variableLikeBuilder.buildVariableSymbol(firDiagnostic.a.fir as FirProperty),
|
||||
firSymbolBuilder.variableLikeBuilder.buildVariableSymbol(firDiagnostic.a.fir),
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
|
||||
+10
@@ -1044,6 +1044,16 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
abstract val variable: KtVariableSymbol
|
||||
}
|
||||
|
||||
abstract class UninitializedEnumEntry : KtFirDiagnostic<KtSimpleNameExpression>() {
|
||||
override val diagnosticClass get() = UninitializedEnumEntry::class
|
||||
abstract val enumEntry: KtVariableLikeSymbol
|
||||
}
|
||||
|
||||
abstract class UninitializedEnumCompanion : KtFirDiagnostic<KtSimpleNameExpression>() {
|
||||
override val diagnosticClass get() = UninitializedEnumCompanion::class
|
||||
abstract val enumClass: KtClassLikeSymbol
|
||||
}
|
||||
|
||||
abstract class ValReassignment : KtFirDiagnostic<KtExpression>() {
|
||||
override val diagnosticClass get() = ValReassignment::class
|
||||
abstract val variable: KtVariableLikeSymbol
|
||||
|
||||
+16
@@ -1687,6 +1687,22 @@ internal class UninitializedVariableImpl(
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class UninitializedEnumEntryImpl(
|
||||
override val enumEntry: KtVariableLikeSymbol,
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.UninitializedEnumEntry(), KtAbstractFirDiagnostic<KtSimpleNameExpression> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class UninitializedEnumCompanionImpl(
|
||||
override val enumClass: KtClassLikeSymbol,
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.UninitializedEnumCompanion(), KtAbstractFirDiagnostic<KtSimpleNameExpression> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ValReassignmentImpl(
|
||||
override val variable: KtVariableLikeSymbol,
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
|
||||
Reference in New Issue
Block a user