[FIR] Report UNINITIALIZED_VARIABLE for top-level properties

When checking top-level properties, check with initialization enabled.
This makes sure the same errors are reported for member and top-level
properties.
This commit is contained in:
Brian Norman
2023-08-31 11:30:08 -05:00
committed by Space Team
parent 1a35d01fe1
commit 4b6c767704
19 changed files with 300 additions and 97 deletions
@@ -13,12 +13,11 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.cfa.util.PropertyInitializationInfoData import org.jetbrains.kotlin.fir.analysis.cfa.util.PropertyInitializationInfoData
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.hasDiagnosticKind
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.hasBackingField import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.declarations.utils.hasExplicitBackingField import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.declarations.utils.isExternal
import org.jetbrains.kotlin.fir.declarations.utils.isLateInit
import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.isCatchParameter import org.jetbrains.kotlin.fir.isCatchParameter
import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol
@@ -27,10 +26,11 @@ import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph.Kind
import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirSyntheticPropertySymbol import org.jetbrains.kotlin.fir.symbols.impl.FirSyntheticPropertySymbol
import org.jetbrains.kotlin.fir.types.resolvedType
object FirPropertyInitializationAnalyzer : AbstractFirPropertyInitializationChecker() { object FirPropertyInitializationAnalyzer : AbstractFirPropertyInitializationChecker() {
override fun analyze(data: PropertyInitializationInfoData, reporter: DiagnosticReporter, context: CheckerContext) { override fun analyze(data: PropertyInitializationInfoData, reporter: DiagnosticReporter, context: CheckerContext) {
data.checkPropertyAccesses(isForClassInitialization = false, context, reporter) data.checkPropertyAccesses(isForInitialization = false, context, reporter)
} }
} }
@@ -44,40 +44,44 @@ val FirDeclaration.evaluatedInPlace: Boolean
} }
/** /**
* [isForClassInitialization] means that caller is interested in member property in the scope * [isForInitialization] means that caller is interested in member property in the scope
* of class initialization section. In this case the fact that property has initializer does * of file or class initialization section. In this case the fact that property has
* not mean that it's safe to access this property in any place: * initializer does not mean that it's safe to access this property in any place:
* *
* ```
* class A { * class A {
* val b = a // a is not initialized here * val b = a // a is not initialized here
* val a = 10 * val a = 10
* val c = a // but initialized here * val c = a // but initialized here
* } * }
* ```
*/ */
@OptIn(SymbolInternals::class) @OptIn(SymbolInternals::class)
fun FirPropertySymbol.requiresInitialization(isForClassInitialization: Boolean): Boolean { fun FirPropertySymbol.requiresInitialization(isForInitialization: Boolean): Boolean {
val hasImplicitBackingField = !hasExplicitBackingField && hasBackingField val hasImplicitBackingField = !hasExplicitBackingField && hasBackingField
return when { return when {
this is FirSyntheticPropertySymbol -> false this is FirSyntheticPropertySymbol -> false
isForClassInitialization -> hasDelegate || hasImplicitBackingField isForInitialization -> hasDelegate || hasImplicitBackingField
else -> !hasInitializer && hasImplicitBackingField && fir.isCatchParameter != true else -> !hasInitializer && hasImplicitBackingField && fir.isCatchParameter != true
} }
} }
fun PropertyInitializationInfoData.checkPropertyAccesses( fun PropertyInitializationInfoData.checkPropertyAccesses(
isForClassInitialization: Boolean, isForInitialization: Boolean,
context: CheckerContext, context: CheckerContext,
reporter: DiagnosticReporter reporter: DiagnosticReporter
) { ) {
// If a property has an initializer (or does not need one), then any reads are OK while any writes are OK // If a property has an initializer (or does not need one), then any reads are OK while any writes are OK
// if it's a `var` and bad if it's a `val`. `FirReassignmentAndInvisibleSetterChecker` does this without a CFG. // if it's a `var` and bad if it's a `val`. `FirReassignmentAndInvisibleSetterChecker` does this without a CFG.
val filtered = properties.filterTo(mutableSetOf()) { it.requiresInitialization(isForClassInitialization) } val filtered = properties.filterTo(mutableSetOf()) { it.requiresInitialization(isForInitialization) }
if (filtered.isEmpty()) return if (filtered.isEmpty()) return
checkPropertyAccesses( checkPropertyAccesses(
graph, filtered, context, reporter, scope = null, graph, filtered, context, reporter, scope = null,
isForClassInitialization, doNotReportUninitializedVariable = false, mutableMapOf() isForInitialization,
doNotReportUninitializedVariable = false,
doNotReportConstantUninitialized = true,
scopes = mutableMapOf(),
) )
} }
@@ -88,9 +92,10 @@ private fun PropertyInitializationInfoData.checkPropertyAccesses(
context: CheckerContext, context: CheckerContext,
reporter: DiagnosticReporter, reporter: DiagnosticReporter,
scope: FirDeclaration?, scope: FirDeclaration?,
isForClassInitialization: Boolean, isForInitialization: Boolean,
doNotReportUninitializedVariable: Boolean, doNotReportUninitializedVariable: Boolean,
scopes: MutableMap<FirPropertySymbol, FirDeclaration?> doNotReportConstantUninitialized: Boolean,
scopes: MutableMap<FirPropertySymbol, FirDeclaration?>,
) { ) {
fun FirQualifiedAccessExpression.hasCorrectReceiver() = fun FirQualifiedAccessExpression.hasCorrectReceiver() =
(dispatchReceiver?.unwrapSmartcastExpression() as? FirThisReceiverExpression)?.calleeReference?.boundSymbol == receiver (dispatchReceiver?.unwrapSmartcastExpression() as? FirThisReceiverExpression)?.calleeReference?.boundSymbol == receiver
@@ -126,7 +131,9 @@ private fun PropertyInitializationInfoData.checkPropertyAccesses(
node is QualifiedAccessNode -> { node is QualifiedAccessNode -> {
if (doNotReportUninitializedVariable) continue if (doNotReportUninitializedVariable) continue
if (node.fir.resolvedType.hasDiagnosticKind(DiagnosticKind.RecursionInImplicitTypes)) continue
val symbol = node.fir.calleeReference.toResolvedPropertySymbol() ?: continue val symbol = node.fir.calleeReference.toResolvedPropertySymbol() ?: continue
if (doNotReportConstantUninitialized && symbol.isConst) continue
if (!symbol.isLateInit && !symbol.isExternal && node.fir.hasCorrectReceiver() && symbol in properties && if (!symbol.isLateInit && !symbol.isExternal && node.fir.hasCorrectReceiver() && symbol in properties &&
getValue(node).values.any { it[symbol]?.isDefinitelyVisited() != true } getValue(node).values.any { it[symbol]?.isDefinitelyVisited() != true }
) { ) {
@@ -144,13 +151,20 @@ private fun PropertyInitializationInfoData.checkPropertyAccesses(
* even if they may be not initialized at this point, because if lambda is not in-place, * even if they may be not initialized at this point, because if lambda is not in-place,
* then it most likely will be called after object will be initialized * then it most likely will be called after object will be initialized
*/ */
val doNotReportForSubGraph = doNotReportUninitializedVariable || val doNotReportForSubGraph = isForInitialization && subGraph.kind.doNotReportUninitializedVariableForInitialization
(isForClassInitialization && subGraph.kind.doNotReportUninitializedVariableForClassInitialization)
// Must report uninitialized variable if we start initializing a constant property. This
// allows "regular" properties to reference constant properties out-of-order, but all other
// property references must be in-order.
val isSubGraphConstProperty = (subGraph.declaration as? FirProperty)?.isConst == true
val newScope = subGraph.declaration?.takeIf { !it.evaluatedInPlace } ?: scope val newScope = subGraph.declaration?.takeIf { !it.evaluatedInPlace } ?: scope
checkPropertyAccesses( checkPropertyAccesses(
subGraph, properties, context, reporter, newScope, subGraph, properties, context, reporter, newScope,
isForClassInitialization, doNotReportForSubGraph, scopes isForInitialization,
doNotReportUninitializedVariable = doNotReportUninitializedVariable || doNotReportForSubGraph,
doNotReportConstantUninitialized = doNotReportConstantUninitialized && !isSubGraphConstProperty,
scopes
) )
} }
} }
@@ -158,8 +172,8 @@ private fun PropertyInitializationInfoData.checkPropertyAccesses(
} }
} }
private val Kind.doNotReportUninitializedVariableForClassInitialization: Boolean private val Kind.doNotReportUninitializedVariableForInitialization: Boolean
get() = when (this) { get() = when (this) {
Kind.AnonymousFunction, Kind.LocalFunction -> true Kind.Function, Kind.AnonymousFunction, Kind.LocalFunction -> true
else -> false else -> false
} }
@@ -23,6 +23,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.getChild import org.jetbrains.kotlin.fir.analysis.getChild
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.* import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyExpressionBlock import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyExpressionBlock
import org.jetbrains.kotlin.fir.references.FirSuperReference import org.jetbrains.kotlin.fir.references.FirSuperReference
@@ -51,6 +53,8 @@ import org.jetbrains.kotlin.types.model.TypeCheckerProviderContext
import org.jetbrains.kotlin.util.ImplementationStatus import org.jetbrains.kotlin.util.ImplementationStatus
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.util.getChildren import org.jetbrains.kotlin.util.getChildren
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
private val INLINE_ONLY_ANNOTATION_CLASS_ID: ClassId = ClassId.topLevel(FqName("kotlin.internal.InlineOnly")) private val INLINE_ONLY_ANNOTATION_CLASS_ID: ClassId = ClassId.topLevel(FqName("kotlin.internal.InlineOnly"))
@@ -828,4 +832,9 @@ fun ConeKotlinType.leastUpperBound(session: FirSession): ConeKotlinType {
fun ConeKotlinType.fullyExpandedClassId(session: FirSession): ClassId? { fun ConeKotlinType.fullyExpandedClassId(session: FirSession): ClassId? {
return fullyExpandedType(session).classId return fullyExpandedType(session).classId
} }
@OptIn(ExperimentalContracts::class)
fun ConeKotlinType.hasDiagnosticKind(kind: DiagnosticKind): Boolean {
contract { returns(true) implies (this@hasDiagnosticKind is ConeErrorType) }
return this is ConeErrorType && (diagnostic as? ConeSimpleDiagnostic)?.kind == kind
}
@@ -10,9 +10,9 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.hasDiagnosticKind
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirImplicitInvokeCall import org.jetbrains.kotlin.fir.expressions.FirImplicitInvokeCall
@@ -33,7 +33,7 @@ object FirDelegatedPropertyChecker : FirPropertyChecker() {
if (delegateType is ConeErrorType) { if (delegateType is ConeErrorType) {
// Implicit recursion type is not reported since the type ref does not have a real source. // Implicit recursion type is not reported since the type ref does not have a real source.
if (source != null && (delegateType.diagnostic as? ConeSimpleDiagnostic)?.kind == DiagnosticKind.RecursionInImplicitTypes) { if (source != null && delegateType.hasDiagnosticKind(DiagnosticKind.RecursionInImplicitTypes)) {
// skip reporting other issues in this case // skip reporting other issues in this case
reporter.reportOn(source, FirErrors.RECURSION_IN_IMPLICIT_TYPES, context) reporter.reportOn(source, FirErrors.RECURSION_IN_IMPLICIT_TYPES, context)
} }
@@ -47,12 +47,12 @@ object FirMemberPropertiesChecker : FirClassChecker() {
private fun FirClass.collectInitializationInfo(context: CheckerContext, reporter: DiagnosticReporter): PropertyInitializationInfo? { private fun FirClass.collectInitializationInfo(context: CheckerContext, reporter: DiagnosticReporter): PropertyInitializationInfo? {
val graph = (this as? FirControlFlowGraphOwner)?.controlFlowGraphReference?.controlFlowGraph ?: return null val graph = (this as? FirControlFlowGraphOwner)?.controlFlowGraphReference?.controlFlowGraph ?: return null
val memberPropertySymbols = declarations.mapNotNullTo(mutableSetOf()) { val memberPropertySymbols = declarations.mapNotNullTo(mutableSetOf()) {
(it.symbol as? FirPropertySymbol)?.takeIf { symbol -> symbol.requiresInitialization(isForClassInitialization = true) } (it.symbol as? FirPropertySymbol)?.takeIf { symbol -> symbol.requiresInitialization(isForInitialization = true) }
} }
if (memberPropertySymbols.isEmpty()) return null if (memberPropertySymbols.isEmpty()) return null
// TODO, KT-59803: merge with `FirPropertyInitializationAnalyzer` for fewer passes. // TODO, KT-59803: merge with `FirPropertyInitializationAnalyzer` for fewer passes.
val data = PropertyInitializationInfoData(memberPropertySymbols, symbol, graph) val data = PropertyInitializationInfoData(memberPropertySymbols, symbol, graph)
data.checkPropertyAccesses(isForClassInitialization = true, context, reporter) data.checkPropertyAccesses(isForInitialization = true, context, reporter)
return data.getValue(graph.exitNode)[NormalPath] return data.getValue(graph.exitNode)[NormalPath]
} }
} }
@@ -58,13 +58,13 @@ object FirTopLevelPropertiesChecker : FirFileChecker() {
} }
val propertySymbols = topLevelProperties.mapNotNullTo(mutableSetOf()) { declaration -> val propertySymbols = topLevelProperties.mapNotNullTo(mutableSetOf()) { declaration ->
(declaration.symbol as? FirPropertySymbol)?.takeIf { it.requiresInitialization(false) } (declaration.symbol as? FirPropertySymbol)?.takeIf { it.requiresInitialization(isForInitialization = true) }
} }
if (propertySymbols.isEmpty()) return null if (propertySymbols.isEmpty()) return null
// TODO, KT-59803: merge with `FirPropertyInitializationAnalyzer` for fewer passes. // TODO, KT-59803: merge with `FirPropertyInitializationAnalyzer` for fewer passes.
val data = PropertyInitializationInfoData(propertySymbols, receiver = null, graph) val data = PropertyInitializationInfoData(propertySymbols, receiver = null, graph)
data.checkPropertyAccesses(isForClassInitialization = false, context, reporter) data.checkPropertyAccesses(isForInitialization = true, context, reporter)
return data.getValue(graph.exitNode)[NormalPath] return data.getValue(graph.exitNode)[NormalPath]
} }
} }
@@ -143,11 +143,11 @@ object FirReassignmentAndInvisibleSetterChecker : FirVariableAssignmentChecker()
// and `FirTopLevelPropertiesChecker` for top-level properties. // and `FirTopLevelPropertiesChecker` for top-level properties.
if ( if (
(property.isLocal || isInFileGraph(property, context)) (property.isLocal || isInFileGraph(property, context))
&& property.requiresInitialization(isForClassInitialization = false) && property.requiresInitialization(isForInitialization = false)
) return ) return
if ( if (
isInOwnersInitializer(expression.dispatchReceiver?.unwrapSmartcastExpression(), context) isInOwnersInitializer(expression.dispatchReceiver?.unwrapSmartcastExpression(), context)
&& property.requiresInitialization(isForClassInitialization = true) && property.requiresInitialization(isForInitialization = true)
) return ) return
reporter.reportOn(expression.lValue.source, FirErrors.VAL_REASSIGNMENT, property, context) reporter.reportOn(expression.lValue.source, FirErrors.VAL_REASSIGNMENT, property, context)
@@ -9,8 +9,8 @@ import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.hasDiagnosticKind
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirLambdaArgumentExpression import org.jetbrains.kotlin.fir.expressions.FirLambdaArgumentExpression
@@ -28,7 +28,7 @@ object FirRecursiveProblemChecker : FirBasicExpressionChecker() {
} }
fun checkConeType(coneType: ConeKotlinType?) { fun checkConeType(coneType: ConeKotlinType?) {
if (coneType is ConeErrorType && (coneType.diagnostic as? ConeSimpleDiagnostic)?.kind == DiagnosticKind.RecursionInImplicitTypes) { if (coneType?.hasDiagnosticKind(DiagnosticKind.RecursionInImplicitTypes) == true) {
val source = ((expression as? FirLambdaArgumentExpression)?.expression ?: expression).source val source = ((expression as? FirLambdaArgumentExpression)?.expression ?: expression).source
reporter.reportOn(source, FirErrors.TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM, context) reporter.reportOn(source, FirErrors.TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM, context)
} else if (coneType is ConeClassLikeType) { } else if (coneType is ConeClassLikeType) {
@@ -50,10 +50,11 @@ object CanBeValChecker : AbstractFirPropertyInitializationChecker() {
override fun visitVariableAssignmentNode(node: VariableAssignmentNode) { override fun visitVariableAssignmentNode(node: VariableAssignmentNode) {
val symbol = node.fir.calleeReference?.toResolvedPropertySymbol() ?: return val symbol = node.fir.calleeReference?.toResolvedPropertySymbol() ?: return
if (symbol.isVar && symbol.source?.kind !is KtFakeSourceElementKind && symbol in data.properties && if (symbol.isVar && symbol.source?.kind !is KtFakeSourceElementKind && symbol in data.properties) {
(!symbol.requiresInitialization(isForClassInitialization = data.graph.kind == ControlFlowGraph.Kind.Class) || data.getValue(node).values.any { it[symbol]?.canBeRevisited() == true }) val isForInitialization = data.graph.kind == ControlFlowGraph.Kind.Class || data.graph.kind == ControlFlowGraph.Kind.File
) { if (!symbol.requiresInitialization(isForInitialization) || data.getValue(node).values.any { it[symbol]?.canBeRevisited() == true }) {
reassigned.add(symbol) reassigned.add(symbol)
}
} }
} }
@@ -187,7 +187,11 @@ class ControlFlowGraphBuilder {
val localFunctionNode = runIf(function is FirSimpleFunction && function.isLocal && bodyBuildingMode) { val localFunctionNode = runIf(function is FirSimpleFunction && function.isLocal && bodyBuildingMode) {
createLocalFunctionDeclarationNode(function).also { addNewSimpleNode(it) } createLocalFunctionDeclarationNode(function).also { addNewSimpleNode(it) }
} }
val kind = if (localFunctionNode != null) ControlFlowGraph.Kind.LocalFunction else ControlFlowGraph.Kind.Function val kind = when {
localFunctionNode != null -> ControlFlowGraph.Kind.LocalFunction
function is FirConstructor -> ControlFlowGraph.Kind.Constructor
else -> ControlFlowGraph.Kind.Function
}
val enterNode = enterGraph(function, name, kind) { val enterNode = enterGraph(function, name, kind) {
createFunctionEnterNode(it) to createFunctionExitNode(it).also { exit -> exitTargetsForReturn[it.symbol] = exit } createFunctionEnterNode(it) to createFunctionExitNode(it).also { exit -> exitTargetsForReturn[it.symbol] = exit }
} }
@@ -411,7 +415,8 @@ class ControlFlowGraphBuilder {
var lastNode: CFGNode<*> = enterNode var lastNode: CFGNode<*> = enterNode
for (property in properties) { for (property in properties) {
addEdge(lastNode, property.enterNode, preferredKind = EdgeKind.CfgForward, propagateDeadness = false) // Top-level property CFGs should never be linked with dead edges.
CFGNode.addEdge(lastNode, property.enterNode, kind = EdgeKind.CfgForward, propagateDeadness = false)
lastNode = property.exitNode lastNode = property.exitNode
} }
@@ -34,6 +34,7 @@ class ControlFlowGraph(val declaration: FirDeclaration?, val name: String, val k
enum class Kind { enum class Kind {
File, File,
Class, Class,
Constructor,
Function, Function,
LocalFunction, LocalFunction,
AnonymousFunction, AnonymousFunction,
+1 -1
View File
@@ -1,4 +1,4 @@
var x : Int = 1 + x var x : Int = 1 + <!UNINITIALIZED_VARIABLE!>x<!>
get() : Int = 1 get() : Int = 1
set(value : <!WRONG_SETTER_PARAMETER_TYPE!>Long<!>) { set(value : <!WRONG_SETTER_PARAMETER_TYPE!>Long<!>) {
field = value.toInt() field = value.toInt()
@@ -1,31 +1,119 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE // !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -WRONG_INVOCATION_KIND
// WITH_STDLIB // WITH_STDLIB
val a: Int import kotlin.contracts.ExperimentalContracts
val b = 1.also { a = 2 } import kotlin.contracts.InvocationKind
<!MUST_BE_INITIALIZED!>val c: Int<!> import kotlin.contracts.contract
val d by lazy { <!CAPTURED_VAL_INITIALIZATION!>c<!> = 2; 1 } import kotlin.properties.ReadOnlyProperty
val e: Int
get() { @OptIn(ExperimentalContracts::class)
<!VAL_REASSIGNMENT!>c<!> = 3 inline fun <T> inPlaceRun(block: () -> T): T {
return c contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return block()
}
fun <T> notInPlaceRun(block: () -> T): T = null!!
fun <T> simpleDelegate(value: T): ReadOnlyProperty<Any?, T> = null!!
@OptIn(ExperimentalContracts::class)
fun <T> inPlaceDelegate(block: () -> T): ReadOnlyProperty<Any?, T> {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return null!!
}
fun <T> notInPlaceDelegate(block: () -> T): ReadOnlyProperty<Any?, T> = null!!
class Some {
val a: String = <!UNINITIALIZED_VARIABLE!>a<!>
val b: String = inPlaceRun { <!UNINITIALIZED_VARIABLE!>b<!> }
val c: String = notInPlaceRun { c }
val d: String by simpleDelegate(<!UNINITIALIZED_VARIABLE!>d<!>)
val e: String by inPlaceDelegate { <!UNINITIALIZED_VARIABLE!>e<!> }
val f: String by notInPlaceDelegate { f }
val g: Int
val h = 1.also { g = 2 }
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>val i: Int<!>
val j by lazy { <!CAPTURED_MEMBER_VAL_INITIALIZATION!>i<!> = 2; 1 }
val k: Int
get() {
<!VAL_REASSIGNMENT!>i<!> = 3
return i
}
val l: Comparator<String> = object : Comparator<String> {
val delegate: Comparator<String> get() = n
override fun compare(o1: String, o2: String): Int = delegate.compare(o1, o2)
} }
class Class { val m: Comparator<String> = object : Comparator<String> by <!UNINITIALIZED_VARIABLE!>n<!> {}
val i: Int
val j = 1.also { i = 2 } val n: Comparator<String> = Comparator { _, _ -> 0 }
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>val k: Int<!>
val l by lazy { <!CAPTURED_MEMBER_VAL_INITIALIZATION!>k<!> = 2; 1 } val t: String = <!UNINITIALIZED_VARIABLE!>z<!>
val n: Int val u: String = inPlaceRun { <!UNINITIALIZED_VARIABLE!>z<!> }
get() { val v: String = notInPlaceRun { z }
<!VAL_REASSIGNMENT!>k<!> = 3 val w: String by simpleDelegate(<!UNINITIALIZED_VARIABLE!>z<!>)
return k val x: String by inPlaceDelegate { <!UNINITIALIZED_VARIABLE!>z<!> }
} val y: String by notInPlaceDelegate { z }
val z: String = "VALUE"
companion object {
const val A: String = <!UNINITIALIZED_VARIABLE!>B<!>
const val B: String = "CONSTANT"
val tConst: String = Z
val uConst: String = inPlaceRun { Z }
val vConst: String = notInPlaceRun { Z }
val wConst: String by simpleDelegate(Z)
val xConst: String by inPlaceDelegate { Z }
val yConst: String by notInPlaceDelegate { Z }
const val Z: String = "CONSTANT"
}
} }
fun main() { val a: String = <!UNINITIALIZED_VARIABLE!>a<!>
val x: Int val b: String = inPlaceRun { <!UNINITIALIZED_VARIABLE!>b<!> }
val y = 1.also { x = 2 } val c: String = notInPlaceRun { c }
val z: Int
val w by lazy { <!CAPTURED_VAL_INITIALIZATION!>z<!> = 2; 1 } val d: String by simpleDelegate(<!UNINITIALIZED_VARIABLE!>d<!>)
val e: String by inPlaceDelegate { <!UNINITIALIZED_VARIABLE!>e<!> }
val f: String by notInPlaceDelegate { f }
val g: Int
val h = 1.also { g = 2 }
<!MUST_BE_INITIALIZED!>val i: Int<!>
val j by lazy { <!CAPTURED_VAL_INITIALIZATION!>i<!> = 2; 1 }
val k: Int
get() {
<!VAL_REASSIGNMENT!>i<!> = 3
return i
}
val l: Comparator<String> = object : Comparator<String> {
val delegate: Comparator<String> get() = n
override fun compare(o1: String, o2: String): Int = delegate.compare(o1, o2)
} }
val m: Comparator<String> = object : Comparator<String> by <!UNINITIALIZED_VARIABLE!>n<!> {}
val n: Comparator<String> = Comparator { _, _ -> 0 }
val t: String = <!UNINITIALIZED_VARIABLE!>z<!>
val u: String = inPlaceRun { <!UNINITIALIZED_VARIABLE!>z<!> }
val v: String = notInPlaceRun { z }
val w: String by simpleDelegate(<!UNINITIALIZED_VARIABLE!>z<!>)
val x: String by inPlaceDelegate { <!UNINITIALIZED_VARIABLE!>z<!> }
val y: String by notInPlaceDelegate { z }
val z: String = "VALUE"
val tConst: String = Z
val uConst: String = inPlaceRun { Z }
val vConst: String = notInPlaceRun { Z }
val wConst: String by simpleDelegate(Z)
val xConst: String by inPlaceDelegate { Z }
val yConst: String by notInPlaceDelegate { Z }
const val Z: String = "CONSTANT"
const val A: String = <!UNINITIALIZED_VARIABLE!>B<!>
const val B: String = "CONSTANT"
@@ -1,31 +1,119 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE // !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -WRONG_INVOCATION_KIND
// WITH_STDLIB // WITH_STDLIB
val a: Int import kotlin.contracts.ExperimentalContracts
val b = 1.also { a = 2 } import kotlin.contracts.InvocationKind
<!MUST_BE_INITIALIZED!>val c: Int<!> import kotlin.contracts.contract
val d by lazy { c = 2; 1 } import kotlin.properties.ReadOnlyProperty
val e: Int
get() { @OptIn(ExperimentalContracts::class)
<!VAL_REASSIGNMENT!>c<!> = 3 inline fun <T> inPlaceRun(block: () -> T): T {
return c contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return block()
}
fun <T> notInPlaceRun(block: () -> T): T = null!!
fun <T> simpleDelegate(value: T): ReadOnlyProperty<Any?, T> = null!!
@OptIn(ExperimentalContracts::class)
fun <T> inPlaceDelegate(block: () -> T): ReadOnlyProperty<Any?, T> {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return null!!
}
fun <T> notInPlaceDelegate(block: () -> T): ReadOnlyProperty<Any?, T> = null!!
class Some {
val a: String = <!UNINITIALIZED_VARIABLE!>a<!>
val b: String = inPlaceRun { <!UNINITIALIZED_VARIABLE!>b<!> }
val c: String = notInPlaceRun { <!UNINITIALIZED_VARIABLE!>c<!> }
val d: String by simpleDelegate(<!DEBUG_INFO_LEAKING_THIS!>d<!>)
val e: String by inPlaceDelegate { e }
val f: String by notInPlaceDelegate { f }
val g: Int
val h = 1.also { g = 2 }
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>val i: Int<!>
val j by lazy { <!CAPTURED_MEMBER_VAL_INITIALIZATION!>i<!> = 2; 1 }
val k: Int
get() {
<!VAL_REASSIGNMENT!>i<!> = 3
return i
}
val l: Comparator<String> = object : Comparator<String> {
val delegate: Comparator<String> get() = n
override fun compare(o1: String, o2: String): Int = delegate.compare(o1, o2)
} }
class Class { val m: Comparator<String> = object : Comparator<String> by <!UNINITIALIZED_VARIABLE!>n<!> {}
val i: Int
val j = 1.also { i = 2 } val n: Comparator<String> = Comparator { _, _ -> 0 }
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>val k: Int<!>
val l by lazy { <!CAPTURED_MEMBER_VAL_INITIALIZATION!>k<!> = 2; 1 } val t: String = <!UNINITIALIZED_VARIABLE!>z<!>
val n: Int val u: String = inPlaceRun { z }
get() { val v: String = notInPlaceRun { z }
<!VAL_REASSIGNMENT!>k<!> = 3 val w: String by simpleDelegate(<!UNINITIALIZED_VARIABLE!>z<!>)
return k val x: String by inPlaceDelegate { z }
} val y: String by notInPlaceDelegate { z }
val z: String = "VALUE"
companion object {
const val A: String = <!UNINITIALIZED_VARIABLE!>B<!>
const val B: String = "CONSTANT"
val tConst: String = <!UNINITIALIZED_VARIABLE!>Z<!>
val uConst: String = inPlaceRun { Z }
val vConst: String = notInPlaceRun { Z }
val wConst: String by simpleDelegate(<!UNINITIALIZED_VARIABLE!>Z<!>)
val xConst: String by inPlaceDelegate { Z }
val yConst: String by notInPlaceDelegate { Z }
const val Z: String = "CONSTANT"
}
} }
fun main() { val a: String = <!UNINITIALIZED_VARIABLE!>a<!>
val x: Int val b: String = inPlaceRun { <!UNINITIALIZED_VARIABLE!>b<!> }
val y = 1.also { x = 2 } val c: String = notInPlaceRun { <!UNINITIALIZED_VARIABLE!>c<!> }
val z: Int
val w by lazy { <!CAPTURED_VAL_INITIALIZATION!>z<!> = 2; 1 } val d: String by simpleDelegate(d)
val e: String by inPlaceDelegate { e }
val f: String by notInPlaceDelegate { f }
val g: Int
val h = 1.also { g = 2 }
<!MUST_BE_INITIALIZED!>val i: Int<!>
val j by lazy { i = 2; 1 }
val k: Int
get() {
<!VAL_REASSIGNMENT!>i<!> = 3
return i
}
val l: Comparator<String> = object : Comparator<String> {
val delegate: Comparator<String> get() = n
override fun compare(o1: String, o2: String): Int = delegate.compare(o1, o2)
} }
val m: Comparator<String> = object : Comparator<String> by <!UNINITIALIZED_VARIABLE!>n<!> {}
val n: Comparator<String> = Comparator { _, _ -> 0 }
val t: String = <!UNINITIALIZED_VARIABLE!>z<!>
val u: String = inPlaceRun { z }
val v: String = notInPlaceRun { z }
val w: String by simpleDelegate(<!UNINITIALIZED_VARIABLE!>z<!>)
val x: String by inPlaceDelegate { z }
val y: String by notInPlaceDelegate { z }
val z: String = "VALUE"
val tConst: String = <!UNINITIALIZED_VARIABLE!>Z<!>
val uConst: String = inPlaceRun { Z }
val vConst: String = notInPlaceRun { Z }
val wConst: String by simpleDelegate(<!UNINITIALIZED_VARIABLE!>Z<!>)
val xConst: String by inPlaceDelegate { Z }
val yConst: String by notInPlaceDelegate { Z }
const val Z: String = "CONSTANT"
const val A: String = <!UNINITIALIZED_VARIABLE!>B<!>
const val B: String = "CONSTANT"
@@ -255,7 +255,7 @@ class Outer() {
} }
class ForwardAccessToBackingField() { //kt-147 class ForwardAccessToBackingField() { //kt-147
val a = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM, UNINITIALIZED_VARIABLE!>a<!> // error val a = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!>a<!> // error
val b = <!UNINITIALIZED_VARIABLE!>c<!> // error val b = <!UNINITIALIZED_VARIABLE!>c<!> // error
val c = 1 val c = 1
} }
@@ -1,4 +0,0 @@
package a
val a : Int = b
val b : Int = a
@@ -1,4 +1,5 @@
// FIR_IDENTICAL
package a package a
val a : Int = <!UNINITIALIZED_VARIABLE!>b<!> val a : Int = <!UNINITIALIZED_VARIABLE!>b<!>
val b : Int = a val b : Int = a
@@ -1,7 +1,7 @@
package aa package aa
val a : Int = b val a : Int = <!UNINITIALIZED_VARIABLE!>b<!>
val b : Int = a + b val b : Int = a + <!UNINITIALIZED_VARIABLE!>b<!>
class C { class C {
val a : Int = <!UNINITIALIZED_VARIABLE!>b<!> val a : Int = <!UNINITIALIZED_VARIABLE!>b<!>
@@ -7,7 +7,7 @@ val a by <!RECURSION_IN_IMPLICIT_TYPES, TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBL
val b by Delegate(<!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!>b<!>) val b by Delegate(<!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!>b<!>)
val c by d val c by <!UNINITIALIZED_VARIABLE!>d<!>
val d by <!RECURSION_IN_IMPLICIT_TYPES, TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!>c<!> val d by <!RECURSION_IN_IMPLICIT_TYPES, TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!>c<!>
class Delegate(i: Int) { class Delegate(i: Int) {
@@ -3,7 +3,7 @@
class Test { class Test {
private val y = object { private val y = object {
val a = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM, UNINITIALIZED_VARIABLE!>y<!>; val a = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!>y<!>;
} }
val z = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!>y.a<!>; val z = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!>y.a<!>;