FIR checker: report CAPTURED_VAL_INITIALIZATION
This commit is contained in:
committed by
TeamCityServer
parent
d38effcbbe
commit
f0f1e2d945
+2
-2
@@ -2,7 +2,7 @@ fun test1() {
|
|||||||
val x: Int
|
val x: Int
|
||||||
|
|
||||||
fun func() {
|
fun func() {
|
||||||
x = 0
|
<!CAPTURED_VAL_INITIALIZATION!>x<!> = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
println(<!UNINITIALIZED_VARIABLE!>x<!>)
|
println(<!UNINITIALIZED_VARIABLE!>x<!>)
|
||||||
@@ -43,4 +43,4 @@ fun test3() {
|
|||||||
|
|
||||||
println(<!UNINITIALIZED_VARIABLE!>x<!>)
|
println(<!UNINITIALIZED_VARIABLE!>x<!>)
|
||||||
println(<!UNINITIALIZED_VARIABLE!>x<!>)
|
println(<!UNINITIALIZED_VARIABLE!>x<!>)
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -691,6 +691,12 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
|
|||||||
val VAL_REASSIGNMENT_VIA_BACKING_FIELD_ERROR by error<KtExpression> {
|
val VAL_REASSIGNMENT_VIA_BACKING_FIELD_ERROR by error<KtExpression> {
|
||||||
parameter<FirPropertySymbol>("property")
|
parameter<FirPropertySymbol>("property")
|
||||||
}
|
}
|
||||||
|
val CAPTURED_VAL_INITIALIZATION by error<KtExpression> {
|
||||||
|
parameter<FirPropertySymbol>("property")
|
||||||
|
}
|
||||||
|
val CAPTURED_MEMBER_VAL_INITIALIZATION by error<KtExpression> {
|
||||||
|
parameter<FirPropertySymbol>("property")
|
||||||
|
}
|
||||||
val WRONG_INVOCATION_KIND by warning<PsiElement> {
|
val WRONG_INVOCATION_KIND by warning<PsiElement> {
|
||||||
parameter<Symbol>("declaration")
|
parameter<Symbol>("declaration")
|
||||||
parameter<EventOccurrencesRange>("requiredRange")
|
parameter<EventOccurrencesRange>("requiredRange")
|
||||||
|
|||||||
@@ -411,6 +411,8 @@ object FirErrors {
|
|||||||
val VAL_REASSIGNMENT by error1<KtExpression, FirVariableSymbol<*>>()
|
val VAL_REASSIGNMENT by error1<KtExpression, FirVariableSymbol<*>>()
|
||||||
val VAL_REASSIGNMENT_VIA_BACKING_FIELD by warning1<KtExpression, FirPropertySymbol>()
|
val VAL_REASSIGNMENT_VIA_BACKING_FIELD by warning1<KtExpression, FirPropertySymbol>()
|
||||||
val VAL_REASSIGNMENT_VIA_BACKING_FIELD_ERROR by error1<KtExpression, FirPropertySymbol>()
|
val VAL_REASSIGNMENT_VIA_BACKING_FIELD_ERROR by error1<KtExpression, FirPropertySymbol>()
|
||||||
|
val CAPTURED_VAL_INITIALIZATION by error1<KtExpression, FirPropertySymbol>()
|
||||||
|
val CAPTURED_MEMBER_VAL_INITIALIZATION by error1<KtExpression, FirPropertySymbol>()
|
||||||
val WRONG_INVOCATION_KIND by warning3<PsiElement, AbstractFirBasedSymbol<*>, EventOccurrencesRange, EventOccurrencesRange>()
|
val WRONG_INVOCATION_KIND by warning3<PsiElement, AbstractFirBasedSymbol<*>, EventOccurrencesRange, EventOccurrencesRange>()
|
||||||
val LEAKED_IN_PLACE_LAMBDA by error1<PsiElement, AbstractFirBasedSymbol<*>>()
|
val LEAKED_IN_PLACE_LAMBDA by error1<PsiElement, AbstractFirBasedSymbol<*>>()
|
||||||
val WRONG_IMPLIES_CONDITION by warning0<PsiElement>()
|
val WRONG_IMPLIES_CONDITION by warning0<PsiElement>()
|
||||||
|
|||||||
+2
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.analysis.cfa
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.CFGNode
|
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.CFGNode
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph
|
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||||
@@ -17,6 +18,7 @@ abstract class AbstractFirPropertyInitializationChecker {
|
|||||||
reporter: DiagnosticReporter,
|
reporter: DiagnosticReporter,
|
||||||
data: Map<CFGNode<*>, PathAwarePropertyInitializationInfo>,
|
data: Map<CFGNode<*>, PathAwarePropertyInitializationInfo>,
|
||||||
properties: Set<FirPropertySymbol>,
|
properties: Set<FirPropertySymbol>,
|
||||||
|
capturedWrites: Set<FirVariableAssignment>,
|
||||||
context: CheckerContext
|
context: CheckerContext
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
|
|||||||
import org.jetbrains.kotlin.fir.FirElement
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.expressions.*
|
import org.jetbrains.kotlin.fir.expressions.*
|
||||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||||
import org.jetbrains.kotlin.fir.util.SetMultimap
|
import org.jetbrains.kotlin.fir.util.SetMultimap
|
||||||
@@ -60,21 +59,58 @@ class PropertyInitializationInfo(
|
|||||||
::EMPTY
|
::EMPTY
|
||||||
}
|
}
|
||||||
|
|
||||||
class LocalPropertyCollector private constructor() : ControlFlowGraphVisitorVoid() {
|
class LocalPropertyAndCapturedWriteCollector private constructor() : ControlFlowGraphVisitorVoid() {
|
||||||
companion object {
|
companion object {
|
||||||
fun collect(graph: ControlFlowGraph): MutableSet<FirPropertySymbol> {
|
fun collect(graph: ControlFlowGraph): Pair<Set<FirPropertySymbol>, Set<FirVariableAssignment>> {
|
||||||
val collector = LocalPropertyCollector()
|
val collector = LocalPropertyAndCapturedWriteCollector()
|
||||||
graph.traverse(TraverseDirection.Forward, collector)
|
graph.traverse(TraverseDirection.Forward, collector)
|
||||||
return collector.symbols
|
return collector.symbols.keys to collector.capturedWrites
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val symbols: MutableSet<FirPropertySymbol> = mutableSetOf()
|
// Mapping from a property symbol to its declaration context
|
||||||
|
// `true` if the (local) property is declared in the currently visited function.
|
||||||
|
// `false` if it is declared in a lambda or a local function (inside the currently visited function).
|
||||||
|
private val symbols: MutableMap<FirPropertySymbol, Boolean> = mutableMapOf()
|
||||||
|
|
||||||
|
private val lambdaOrLocalFunctionStack: MutableList<FirFunction<*>> = mutableListOf()
|
||||||
|
private val capturedWrites: MutableSet<FirVariableAssignment> = mutableSetOf()
|
||||||
|
|
||||||
override fun visitNode(node: CFGNode<*>) {}
|
override fun visitNode(node: CFGNode<*>) {}
|
||||||
|
|
||||||
override fun visitVariableDeclarationNode(node: VariableDeclarationNode) {
|
override fun visitVariableDeclarationNode(node: VariableDeclarationNode) {
|
||||||
symbols += node.fir.symbol
|
symbols[node.fir.symbol] = lambdaOrLocalFunctionStack.lastOrNull() == null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitPostponedLambdaEnterNode(node: PostponedLambdaEnterNode) {
|
||||||
|
lambdaOrLocalFunctionStack.add(node.fir)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitPostponedLambdaExitNode(node: PostponedLambdaExitNode) {
|
||||||
|
lambdaOrLocalFunctionStack.remove(node.fir)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitLocalFunctionDeclarationNode(node: LocalFunctionDeclarationNode, data: Nothing?) {
|
||||||
|
lambdaOrLocalFunctionStack.add(node.fir)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitFunctionExitNode(node: FunctionExitNode) {
|
||||||
|
lambdaOrLocalFunctionStack.remove(node.fir)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitVariableAssignmentNode(node: VariableAssignmentNode) {
|
||||||
|
// Check if this variable assignment is inside a lambda or a local function.
|
||||||
|
if (lambdaOrLocalFunctionStack.isEmpty()) return
|
||||||
|
|
||||||
|
// Check if the assigned variable doesn't belong to any lambda or local function.
|
||||||
|
val symbol = node.fir.referredPropertySymbol ?: return
|
||||||
|
if (symbol !in symbols || symbols[symbol] == false) return
|
||||||
|
|
||||||
|
// If all nested declarations are lambdas that are invoked in-place (according to the contract),
|
||||||
|
// this variable assignment is not a captured write.
|
||||||
|
if (lambdaOrLocalFunctionStack.all { it is FirAnonymousFunction && it.invocationKind.isInPlace }) return
|
||||||
|
|
||||||
|
capturedWrites.add(node.fir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,8 +146,7 @@ class PropertyInitializationInfoCollector(
|
|||||||
data: Collection<Pair<EdgeLabel, PathAwarePropertyInitializationInfo>>
|
data: Collection<Pair<EdgeLabel, PathAwarePropertyInitializationInfo>>
|
||||||
): PathAwarePropertyInitializationInfo {
|
): PathAwarePropertyInitializationInfo {
|
||||||
val dataForNode = visitNode(node, data)
|
val dataForNode = visitNode(node, data)
|
||||||
val reference = node.fir.lValue as? FirResolvedNamedReference ?: return dataForNode
|
val symbol = node.fir.referredPropertySymbol ?: return dataForNode
|
||||||
val symbol = reference.resolvedSymbol as? FirPropertySymbol ?: return dataForNode
|
|
||||||
return if (symbol !in localProperties) {
|
return if (symbol !in localProperties) {
|
||||||
dataForNode
|
dataForNode
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+2
-2
@@ -61,9 +61,9 @@ class FirControlFlowAnalyzer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun runAssignmentCfaCheckers(graph: ControlFlowGraph, reporter: DiagnosticReporter, context: CheckerContext) {
|
private fun runAssignmentCfaCheckers(graph: ControlFlowGraph, reporter: DiagnosticReporter, context: CheckerContext) {
|
||||||
val properties = LocalPropertyCollector.collect(graph)
|
val (properties, capturedWrites) = LocalPropertyAndCapturedWriteCollector.collect(graph)
|
||||||
if (properties.isEmpty()) return
|
if (properties.isEmpty()) return
|
||||||
val data = PropertyInitializationInfoCollector(properties).getData(graph)
|
val data = PropertyInitializationInfoCollector(properties).getData(graph)
|
||||||
variableAssignmentCheckers.forEach { it.analyze(graph, reporter, data, properties, context) }
|
variableAssignmentCheckers.forEach { it.analyze(graph, reporter, data, properties, capturedWrites, context) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-4
@@ -11,8 +11,11 @@ import org.jetbrains.kotlin.contracts.description.isDefinitelyVisited
|
|||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||||
import org.jetbrains.kotlin.fir.declarations.isLateInit
|
import org.jetbrains.kotlin.fir.declarations.isLateInit
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.referredPropertySymbol
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
|
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
|
||||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||||
@@ -24,6 +27,7 @@ object FirPropertyInitializationAnalyzer : AbstractFirPropertyInitializationChec
|
|||||||
reporter: DiagnosticReporter,
|
reporter: DiagnosticReporter,
|
||||||
data: Map<CFGNode<*>, PathAwarePropertyInitializationInfo>,
|
data: Map<CFGNode<*>, PathAwarePropertyInitializationInfo>,
|
||||||
properties: Set<FirPropertySymbol>,
|
properties: Set<FirPropertySymbol>,
|
||||||
|
capturedWrites: Set<FirVariableAssignment>,
|
||||||
context: CheckerContext
|
context: CheckerContext
|
||||||
) {
|
) {
|
||||||
val localData = data.filter {
|
val localData = data.filter {
|
||||||
@@ -33,21 +37,21 @@ object FirPropertyInitializationAnalyzer : AbstractFirPropertyInitializationChec
|
|||||||
|
|
||||||
val localProperties = properties.filter { it.fir.initializer == null && it.fir.delegate == null }.toSet()
|
val localProperties = properties.filter { it.fir.initializer == null && it.fir.delegate == null }.toSet()
|
||||||
|
|
||||||
val reporterVisitor = PropertyReporter(localData, localProperties, reporter, context)
|
val reporterVisitor = PropertyReporter(localData, localProperties, capturedWrites, reporter, context)
|
||||||
graph.traverse(TraverseDirection.Forward, reporterVisitor)
|
graph.traverse(TraverseDirection.Forward, reporterVisitor)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PropertyReporter(
|
private class PropertyReporter(
|
||||||
val data: Map<CFGNode<*>, PathAwarePropertyInitializationInfo>,
|
val data: Map<CFGNode<*>, PathAwarePropertyInitializationInfo>,
|
||||||
val localProperties: Set<FirPropertySymbol>,
|
val localProperties: Set<FirPropertySymbol>,
|
||||||
|
val capturedWrites: Set<FirVariableAssignment>,
|
||||||
val reporter: DiagnosticReporter,
|
val reporter: DiagnosticReporter,
|
||||||
val context: CheckerContext
|
val context: CheckerContext
|
||||||
) : ControlFlowGraphVisitorVoid() {
|
) : ControlFlowGraphVisitorVoid() {
|
||||||
override fun visitNode(node: CFGNode<*>) {}
|
override fun visitNode(node: CFGNode<*>) {}
|
||||||
|
|
||||||
private fun getPropertySymbol(node: CFGNode<*>): FirPropertySymbol? {
|
private fun getPropertySymbol(node: CFGNode<*>): FirPropertySymbol? {
|
||||||
val reference = (node.fir as? FirQualifiedAccess)?.calleeReference as? FirResolvedNamedReference ?: return null
|
return (node.fir as? FirQualifiedAccess)?.referredPropertySymbol
|
||||||
return reference.resolvedSymbol as? FirPropertySymbol
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitVariableAssignmentNode(node: VariableAssignmentNode) {
|
override fun visitVariableAssignmentNode(node: VariableAssignmentNode) {
|
||||||
@@ -66,10 +70,17 @@ object FirPropertyInitializationAnalyzer : AbstractFirPropertyInitializationChec
|
|||||||
symbol: FirPropertySymbol,
|
symbol: FirPropertySymbol,
|
||||||
node: VariableAssignmentNode
|
node: VariableAssignmentNode
|
||||||
): Boolean {
|
): Boolean {
|
||||||
|
if (symbol.fir.isVal && 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)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
val kind = info[symbol] ?: EventOccurrencesRange.ZERO
|
val kind = info[symbol] ?: EventOccurrencesRange.ZERO
|
||||||
if (symbol.fir.isVal && kind.canBeRevisited()) {
|
if (symbol.fir.isVal && kind.canBeRevisited()) {
|
||||||
node.fir.lValue.source?.let {
|
node.fir.lValue.source?.let {
|
||||||
// TODO: differentiate CAPTURED_VAL_INITIALIZATION
|
|
||||||
reporter.report(FirErrors.VAL_REASSIGNMENT.on(it, symbol), context)
|
reporter.report(FirErrors.VAL_REASSIGNMENT.on(it, symbol), context)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.fir.analysis.checkers.getChildren
|
|||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
|
||||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||||
@@ -28,6 +29,7 @@ object CanBeValChecker : AbstractFirPropertyInitializationChecker() {
|
|||||||
reporter: DiagnosticReporter,
|
reporter: DiagnosticReporter,
|
||||||
data: Map<CFGNode<*>, PathAwarePropertyInitializationInfo>,
|
data: Map<CFGNode<*>, PathAwarePropertyInitializationInfo>,
|
||||||
properties: Set<FirPropertySymbol>,
|
properties: Set<FirPropertySymbol>,
|
||||||
|
capturedWrites: Set<FirVariableAssignment>,
|
||||||
context: CheckerContext
|
context: CheckerContext
|
||||||
) {
|
) {
|
||||||
val unprocessedProperties = mutableSetOf<FirPropertySymbol>()
|
val unprocessedProperties = mutableSetOf<FirPropertySymbol>()
|
||||||
|
|||||||
+1
-1
@@ -36,7 +36,7 @@ object UnusedChecker : FirControlFlowChecker() {
|
|||||||
!it.symbol.classId.isLocal
|
!it.symbol.classId.isLocal
|
||||||
} != null
|
} != null
|
||||||
) return
|
) return
|
||||||
val properties = LocalPropertyCollector.collect(graph)
|
val (properties, _) = LocalPropertyAndCapturedWriteCollector.collect(graph)
|
||||||
if (properties.isEmpty()) return
|
if (properties.isEmpty()) return
|
||||||
|
|
||||||
val data = ValueWritesWithoutReading(context.session, properties).getData(graph)
|
val data = ValueWritesWithoutReading(context.session, properties).getData(graph)
|
||||||
|
|||||||
+12
@@ -58,6 +58,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CANNOT_INFER_PARA
|
|||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CANNOT_WEAKEN_ACCESS_PRIVILEGE
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CANNOT_WEAKEN_ACCESS_PRIVILEGE
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CAN_BE_VAL
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CAN_BE_VAL
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CAPTURED_MEMBER_VAL_INITIALIZATION
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CAPTURED_VAL_INITIALIZATION
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CATCH_PARAMETER_WITH_DEFAULT_VALUE
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CATCH_PARAMETER_WITH_DEFAULT_VALUE
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CLASS_IN_SUPERTYPE_FOR_ENUM
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CLASS_IN_SUPERTYPE_FOR_ENUM
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CLASS_LITERAL_LHS_NOT_A_CLASS
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CLASS_LITERAL_LHS_NOT_A_CLASS
|
||||||
@@ -941,6 +943,16 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
|
|||||||
map.put(VAL_REASSIGNMENT, "Val cannot be reassigned", VARIABLE_NAME)
|
map.put(VAL_REASSIGNMENT, "Val cannot be reassigned", VARIABLE_NAME)
|
||||||
map.put(VAL_REASSIGNMENT_VIA_BACKING_FIELD, "Reassignment of read-only property via backing field is deprecated", VARIABLE_NAME)
|
map.put(VAL_REASSIGNMENT_VIA_BACKING_FIELD, "Reassignment of read-only property via backing field is deprecated", VARIABLE_NAME)
|
||||||
map.put(VAL_REASSIGNMENT_VIA_BACKING_FIELD_ERROR, "Reassignment of read-only property via backing field", VARIABLE_NAME)
|
map.put(VAL_REASSIGNMENT_VIA_BACKING_FIELD_ERROR, "Reassignment of read-only property via backing field", VARIABLE_NAME)
|
||||||
|
map.put(
|
||||||
|
CAPTURED_VAL_INITIALIZATION,
|
||||||
|
"Captured values initialization is forbidden due to possible reassignment",
|
||||||
|
VARIABLE_NAME
|
||||||
|
)
|
||||||
|
map.put(
|
||||||
|
CAPTURED_MEMBER_VAL_INITIALIZATION,
|
||||||
|
"Captured member values initialization is forbidden due to possible reassignment",
|
||||||
|
VARIABLE_NAME
|
||||||
|
)
|
||||||
map.put(
|
map.put(
|
||||||
WRONG_INVOCATION_KIND,
|
WRONG_INVOCATION_KIND,
|
||||||
"{2} wrong invocation kind: given {3} case, but {4} case is possible",
|
"{2} wrong invocation kind: given {3} case, but {4} case is possible",
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.fir.resolve.dfa.cfg
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
|
||||||
|
|
||||||
|
val EventOccurrencesRange?.isInPlace: Boolean
|
||||||
|
get() = this != null
|
||||||
-3
@@ -306,9 +306,6 @@ class ControlFlowGraphBuilder {
|
|||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
private val EventOccurrencesRange?.isInPlace: Boolean
|
|
||||||
get() = this != null
|
|
||||||
|
|
||||||
fun exitAnonymousFunction(anonymousFunction: FirAnonymousFunction): Triple<FunctionExitNode, PostponedLambdaExitNode?, ControlFlowGraph> {
|
fun exitAnonymousFunction(anonymousFunction: FirAnonymousFunction): Triple<FunctionExitNode, PostponedLambdaExitNode?, ControlFlowGraph> {
|
||||||
val symbol = anonymousFunction.symbol
|
val symbol = anonymousFunction.symbol
|
||||||
val exitNode = exitsOfAnonymousFunctions.remove(symbol)!!.also {
|
val exitNode = exitsOfAnonymousFunctions.remove(symbol)!!.also {
|
||||||
|
|||||||
@@ -11,10 +11,13 @@ import org.jetbrains.kotlin.fir.declarations.builder.FirRegularClassBuilder
|
|||||||
import org.jetbrains.kotlin.fir.declarations.builder.FirTypeParameterBuilder
|
import org.jetbrains.kotlin.fir.declarations.builder.FirTypeParameterBuilder
|
||||||
import org.jetbrains.kotlin.fir.declarations.impl.FirFileImpl
|
import org.jetbrains.kotlin.fir.declarations.impl.FirFileImpl
|
||||||
import org.jetbrains.kotlin.fir.declarations.impl.FirRegularClassImpl
|
import org.jetbrains.kotlin.fir.declarations.impl.FirRegularClassImpl
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
|
||||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||||
import org.jetbrains.kotlin.fir.render
|
import org.jetbrains.kotlin.fir.render
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousObjectSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousObjectSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||||
import org.jetbrains.kotlin.fir.types.ConeFlexibleType
|
import org.jetbrains.kotlin.fir.types.ConeFlexibleType
|
||||||
@@ -216,6 +219,12 @@ val FirProperty.hasBackingField: Boolean
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val FirQualifiedAccess.referredPropertySymbol: FirPropertySymbol?
|
||||||
|
get() {
|
||||||
|
val reference = calleeReference as? FirResolvedNamedReference ?: return null
|
||||||
|
return reference.resolvedSymbol as? FirPropertySymbol
|
||||||
|
}
|
||||||
|
|
||||||
inline val FirDeclaration.isFromLibrary: Boolean
|
inline val FirDeclaration.isFromLibrary: Boolean
|
||||||
get() = origin == FirDeclarationOrigin.Library
|
get() = origin == FirDeclarationOrigin.Library
|
||||||
inline val FirDeclaration.isSynthetic: Boolean
|
inline val FirDeclaration.isSynthetic: Boolean
|
||||||
|
|||||||
+4
-4
@@ -17,14 +17,14 @@ fun foo() {
|
|||||||
fun bar() {
|
fun bar() {
|
||||||
val x: Int
|
val x: Int
|
||||||
exec {
|
exec {
|
||||||
x = 13
|
<!CAPTURED_VAL_INITIALIZATION!>x<!> = 13
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bar2() {
|
fun bar2() {
|
||||||
val x: Int
|
val x: Int
|
||||||
fun foo() {
|
fun foo() {
|
||||||
x = 3
|
<!CAPTURED_VAL_INITIALIZATION!>x<!> = 3
|
||||||
}
|
}
|
||||||
foo()
|
foo()
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ class Your {
|
|||||||
val y = if (true) {
|
val y = if (true) {
|
||||||
val xx: Int
|
val xx: Int
|
||||||
exec {
|
exec {
|
||||||
xx = 42
|
<!CAPTURED_VAL_INITIALIZATION!>xx<!> = 42
|
||||||
}
|
}
|
||||||
24
|
24
|
||||||
}
|
}
|
||||||
@@ -70,7 +70,7 @@ class Your {
|
|||||||
val z = if (true) {
|
val z = if (true) {
|
||||||
val xx: Int
|
val xx: Int
|
||||||
exec {
|
exec {
|
||||||
xx = 24
|
<!CAPTURED_VAL_INITIALIZATION!>xx<!> = 24
|
||||||
}
|
}
|
||||||
42
|
42
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -59,7 +59,7 @@ fun gau() {
|
|||||||
object: Any() {
|
object: Any() {
|
||||||
init {
|
init {
|
||||||
// Error!
|
// Error!
|
||||||
y = ""
|
<!CAPTURED_VAL_INITIALIZATION!>y<!> = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -36,7 +36,7 @@ fun branchingIndetermineFlow(a: Any?) {
|
|||||||
|
|
||||||
fun nonAnonymousLambdas() {
|
fun nonAnonymousLambdas() {
|
||||||
val x: Int
|
val x: Int
|
||||||
val initializer = { x = 42 }
|
val initializer = { <!CAPTURED_VAL_INITIALIZATION!>x<!> = 42 }
|
||||||
myRun(initializer)
|
myRun(initializer)
|
||||||
<!UNINITIALIZED_VARIABLE!>x<!>.inc()
|
<!UNINITIALIZED_VARIABLE!>x<!>.inc()
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,7 @@ fun funWithUnknownInvocations(block: () -> Unit) = block()
|
|||||||
fun nestedIndefiniteAssignment() {
|
fun nestedIndefiniteAssignment() {
|
||||||
val x: Int
|
val x: Int
|
||||||
// Captured val initialization reported, because we don't know anything about funWithUnknownInvocations
|
// Captured val initialization reported, because we don't know anything about funWithUnknownInvocations
|
||||||
funWithUnknownInvocations { myRun { x = 42 } }
|
funWithUnknownInvocations { myRun { <!CAPTURED_VAL_INITIALIZATION!>x<!> = 42 } }
|
||||||
<!UNINITIALIZED_VARIABLE!>x<!>.inc()
|
<!UNINITIALIZED_VARIABLE!>x<!>.inc()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,4 +65,4 @@ class InitializationForbiddenInNonInitSection {
|
|||||||
fun setup() {
|
fun setup() {
|
||||||
myRun { x = 42 }
|
myRun { x = 42 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -46,6 +46,6 @@ fun funWithUnknownInvocations(block: () -> Unit) = block()
|
|||||||
|
|
||||||
fun nestedIndefiniteAssignment() {
|
fun nestedIndefiniteAssignment() {
|
||||||
val x: Int
|
val x: Int
|
||||||
funWithUnknownInvocations { myRun { x = 42 } }
|
funWithUnknownInvocations { myRun { <!CAPTURED_VAL_INITIALIZATION!>x<!> = 42 } }
|
||||||
<!UNINITIALIZED_VARIABLE!>x<!>.inc()
|
<!UNINITIALIZED_VARIABLE!>x<!>.inc()
|
||||||
}
|
}
|
||||||
|
|||||||
+14
@@ -1988,6 +1988,20 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
|||||||
token,
|
token,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
add(FirErrors.CAPTURED_VAL_INITIALIZATION) { firDiagnostic ->
|
||||||
|
CapturedValInitializationImpl(
|
||||||
|
firSymbolBuilder.variableLikeBuilder.buildVariableSymbol(firDiagnostic.a.fir),
|
||||||
|
firDiagnostic as FirPsiDiagnostic<*>,
|
||||||
|
token,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
add(FirErrors.CAPTURED_MEMBER_VAL_INITIALIZATION) { firDiagnostic ->
|
||||||
|
CapturedMemberValInitializationImpl(
|
||||||
|
firSymbolBuilder.variableLikeBuilder.buildVariableSymbol(firDiagnostic.a.fir),
|
||||||
|
firDiagnostic as FirPsiDiagnostic<*>,
|
||||||
|
token,
|
||||||
|
)
|
||||||
|
}
|
||||||
add(FirErrors.WRONG_INVOCATION_KIND) { firDiagnostic ->
|
add(FirErrors.WRONG_INVOCATION_KIND) { firDiagnostic ->
|
||||||
WrongInvocationKindImpl(
|
WrongInvocationKindImpl(
|
||||||
firSymbolBuilder.buildSymbol(firDiagnostic.a.fir as FirDeclaration),
|
firSymbolBuilder.buildSymbol(firDiagnostic.a.fir as FirDeclaration),
|
||||||
|
|||||||
+10
@@ -1395,6 +1395,16 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
|
|||||||
abstract val property: KtVariableSymbol
|
abstract val property: KtVariableSymbol
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class CapturedValInitialization : KtFirDiagnostic<KtExpression>() {
|
||||||
|
override val diagnosticClass get() = CapturedValInitialization::class
|
||||||
|
abstract val property: KtVariableSymbol
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class CapturedMemberValInitialization : KtFirDiagnostic<KtExpression>() {
|
||||||
|
override val diagnosticClass get() = CapturedMemberValInitialization::class
|
||||||
|
abstract val property: KtVariableSymbol
|
||||||
|
}
|
||||||
|
|
||||||
abstract class WrongInvocationKind : KtFirDiagnostic<PsiElement>() {
|
abstract class WrongInvocationKind : KtFirDiagnostic<PsiElement>() {
|
||||||
override val diagnosticClass get() = WrongInvocationKind::class
|
override val diagnosticClass get() = WrongInvocationKind::class
|
||||||
abstract val declaration: KtSymbol
|
abstract val declaration: KtSymbol
|
||||||
|
|||||||
+16
@@ -2269,6 +2269,22 @@ internal class ValReassignmentViaBackingFieldErrorImpl(
|
|||||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class CapturedValInitializationImpl(
|
||||||
|
override val property: KtVariableSymbol,
|
||||||
|
firDiagnostic: FirPsiDiagnostic<*>,
|
||||||
|
override val token: ValidityToken,
|
||||||
|
) : KtFirDiagnostic.CapturedValInitialization(), KtAbstractFirDiagnostic<KtExpression> {
|
||||||
|
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class CapturedMemberValInitializationImpl(
|
||||||
|
override val property: KtVariableSymbol,
|
||||||
|
firDiagnostic: FirPsiDiagnostic<*>,
|
||||||
|
override val token: ValidityToken,
|
||||||
|
) : KtFirDiagnostic.CapturedMemberValInitialization(), KtAbstractFirDiagnostic<KtExpression> {
|
||||||
|
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||||
|
}
|
||||||
|
|
||||||
internal class WrongInvocationKindImpl(
|
internal class WrongInvocationKindImpl(
|
||||||
override val declaration: KtSymbol,
|
override val declaration: KtSymbol,
|
||||||
override val requiredRange: EventOccurrencesRange,
|
override val requiredRange: EventOccurrencesRange,
|
||||||
|
|||||||
Reference in New Issue
Block a user