FIR: check assignments and references to members in constructors
I.e. emit VAL_REASSIGNMENT on repeated assignments to `this.something`, UNINITIALIZED_VARIABLE on reads of it before any assignment if there is no initializer, and CAPTURED_MEMBER_VAL_INITIALIZATION on assignments inside non-called-in-place functions and named classes. ^KT-55528 Fixed
This commit is contained in:
+4
-64
@@ -5,25 +5,15 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.cfa
|
||||
|
||||
import org.jetbrains.kotlin.contracts.description.canBeRevisited
|
||||
import org.jetbrains.kotlin.contracts.description.isDefinitelyVisited
|
||||
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.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isLateInit
|
||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.checkPropertyAccesses
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.requiresInitialization
|
||||
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
|
||||
import org.jetbrains.kotlin.fir.isCatchParameter
|
||||
import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirSyntheticPropertySymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
object FirPropertyInitializationAnalyzer : AbstractFirPropertyInitializationChecker() {
|
||||
override fun analyze(
|
||||
graph: ControlFlowGraph,
|
||||
@@ -32,55 +22,5 @@ object FirPropertyInitializationAnalyzer : AbstractFirPropertyInitializationChec
|
||||
properties: Set<FirPropertySymbol>,
|
||||
capturedWrites: Set<FirVariableAssignment>,
|
||||
context: CheckerContext
|
||||
) {
|
||||
val localProperties = properties.filterNotTo(mutableSetOf()) { it.isInitialized() }
|
||||
val reporterVisitor = PropertyReporter(data, localProperties, capturedWrites, reporter, context)
|
||||
graph.traverse(reporterVisitor)
|
||||
}
|
||||
|
||||
private fun FirVariableSymbol<*>.isInitialized(): Boolean {
|
||||
return fir.initializer != null
|
||||
|| fir.delegate != null
|
||||
|| this is FirPropertySymbol && fir.isCatchParameter == true
|
||||
}
|
||||
|
||||
private class PropertyReporter(
|
||||
val data: PropertyInitializationInfoData,
|
||||
val localProperties: Set<FirPropertySymbol>,
|
||||
val capturedWrites: Set<FirVariableAssignment>,
|
||||
val reporter: DiagnosticReporter,
|
||||
val context: CheckerContext
|
||||
) : ControlFlowGraphVisitorVoid() {
|
||||
override fun visitNode(node: CFGNode<*>) {
|
||||
// TODO: `node.isUnion` - f({ x = 1 }, { x = 2 }) - which to report?
|
||||
// Also this is currently indistinguishable from x = 1; f({}, {}).
|
||||
}
|
||||
|
||||
private val CFGNode<*>.propertySymbol: FirPropertySymbol?
|
||||
get() = (fir as? FirQualifiedAccess)?.calleeReference?.toResolvedPropertySymbol()
|
||||
|
||||
override fun visitVariableAssignmentNode(node: VariableAssignmentNode) {
|
||||
val symbol = node.propertySymbol ?: return
|
||||
if (!symbol.fir.isVal) return
|
||||
|
||||
if (node.fir in capturedWrites) {
|
||||
if (symbol.fir.isLocal) {
|
||||
reporter.reportOn(node.fir.lValue.source, FirErrors.CAPTURED_VAL_INITIALIZATION, symbol, context)
|
||||
} else {
|
||||
reporter.reportOn(node.fir.lValue.source, FirErrors.CAPTURED_MEMBER_VAL_INITIALIZATION, symbol, context)
|
||||
}
|
||||
} else if (data.getValue(node).values.any { it[symbol]?.canBeRevisited() == true }) {
|
||||
reporter.reportOn(node.fir.lValue.source, FirErrors.VAL_REASSIGNMENT, symbol, context)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitQualifiedAccessNode(node: QualifiedAccessNode) {
|
||||
val symbol = node.propertySymbol ?: return
|
||||
if (symbol in localProperties && !symbol.fir.isLateInit && symbol !is FirSyntheticPropertySymbol &&
|
||||
!data.getValue(node).values.all { it[symbol]?.isDefinitelyVisited() == true }
|
||||
) {
|
||||
reporter.reportOn(node.fir.source, FirErrors.UNINITIALIZED_VARIABLE, symbol, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
) = graph.checkPropertyAccesses(properties.filterTo(mutableSetOf()) { it.requiresInitialization }, null, context, reporter, data)
|
||||
}
|
||||
|
||||
+2
-5
@@ -13,9 +13,9 @@ import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||
|
||||
class PropertyInitializationInfoData(properties: Set<FirPropertySymbol>, graph: ControlFlowGraph) {
|
||||
class PropertyInitializationInfoData(properties: Set<FirPropertySymbol>, receiver: FirBasedSymbol<*>?, graph: ControlFlowGraph) {
|
||||
private val data by lazy(LazyThreadSafetyMode.NONE) {
|
||||
PropertyInitializationInfoCollector(properties).getData(graph)
|
||||
graph.collectDataForNode(TraverseDirection.Forward, PropertyInitializationInfoCollector(properties, receiver))
|
||||
}
|
||||
|
||||
fun getValue(node: CFGNode<*>): PathAwarePropertyInitializationInfo {
|
||||
@@ -28,9 +28,6 @@ class PropertyInitializationInfoCollector(
|
||||
private val expectedReceiver: FirBasedSymbol<*>? = null,
|
||||
private val declaredVariableCollector: DeclaredVariableCollector = DeclaredVariableCollector(),
|
||||
) : PathAwareControlFlowGraphVisitor<PropertyInitializationInfo>() {
|
||||
fun getData(graph: ControlFlowGraph) =
|
||||
graph.collectDataForNode(TraverseDirection.Forward, this)
|
||||
|
||||
companion object {
|
||||
private val EMPTY_INFO: PathAwarePropertyInitializationInfo = persistentMapOf(NormalPath to PropertyInitializationInfo.EMPTY)
|
||||
}
|
||||
|
||||
+113
-8
@@ -6,28 +6,38 @@
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.contracts.description.canBeRevisited
|
||||
import org.jetbrains.kotlin.contracts.description.isDefinitelyVisited
|
||||
import org.jetbrains.kotlin.contracts.description.isInPlace
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.fir.analysis.cfa.util.PropertyInitializationInfo
|
||||
import org.jetbrains.kotlin.fir.analysis.cfa.util.PropertyInitializationInfoCollector
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.contains
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.getModifierList
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.analysis.cfa.util.PropertyInitializationInfoData
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.*
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.BlockExitNode
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.NormalPath
|
||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
|
||||
import org.jetbrains.kotlin.fir.expressions.FirThisReceiverExpression
|
||||
import org.jetbrains.kotlin.fir.isCatchParameter
|
||||
import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.controlFlowGraph
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
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.lexer.KtTokens
|
||||
|
||||
// See old FE's [DeclarationsChecker]
|
||||
object FirMemberPropertiesChecker : FirClassChecker() {
|
||||
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val info = declaration.collectInitializationInfo()
|
||||
val info = declaration.collectInitializationInfo(context, reporter)
|
||||
var reachedDeadEnd =
|
||||
(declaration as? FirControlFlowGraphOwner)?.controlFlowGraphReference?.controlFlowGraph?.enterNode?.isDead == true
|
||||
for (innerDeclaration in declaration.declarations) {
|
||||
@@ -42,15 +52,17 @@ object FirMemberPropertiesChecker : FirClassChecker() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirClass.collectInitializationInfo(): PropertyInitializationInfo? {
|
||||
private fun FirClass.collectInitializationInfo(context: CheckerContext, reporter: DiagnosticReporter): PropertyInitializationInfo? {
|
||||
val graph = (this as? FirControlFlowGraphOwner)?.controlFlowGraphReference?.controlFlowGraph ?: return null
|
||||
val memberPropertySymbols = declarations.mapNotNullTo(mutableSetOf()) {
|
||||
(it as? FirProperty)?.takeIf { fir -> fir.initializer == null }?.symbol
|
||||
(it.symbol as? FirPropertySymbol)?.takeIf { symbol -> symbol.requiresInitialization }
|
||||
}
|
||||
if (memberPropertySymbols.isEmpty()) return null
|
||||
// TODO: this also visits non-constructor member functions...
|
||||
// TODO: also use FirPropertyInitializationAnalyzer.PropertyReporter to report reassignments or use-before-initialization
|
||||
return PropertyInitializationInfoCollector(memberPropertySymbols, symbol).getData(graph)[graph.exitNode]?.get(NormalPath)
|
||||
// TODO: merge with `FirPropertyInitializationAnalyzer` for fewer passes.
|
||||
val data = PropertyInitializationInfoData(memberPropertySymbols, symbol, graph)
|
||||
graph.checkPropertyAccesses(memberPropertySymbols, symbol, context, reporter, data)
|
||||
return data.getValue(graph.exitNode)[NormalPath]
|
||||
}
|
||||
|
||||
private fun checkProperty(
|
||||
@@ -124,3 +136,96 @@ object FirMemberPropertiesChecker : FirClassChecker() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val FirDeclaration.evaluatedInPlace: Boolean
|
||||
get() = when (this) {
|
||||
is FirAnonymousFunction -> invocationKind.isInPlace
|
||||
is FirAnonymousObject -> classKind != ClassKind.ENUM_ENTRY
|
||||
is FirConstructor -> true // child of class initialization graph
|
||||
is FirFunction, is FirClass -> false
|
||||
else -> true // property initializer, etc.
|
||||
}
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
val FirPropertySymbol.requiresInitialization: Boolean
|
||||
get() = this !is FirSyntheticPropertySymbol && !hasInitializer && !hasExplicitBackingField &&
|
||||
hasBackingField && fir.isCatchParameter != true
|
||||
|
||||
fun ControlFlowGraph.checkPropertyAccesses(
|
||||
properties: Set<FirPropertySymbol>,
|
||||
receiver: FirBasedSymbol<*>?,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter,
|
||||
data: PropertyInitializationInfoData
|
||||
) {
|
||||
// NOTE: assert(properties.all { it.requiresInitialization })
|
||||
// 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 (properties.isEmpty()) return
|
||||
|
||||
checkPropertyAccesses(properties, receiver, context, reporter, data, null, mutableMapOf())
|
||||
}
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
private fun ControlFlowGraph.checkPropertyAccesses(
|
||||
properties: Set<FirPropertySymbol>,
|
||||
receiver: FirBasedSymbol<*>?,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter,
|
||||
data: PropertyInitializationInfoData,
|
||||
scope: FirDeclaration?,
|
||||
scopes: MutableMap<FirPropertySymbol, FirDeclaration?>,
|
||||
) {
|
||||
fun FirQualifiedAccess.hasCorrectReceiver() =
|
||||
(dispatchReceiver as? FirThisReceiverExpression)?.calleeReference?.boundSymbol == receiver
|
||||
|
||||
for (node in nodes) {
|
||||
when {
|
||||
// TODO: `node.isUnion` - f({ x = 1 }, { x = 2 }) - which to report?
|
||||
// Also this is currently indistinguishable from x = 1; f({}, {}).
|
||||
|
||||
node is VariableDeclarationNode -> {
|
||||
val symbol = node.fir.symbol
|
||||
if (scope != null && receiver == null && node.fir.isVal && symbol in properties) {
|
||||
// It's OK to initialize this variable from a nested called-in-place function, but not from
|
||||
// a non-called-in-place function or a non-anonymous-object class initializer.
|
||||
scopes[symbol] = scope
|
||||
}
|
||||
}
|
||||
|
||||
node is VariableAssignmentNode -> {
|
||||
val symbol = node.fir.calleeReference.toResolvedPropertySymbol() ?: continue
|
||||
if (!symbol.fir.isVal || !node.fir.hasCorrectReceiver() || symbol !in properties) continue
|
||||
|
||||
if (scope != scopes[symbol]) {
|
||||
val error = if (receiver != null)
|
||||
FirErrors.CAPTURED_MEMBER_VAL_INITIALIZATION
|
||||
else
|
||||
FirErrors.CAPTURED_VAL_INITIALIZATION
|
||||
reporter.reportOn(node.fir.lValue.source, error, symbol, context)
|
||||
} else if (data.getValue(node).values.any { it[symbol]?.canBeRevisited() == true }) {
|
||||
reporter.reportOn(node.fir.lValue.source, FirErrors.VAL_REASSIGNMENT, symbol, context)
|
||||
}
|
||||
}
|
||||
|
||||
node is QualifiedAccessNode -> {
|
||||
val symbol = node.fir.calleeReference.toResolvedPropertySymbol() ?: continue
|
||||
if (!symbol.isLateInit && node.fir.hasCorrectReceiver() && symbol in properties &&
|
||||
data.getValue(node).values.any { it[symbol]?.isDefinitelyVisited() != true }
|
||||
) {
|
||||
reporter.reportOn(node.fir.source, FirErrors.UNINITIALIZED_VARIABLE, symbol, context)
|
||||
}
|
||||
}
|
||||
|
||||
// In the class case, subgraphs of the exit node are member functions, which are considered to not
|
||||
// be part of initialization, so any val is considered to be initialized there and the CFG is not
|
||||
// needed. The errors on reassignments will be emitted by `FirReassignmentAndInvisibleSetterChecker`.
|
||||
node is CFGNodeWithSubgraphs<*> && (receiver == null || node !== exitNode) -> {
|
||||
for (subGraph in node.subGraphs) {
|
||||
val newScope = subGraph.declaration?.takeIf { !it.evaluatedInPlace } ?: scope
|
||||
subGraph.checkPropertyAccesses(properties, receiver, context, reporter, data, newScope, scopes)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+27
-42
@@ -10,15 +10,13 @@ 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.context.findClosest
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.evaluatedInPlace
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.requiresInitialization
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.containingClassLookupTag
|
||||
import org.jetbrains.kotlin.fir.declarations.FirAnonymousInitializer
|
||||
import org.jetbrains.kotlin.fir.declarations.FirConstructor
|
||||
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
||||
import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.hasBackingField
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.visibility
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirSmartCastExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirThisReceiverExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
|
||||
@@ -29,12 +27,8 @@ import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol
|
||||
import org.jetbrains.kotlin.fir.references.toResolvedValueParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.ExpressionReceiverValue
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
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.toSymbol
|
||||
import org.jetbrains.kotlin.fir.visibilityChecker
|
||||
|
||||
object FirReassignmentAndInvisibleSetterChecker : FirVariableAssignmentChecker() {
|
||||
@@ -43,7 +37,7 @@ object FirReassignmentAndInvisibleSetterChecker : FirVariableAssignmentChecker()
|
||||
checkValReassignmentViaBackingField(expression, context, reporter)
|
||||
checkValReassignmentOnValueParameter(expression, context, reporter)
|
||||
checkAssignmentToThis(expression, context, reporter)
|
||||
checkValReassignmentOfNonLocalProperty(expression, context, reporter)
|
||||
checkValReassignment(expression, context, reporter)
|
||||
}
|
||||
|
||||
private fun checkInvisibleSetter(
|
||||
@@ -127,39 +121,30 @@ object FirReassignmentAndInvisibleSetterChecker : FirVariableAssignmentChecker()
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkValReassignmentOfNonLocalProperty(
|
||||
expression: FirVariableAssignment,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
private fun checkValReassignment(expression: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val property = expression.lValue.toResolvedPropertySymbol() ?: return
|
||||
if (property.isLocal || property.isVar) return
|
||||
val isLegal = when {
|
||||
property is FirSyntheticPropertySymbol -> false
|
||||
property.hasDelegate || !property.hasBackingField -> false
|
||||
property.hasInitializer -> false
|
||||
else -> {
|
||||
val thisReceiverSymbol = (expression.dispatchReceiver as? FirThisReceiverExpression)?.calleeReference?.boundSymbol
|
||||
thisReceiverSymbol != null && isInsideInitializationOfClass(thisReceiverSymbol, context)
|
||||
if (property.isVar) return
|
||||
// Assignments of uninitialized `val`s must be checked via CFG, since the first one is OK.
|
||||
// See `FirPropertyInitializationAnalyzer` for locals and `FirMemberPropertiesChecker` for backing fields in initializers.
|
||||
if (property.requiresInitialization && (property.isLocal || isInOwnersInitializer(expression.dispatchReceiver, context))) return
|
||||
|
||||
reporter.reportOn(expression.lValue.source, FirErrors.VAL_REASSIGNMENT, property, context)
|
||||
}
|
||||
|
||||
private fun isInOwnersInitializer(receiver: FirExpression, context: CheckerContext): Boolean {
|
||||
val uninitializedThisSymbol = (receiver as? FirThisReceiverExpression)?.calleeReference?.boundSymbol ?: return false
|
||||
var foundInitializer = false
|
||||
for ((i, declaration) in context.containingDeclarations.withIndex()) {
|
||||
if (declaration is FirClass) {
|
||||
foundInitializer = if (context.containingDeclarations.getOrNull(i + 1)?.evaluatedInPlace == false) {
|
||||
// In member function of a class, assume all outer classes are already initialized
|
||||
// by the time this function is called.
|
||||
false
|
||||
} else {
|
||||
foundInitializer || declaration.symbol == uninitializedThisSymbol
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isLegal) {
|
||||
reporter.reportOn(expression.lValue.source, FirErrors.VAL_REASSIGNMENT, property, context)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isInsideInitializationOfClass(classSymbol: FirBasedSymbol<*>, context: CheckerContext): Boolean {
|
||||
val session = context.session
|
||||
for (containingDeclaration in context.containingDeclarations) {
|
||||
val containingClassSymbol = when (containingDeclaration) {
|
||||
is FirProperty -> containingDeclaration.containingClassLookupTag()?.toSymbol(session)
|
||||
is FirAnonymousInitializer -> containingDeclaration.dispatchReceiverType?.toSymbol(session)
|
||||
is FirConstructor -> containingDeclaration.containingClassLookupTag()?.toSymbol(session)
|
||||
else -> null
|
||||
} ?: continue
|
||||
|
||||
if (containingClassSymbol == classSymbol) return true
|
||||
}
|
||||
return false
|
||||
return foundInitializer
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ class ControlFlowAnalysisDiagnosticComponent(
|
||||
|
||||
val (properties, capturedWrites) = LocalPropertyAndCapturedWriteCollector.collect(graph)
|
||||
if (properties.isNotEmpty()) {
|
||||
val data = PropertyInitializationInfoData(properties, graph)
|
||||
val data = PropertyInitializationInfoData(properties, receiver = null, graph)
|
||||
variableAssignmentCheckers.forEach { it.analyze(graph, reporter, data, properties, capturedWrites, context) }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user