[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:
+35
-21
@@ -13,12 +13,11 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
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.hasDiagnosticKind
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.hasBackingField
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.hasExplicitBackingField
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isExternal
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isLateInit
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.*
|
||||
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.isCatchParameter
|
||||
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.impl.FirPropertySymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirSyntheticPropertySymbol
|
||||
import org.jetbrains.kotlin.fir.types.resolvedType
|
||||
|
||||
object FirPropertyInitializationAnalyzer : AbstractFirPropertyInitializationChecker() {
|
||||
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
|
||||
* of class initialization section. In this case the fact that property has initializer does
|
||||
* not mean that it's safe to access this property in any place:
|
||||
* [isForInitialization] means that caller is interested in member property in the scope
|
||||
* of file or class initialization section. In this case the fact that property has
|
||||
* initializer does not mean that it's safe to access this property in any place:
|
||||
*
|
||||
* ```
|
||||
* class A {
|
||||
* val b = a // a is not initialized here
|
||||
* val a = 10
|
||||
* val c = a // but initialized here
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
fun FirPropertySymbol.requiresInitialization(isForClassInitialization: Boolean): Boolean {
|
||||
fun FirPropertySymbol.requiresInitialization(isForInitialization: Boolean): Boolean {
|
||||
val hasImplicitBackingField = !hasExplicitBackingField && hasBackingField
|
||||
return when {
|
||||
this is FirSyntheticPropertySymbol -> false
|
||||
isForClassInitialization -> hasDelegate || hasImplicitBackingField
|
||||
isForInitialization -> hasDelegate || hasImplicitBackingField
|
||||
else -> !hasInitializer && hasImplicitBackingField && fir.isCatchParameter != true
|
||||
}
|
||||
}
|
||||
|
||||
fun PropertyInitializationInfoData.checkPropertyAccesses(
|
||||
isForClassInitialization: Boolean,
|
||||
isForInitialization: Boolean,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
// 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.
|
||||
val filtered = properties.filterTo(mutableSetOf()) { it.requiresInitialization(isForClassInitialization) }
|
||||
val filtered = properties.filterTo(mutableSetOf()) { it.requiresInitialization(isForInitialization) }
|
||||
if (filtered.isEmpty()) return
|
||||
|
||||
checkPropertyAccesses(
|
||||
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,
|
||||
reporter: DiagnosticReporter,
|
||||
scope: FirDeclaration?,
|
||||
isForClassInitialization: Boolean,
|
||||
isForInitialization: Boolean,
|
||||
doNotReportUninitializedVariable: Boolean,
|
||||
scopes: MutableMap<FirPropertySymbol, FirDeclaration?>
|
||||
doNotReportConstantUninitialized: Boolean,
|
||||
scopes: MutableMap<FirPropertySymbol, FirDeclaration?>,
|
||||
) {
|
||||
fun FirQualifiedAccessExpression.hasCorrectReceiver() =
|
||||
(dispatchReceiver?.unwrapSmartcastExpression() as? FirThisReceiverExpression)?.calleeReference?.boundSymbol == receiver
|
||||
@@ -126,7 +131,9 @@ private fun PropertyInitializationInfoData.checkPropertyAccesses(
|
||||
|
||||
node is QualifiedAccessNode -> {
|
||||
if (doNotReportUninitializedVariable) continue
|
||||
if (node.fir.resolvedType.hasDiagnosticKind(DiagnosticKind.RecursionInImplicitTypes)) continue
|
||||
val symbol = node.fir.calleeReference.toResolvedPropertySymbol() ?: continue
|
||||
if (doNotReportConstantUninitialized && symbol.isConst) continue
|
||||
if (!symbol.isLateInit && !symbol.isExternal && node.fir.hasCorrectReceiver() && symbol in properties &&
|
||||
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,
|
||||
* then it most likely will be called after object will be initialized
|
||||
*/
|
||||
val doNotReportForSubGraph = doNotReportUninitializedVariable ||
|
||||
(isForClassInitialization && subGraph.kind.doNotReportUninitializedVariableForClassInitialization)
|
||||
val doNotReportForSubGraph = isForInitialization && subGraph.kind.doNotReportUninitializedVariableForInitialization
|
||||
|
||||
// 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
|
||||
checkPropertyAccesses(
|
||||
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) {
|
||||
Kind.AnonymousFunction, Kind.LocalFunction -> true
|
||||
Kind.Function, Kind.AnonymousFunction, Kind.LocalFunction -> true
|
||||
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.declarations.*
|
||||
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.impl.FirEmptyExpressionBlock
|
||||
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.OperatorNameConventions
|
||||
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"))
|
||||
|
||||
@@ -828,4 +832,9 @@ fun ConeKotlinType.leastUpperBound(session: FirSession): ConeKotlinType {
|
||||
|
||||
fun ConeKotlinType.fullyExpandedClassId(session: FirSession): 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
|
||||
}
|
||||
|
||||
+2
-2
@@ -10,9 +10,9 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
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.declarations.FirProperty
|
||||
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
|
||||
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
|
||||
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirImplicitInvokeCall
|
||||
@@ -33,7 +33,7 @@ object FirDelegatedPropertyChecker : FirPropertyChecker() {
|
||||
|
||||
if (delegateType is ConeErrorType) {
|
||||
// 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
|
||||
reporter.reportOn(source, FirErrors.RECURSION_IN_IMPLICIT_TYPES, context)
|
||||
}
|
||||
|
||||
+2
-2
@@ -47,12 +47,12 @@ object FirMemberPropertiesChecker : FirClassChecker() {
|
||||
private fun FirClass.collectInitializationInfo(context: CheckerContext, reporter: DiagnosticReporter): PropertyInitializationInfo? {
|
||||
val graph = (this as? FirControlFlowGraphOwner)?.controlFlowGraphReference?.controlFlowGraph ?: return null
|
||||
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
|
||||
// TODO, KT-59803: merge with `FirPropertyInitializationAnalyzer` for fewer passes.
|
||||
val data = PropertyInitializationInfoData(memberPropertySymbols, symbol, graph)
|
||||
data.checkPropertyAccesses(isForClassInitialization = true, context, reporter)
|
||||
data.checkPropertyAccesses(isForInitialization = true, context, reporter)
|
||||
return data.getValue(graph.exitNode)[NormalPath]
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -58,13 +58,13 @@ object FirTopLevelPropertiesChecker : FirFileChecker() {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
// TODO, KT-59803: merge with `FirPropertyInitializationAnalyzer` for fewer passes.
|
||||
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]
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -143,11 +143,11 @@ object FirReassignmentAndInvisibleSetterChecker : FirVariableAssignmentChecker()
|
||||
// and `FirTopLevelPropertiesChecker` for top-level properties.
|
||||
if (
|
||||
(property.isLocal || isInFileGraph(property, context))
|
||||
&& property.requiresInitialization(isForClassInitialization = false)
|
||||
&& property.requiresInitialization(isForInitialization = false)
|
||||
) return
|
||||
if (
|
||||
isInOwnersInitializer(expression.dispatchReceiver?.unwrapSmartcastExpression(), context)
|
||||
&& property.requiresInitialization(isForClassInitialization = true)
|
||||
&& property.requiresInitialization(isForInitialization = true)
|
||||
) return
|
||||
|
||||
reporter.reportOn(expression.lValue.source, FirErrors.VAL_REASSIGNMENT, property, context)
|
||||
|
||||
+2
-2
@@ -9,8 +9,8 @@ import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.hasDiagnosticKind
|
||||
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.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirLambdaArgumentExpression
|
||||
@@ -28,7 +28,7 @@ object FirRecursiveProblemChecker : FirBasicExpressionChecker() {
|
||||
}
|
||||
|
||||
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
|
||||
reporter.reportOn(source, FirErrors.TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM, context)
|
||||
} else if (coneType is ConeClassLikeType) {
|
||||
|
||||
+5
-4
@@ -50,10 +50,11 @@ object CanBeValChecker : AbstractFirPropertyInitializationChecker() {
|
||||
|
||||
override fun visitVariableAssignmentNode(node: VariableAssignmentNode) {
|
||||
val symbol = node.fir.calleeReference?.toResolvedPropertySymbol() ?: return
|
||||
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 })
|
||||
) {
|
||||
reassigned.add(symbol)
|
||||
if (symbol.isVar && symbol.source?.kind !is KtFakeSourceElementKind && symbol in data.properties) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-2
@@ -187,7 +187,11 @@ class ControlFlowGraphBuilder {
|
||||
val localFunctionNode = runIf(function is FirSimpleFunction && function.isLocal && bodyBuildingMode) {
|
||||
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) {
|
||||
createFunctionEnterNode(it) to createFunctionExitNode(it).also { exit -> exitTargetsForReturn[it.symbol] = exit }
|
||||
}
|
||||
@@ -411,7 +415,8 @@ class ControlFlowGraphBuilder {
|
||||
|
||||
var lastNode: CFGNode<*> = enterNode
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -34,6 +34,7 @@ class ControlFlowGraph(val declaration: FirDeclaration?, val name: String, val k
|
||||
enum class Kind {
|
||||
File,
|
||||
Class,
|
||||
Constructor,
|
||||
Function,
|
||||
LocalFunction,
|
||||
AnonymousFunction,
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
var x : Int = 1 + x
|
||||
var x : Int = 1 + <!UNINITIALIZED_VARIABLE!>x<!>
|
||||
get() : Int = 1
|
||||
set(value : <!WRONG_SETTER_PARAMETER_TYPE!>Long<!>) {
|
||||
field = value.toInt()
|
||||
|
||||
+112
-24
@@ -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
|
||||
|
||||
val a: Int
|
||||
val b = 1.also { a = 2 }
|
||||
<!MUST_BE_INITIALIZED!>val c: Int<!>
|
||||
val d by lazy { <!CAPTURED_VAL_INITIALIZATION!>c<!> = 2; 1 }
|
||||
val e: Int
|
||||
get() {
|
||||
<!VAL_REASSIGNMENT!>c<!> = 3
|
||||
return c
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
inline fun <T> inPlaceRun(block: () -> T): T {
|
||||
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 i: Int
|
||||
val j = 1.also { i = 2 }
|
||||
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>val k: Int<!>
|
||||
val l by lazy { <!CAPTURED_MEMBER_VAL_INITIALIZATION!>k<!> = 2; 1 }
|
||||
val n: Int
|
||||
get() {
|
||||
<!VAL_REASSIGNMENT!>k<!> = 3
|
||||
return k
|
||||
}
|
||||
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"
|
||||
|
||||
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 x: Int
|
||||
val y = 1.also { x = 2 }
|
||||
val z: Int
|
||||
val w by lazy { <!CAPTURED_VAL_INITIALIZATION!>z<!> = 2; 1 }
|
||||
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!>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"
|
||||
|
||||
+112
-24
@@ -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
|
||||
|
||||
val a: Int
|
||||
val b = 1.also { a = 2 }
|
||||
<!MUST_BE_INITIALIZED!>val c: Int<!>
|
||||
val d by lazy { c = 2; 1 }
|
||||
val e: Int
|
||||
get() {
|
||||
<!VAL_REASSIGNMENT!>c<!> = 3
|
||||
return c
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
inline fun <T> inPlaceRun(block: () -> T): T {
|
||||
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 i: Int
|
||||
val j = 1.also { i = 2 }
|
||||
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>val k: Int<!>
|
||||
val l by lazy { <!CAPTURED_MEMBER_VAL_INITIALIZATION!>k<!> = 2; 1 }
|
||||
val n: Int
|
||||
get() {
|
||||
<!VAL_REASSIGNMENT!>k<!> = 3
|
||||
return k
|
||||
}
|
||||
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"
|
||||
|
||||
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 x: Int
|
||||
val y = 1.also { x = 2 }
|
||||
val z: Int
|
||||
val w by lazy { <!CAPTURED_VAL_INITIALIZATION!>z<!> = 2; 1 }
|
||||
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(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"
|
||||
|
||||
Vendored
+1
-1
@@ -255,7 +255,7 @@ class Outer() {
|
||||
}
|
||||
|
||||
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 c = 1
|
||||
}
|
||||
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
package a
|
||||
|
||||
val a : Int = b
|
||||
val b : Int = a
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
// FIR_IDENTICAL
|
||||
package a
|
||||
|
||||
val a : Int = <!UNINITIALIZED_VARIABLE!>b<!>
|
||||
val b : Int = a
|
||||
val b : Int = a
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
package aa
|
||||
|
||||
val a : Int = b
|
||||
val b : Int = a + b
|
||||
val a : Int = <!UNINITIALIZED_VARIABLE!>b<!>
|
||||
val b : Int = a + <!UNINITIALIZED_VARIABLE!>b<!>
|
||||
|
||||
class C {
|
||||
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 c by d
|
||||
val c by <!UNINITIALIZED_VARIABLE!>d<!>
|
||||
val d by <!RECURSION_IN_IMPLICIT_TYPES, TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!>c<!>
|
||||
|
||||
class Delegate(i: Int) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
class Test {
|
||||
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<!>;
|
||||
|
||||
Reference in New Issue
Block a user