5
5
6
6
package org.jetbrains.kotlin.fir.resolve.dfa
7
7
8
+ import org.jetbrains.kotlin.KtFakeSourceElementKind
8
9
import org.jetbrains.kotlin.contracts.description.isInPlace
9
10
import org.jetbrains.kotlin.fir.FirElement
10
11
import org.jetbrains.kotlin.fir.FirSession
11
12
import org.jetbrains.kotlin.fir.declarations.*
12
13
import org.jetbrains.kotlin.fir.expressions.*
13
- import org.jetbrains.kotlin.fir.expressions.explicitReceiver
14
14
import org.jetbrains.kotlin.fir.references.FirNamedReference
15
15
import org.jetbrains.kotlin.fir.references.FirReference
16
16
import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol
17
17
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
18
18
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
19
+ import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
19
20
import org.jetbrains.kotlin.fir.types.ConeKotlinType
20
21
import org.jetbrains.kotlin.fir.types.typeContext
21
22
import org.jetbrains.kotlin.fir.visitors.FirVisitor
@@ -31,7 +32,7 @@ import org.jetbrains.kotlin.types.AbstractTypeChecker
31
32
**/
32
33
internal class FirLocalVariableAssignmentAnalyzer {
33
34
private var rootFunction: FirFunctionSymbol <* >? = null
34
- private var assignedLocalVariablesByDeclaration: Map <FirBasedSymbol <* >, Fork > ? = null
35
+ private var assignedLocalVariablesByDeclaration: Map <Any / * FirBasedSymbol <* > | FirLoop */ , Fork > ? = null
35
36
private var variableAssignments: Map <FirProperty , List <Assignment >>? = null
36
37
37
38
private val scopes: Stack <Pair <Fork ?, VariableAssignments >> = stackOf()
@@ -82,14 +83,14 @@ internal class FirLocalVariableAssignmentAnalyzer {
82
83
return assignments.all { AbstractTypeChecker .isSubtypeOf(session.typeContext, it.type!! , targetType) }
83
84
}
84
85
85
- private fun getInfoForDeclaration (symbol : FirBasedSymbol < * > ): Fork ? {
86
+ private fun getInfoForDeclaration (symbol : Any ): Fork ? {
86
87
val root = rootFunction ? : return null
87
88
if (root == symbol) return null
88
89
val cachedMap = buildInfoForRoot(root)
89
90
return cachedMap[symbol]
90
91
}
91
92
92
- private fun buildInfoForRoot (root : FirFunctionSymbol <* >): Map <FirBasedSymbol < * > , Fork> {
93
+ private fun buildInfoForRoot (root : FirFunctionSymbol <* >): Map <Any , Fork > {
93
94
assignedLocalVariablesByDeclaration?.let { return it }
94
95
95
96
val data = MiniCfgBuilder .MiniCfgData ()
@@ -102,7 +103,7 @@ internal class FirLocalVariableAssignmentAnalyzer {
102
103
}
103
104
104
105
private fun enterScope (
105
- symbol : FirBasedSymbol < * > ,
106
+ symbol : Any ,
106
107
evaluatedInPlace : Boolean ,
107
108
): Pair <Fork ?, VariableAssignments > {
108
109
val currentInfo = getInfoForDeclaration(symbol)
@@ -134,11 +135,15 @@ internal class FirLocalVariableAssignmentAnalyzer {
134
135
return scopes.top()
135
136
}
136
137
137
- fun enterFunction (function : FirFunction ) {
138
+ /* *
139
+ * Enters an [FirFunction] and returns all [FirPropertySymbol]s which are defined before the function that will be modified within the
140
+ * function.
141
+ */
142
+ fun enterFunction (function : FirFunction ): Set <FirPropertySymbol > {
138
143
if (rootFunction == null ) {
139
144
rootFunction = function.symbol
140
145
scopes.push(null to VariableAssignments ())
141
- return
146
+ return emptySet()
142
147
}
143
148
val (info, prohibitSmartCasts) =
144
149
enterScope(function.symbol, function is FirAnonymousFunction && function.invocationKind.isInPlace)
@@ -149,6 +154,7 @@ internal class FirLocalVariableAssignmentAnalyzer {
149
154
}
150
155
}
151
156
}
157
+ return scopes.top().first?.assignedInside?.getAssignedProperties().orEmpty()
152
158
}
153
159
154
160
fun exitFunction () {
@@ -202,6 +208,24 @@ internal class FirLocalVariableAssignmentAnalyzer {
202
208
}
203
209
}
204
210
211
+ /* *
212
+ * Enters an [FirLoop] and returns all [FirPropertySymbol]s which are defined before the loop that will be modified within the loop.
213
+ */
214
+ fun enterLoop (loop : FirLoop ): Set <FirPropertySymbol > {
215
+ if (rootFunction == null ) return emptySet()
216
+ val (info, _) = enterScope(loop, evaluatedInPlace = true )
217
+ return info?.assignedInside?.getAssignedProperties().orEmpty()
218
+ }
219
+
220
+ /* *
221
+ * Exits an [FirLoop] and returns all [FirPropertySymbol]s which were defined before the loop that were modified within the loop.
222
+ */
223
+ fun exitLoop (): Set <FirPropertySymbol > {
224
+ if (rootFunction == null ) return emptySet()
225
+ val (info, _) = scopes.pop()
226
+ return info?.assignedInside?.getAssignedProperties().orEmpty()
227
+ }
228
+
205
229
fun visitAssignment (property : FirProperty , type : ConeKotlinType ) {
206
230
buildInfoForRoot(rootFunction ? : return )
207
231
val assignments = variableAssignments?.get(property) ? : return
@@ -293,6 +317,7 @@ internal class FirLocalVariableAssignmentAnalyzer {
293
317
)
294
318
295
319
private class Assignment (
320
+ val operatorAssignment : Boolean ,
296
321
var type : ConeKotlinType ? = null ,
297
322
)
298
323
@@ -329,6 +354,13 @@ internal class FirLocalVariableAssignmentAnalyzer {
329
354
}
330
355
331
356
fun isEmpty (): Boolean = assignments.isEmpty()
357
+
358
+ fun getAssignedProperties (): Set <FirPropertySymbol > {
359
+ return assignments.entries
360
+ // TODO(KT-57563): Operator assignments should be treated just like any other assignment.
361
+ .filter { (_, v) -> v.any { ! it.operatorAssignment } }
362
+ .mapTo(mutableSetOf ()) { (k, _) -> k.symbol }
363
+ }
332
364
}
333
365
334
366
private class MiniFlow (val parents : Set <MiniFlow >) {
@@ -412,8 +444,8 @@ internal class FirLocalVariableAssignmentAnalyzer {
412
444
// All forks in the loop should have the same set of variables assigned later, equal to the set
413
445
// at the start of the loop.
414
446
data.flow.recordAssignments(assignedInside)
415
- // The loop flows are detached from the entry flow, so we need to re-join them.
416
- data.flow = setOf (entry, data.flow).join( )
447
+ data. flow = entry.fork()
448
+ data.forks[loop] = Fork ( data.flow.assignedLater, assignedInside )
417
449
}
418
450
419
451
override fun visitWhileLoop (whileLoop : FirWhileLoop , data : MiniCfgData ) =
@@ -457,21 +489,24 @@ internal class FirLocalVariableAssignmentAnalyzer {
457
489
override fun visitVariableAssignment (variableAssignment : FirVariableAssignment , data : MiniCfgData ) {
458
490
visitElement(variableAssignment, data)
459
491
if (variableAssignment.explicitReceiver != null ) return
460
- variableAssignment.calleeReference?.let { data.recordAssignment(it) }
492
+ variableAssignment.calleeReference?.let {
493
+ val operatorAssignment = variableAssignment.source?.kind is KtFakeSourceElementKind .DesugaredIncrementOrDecrement
494
+ data.recordAssignment(it, operatorAssignment)
495
+ }
461
496
}
462
497
463
498
override fun visitAssignmentOperatorStatement (assignmentOperatorStatement : FirAssignmentOperatorStatement , data : MiniCfgData ) {
464
499
visitElement(assignmentOperatorStatement, data)
465
500
val lhs = assignmentOperatorStatement.leftArgument as ? FirQualifiedAccessExpression ? : return
466
501
if (lhs.explicitReceiver != null ) return
467
- data.recordAssignment(lhs.calleeReference)
502
+ data.recordAssignment(lhs.calleeReference, operatorAssignment = true )
468
503
}
469
504
470
- private fun MiniCfgData.recordAssignment (reference : FirReference ) {
505
+ private fun MiniCfgData.recordAssignment (reference : FirReference , operatorAssignment : Boolean ) {
471
506
val name = (reference as ? FirNamedReference )?.name ? : return
472
507
val property = variableDeclarations.lastOrNull { name in it }?.get(name) ? : return
473
508
474
- val assignment = Assignment ()
509
+ val assignment = Assignment (operatorAssignment )
475
510
assignments.getOrPut(property) { mutableListOf () }.add(assignment)
476
511
flow.recordAssignment(property, assignment)
477
512
}
@@ -492,7 +527,7 @@ internal class FirLocalVariableAssignmentAnalyzer {
492
527
var flow: MiniFlow = MiniFlow .start()
493
528
val variableDeclarations: ArrayDeque <MutableMap <Name , FirProperty >> = ArrayDeque (listOf (mutableMapOf ()))
494
529
val assignments: MutableMap <FirProperty , MutableList <Assignment >> = mutableMapOf ()
495
- val forks: MutableMap <FirBasedSymbol <* >, Fork > = mutableMapOf ()
530
+ val forks: MutableMap <Any / * FirBasedSymbol <* > | FirLoop */ , Fork > = mutableMapOf ()
496
531
}
497
532
}
498
533
}
0 commit comments