Revert "FIR DFA: store stability in RealVariable"
This reverts commit 62f7e8f7
This commit is contained in:
+20
-24
@@ -156,34 +156,30 @@ object FirReturnsImpliesAnalyzer : FirControlFlowChecker() {
|
||||
variableStorage: VariableStorage,
|
||||
flow: Flow,
|
||||
context: CheckerContext
|
||||
): MutableTypeStatements? {
|
||||
fun buildTypeStatements(arg: ConeValueParameterReference, exactType: Boolean, type: ConeKotlinType): MutableTypeStatements? {
|
||||
): MutableTypeStatements? = when (this) {
|
||||
is ConeBinaryLogicExpression -> {
|
||||
val left = left.buildTypeStatements(function, logicSystem, variableStorage, flow, context)
|
||||
val right = right.buildTypeStatements(function, logicSystem, variableStorage, flow, context)
|
||||
if (left != null && right != null) {
|
||||
if (kind == LogicOperationKind.AND) {
|
||||
left.apply { mergeTypeStatements(right) }
|
||||
} else logicSystem.orForTypeStatements(left, right)
|
||||
} else (left ?: right)
|
||||
}
|
||||
is ConeIsInstancePredicate -> {
|
||||
val fir = function.getParameterSymbol(arg.parameterIndex, context).fir
|
||||
val realVar = variableStorage.getOrCreateRealVariable(flow, fir.symbol, fir)
|
||||
?.takeIf {
|
||||
it.stability == PropertyStability.STABLE_VALUE ||
|
||||
// TODO: consider removing the part below
|
||||
it.stability == PropertyStability.LOCAL_VAR
|
||||
}
|
||||
return realVar?.to(simpleTypeStatement(realVar, exactType, type))?.let { mutableMapOf(it) }
|
||||
realVar?.to(simpleTypeStatement(realVar, !isNegated, type))?.let { mutableMapOf(it) }
|
||||
}
|
||||
return when (this) {
|
||||
is ConeBinaryLogicExpression -> {
|
||||
val left = left.buildTypeStatements(function, logicSystem, variableStorage, flow, context)
|
||||
val right = right.buildTypeStatements(function, logicSystem, variableStorage, flow, context)
|
||||
if (left != null && right != null) {
|
||||
if (kind == LogicOperationKind.AND) {
|
||||
left.apply { mergeTypeStatements(right) }
|
||||
} else logicSystem.orForTypeStatements(left, right)
|
||||
} else (left ?: right)
|
||||
}
|
||||
is ConeIsInstancePredicate -> buildTypeStatements(arg, !isNegated, type)
|
||||
is ConeIsNullPredicate -> buildTypeStatements(arg, isNegated, context.session.builtinTypes.anyType.type)
|
||||
is ConeLogicalNot -> arg.buildTypeStatements(function, logicSystem, variableStorage, flow, context)
|
||||
?.mapValuesTo(mutableMapOf()) { (_, value) -> value.invert() }
|
||||
is ConeIsNullPredicate -> {
|
||||
val fir = function.getParameterSymbol(arg.parameterIndex, context).fir
|
||||
val realVar = variableStorage.getOrCreateRealVariable(flow, fir.symbol, fir)
|
||||
realVar?.to(simpleTypeStatement(realVar, isNegated, context.session.builtinTypes.anyType.type))?.let { mutableMapOf(it) }
|
||||
}
|
||||
is ConeLogicalNot -> arg.buildTypeStatements(function, logicSystem, variableStorage, flow, context)
|
||||
?.mapValuesTo(mutableMapOf()) { (_, value) -> value.invert() }
|
||||
|
||||
else -> null
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun ConeKotlinType.isInapplicableWith(operation: Operation, session: FirSession): Boolean {
|
||||
|
||||
@@ -10,10 +10,7 @@ import org.jetbrains.kotlin.builtins.functions.FunctionClassKind
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
|
||||
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
|
||||
import org.jetbrains.kotlin.fir.diagnostics.ConeStubDiagnostic
|
||||
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
|
||||
import org.jetbrains.kotlin.fir.diagnostics.*
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.expressions.builder.*
|
||||
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
|
||||
@@ -26,12 +23,12 @@ import org.jetbrains.kotlin.fir.resolve.calls.ImplicitDispatchReceiverValue
|
||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError
|
||||
import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents
|
||||
import org.jetbrains.kotlin.fir.resolve.inference.isBuiltinFunctionalType
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.getSymbolByTypeRef
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.*
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.ensureResolved
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.delegatedWrapperData
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.importedFromObjectData
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.*
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
|
||||
@@ -250,8 +247,7 @@ private fun BodyResolveComponents.typeFromSymbol(symbol: AbstractFirBasedSymbol<
|
||||
fun BodyResolveComponents.transformQualifiedAccessUsingSmartcastInfo(
|
||||
qualifiedAccessExpression: FirQualifiedAccessExpression
|
||||
): FirQualifiedAccessExpression {
|
||||
val (stability, typesFromSmartCast) = dataFlowAnalyzer.getTypeUsingSmartcastInfo(qualifiedAccessExpression)
|
||||
?: return qualifiedAccessExpression
|
||||
val typesFromSmartCast = dataFlowAnalyzer.getTypeUsingSmartcastInfo(qualifiedAccessExpression) ?: return qualifiedAccessExpression
|
||||
val originalType = qualifiedAccessExpression.resultType.coneType
|
||||
// For example, if (x == null) { ... },
|
||||
// we don't want to smartcast to Nothing?, but we want to record the nullability to its own kind of node.
|
||||
@@ -272,11 +268,9 @@ fun BodyResolveComponents.transformQualifiedAccessUsingSmartcastInfo(
|
||||
return buildExpressionWithSmartcastToNull {
|
||||
originalExpression = qualifiedAccessExpression
|
||||
// TODO: Use Nothing? during resolution?
|
||||
smartcastType = intersectedTypeRefWithoutNullableNothing
|
||||
typeRef = intersectedTypeRefWithoutNullableNothing
|
||||
// NB: Nothing? in types from smartcast in DFA is recorded here (and the expression kind itself).
|
||||
this.typesFromSmartCast = typesFromSmartCast
|
||||
// TODO: differentiate capture local variable
|
||||
this.smartcastStability = stability.impliedSmartcastStability ?: SmartcastStability.STABLE_VALUE
|
||||
}
|
||||
}
|
||||
val allTypes = typesFromSmartCast.also {
|
||||
@@ -294,8 +288,7 @@ fun BodyResolveComponents.transformQualifiedAccessUsingSmartcastInfo(
|
||||
originalExpression = qualifiedAccessExpression
|
||||
smartcastType = intersectedTypeRef
|
||||
this.typesFromSmartCast = typesFromSmartCast
|
||||
// TODO: differentiate capture local variable
|
||||
this.smartcastStability = stability.impliedSmartcastStability ?: SmartcastStability.STABLE_VALUE
|
||||
smartcastStability = SmartcastStability.STABLE_VALUE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-24
@@ -162,7 +162,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
|
||||
// ----------------------------------- Requests -----------------------------------
|
||||
|
||||
fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): Pair<PropertyStability, MutableList<ConeKotlinType>>? {
|
||||
fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): MutableList<ConeKotlinType>? {
|
||||
/*
|
||||
* DataFlowAnalyzer holds variables only for declarations that have some smartcast (or can have)
|
||||
* If there is no useful information there is no data flow variable also
|
||||
@@ -170,14 +170,13 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
val symbol: AbstractFirBasedSymbol<*> = qualifiedAccessExpression.symbol ?: return null
|
||||
val flow = graphBuilder.lastNode.flow
|
||||
var variable = variableStorage.getRealVariableWithoutUnwrappingAlias(symbol, qualifiedAccessExpression, flow) ?: return null
|
||||
val stability = variable.stability
|
||||
val result = mutableListOf<ConeKotlinType>()
|
||||
flow.directAliasMap[variable]?.let {
|
||||
result.addIfNotNull(it.originalType)
|
||||
variable = it.variable
|
||||
}
|
||||
flow.getTypeStatement(variable)?.exactType?.let { result += it }
|
||||
return result.takeIf { it.isNotEmpty() }?.let { stability to it }
|
||||
return result.takeIf { it.isNotEmpty() }
|
||||
}
|
||||
|
||||
fun returnExpressionsOfAnonymousFunction(function: FirAnonymousFunction): Collection<FirStatement> {
|
||||
@@ -1010,12 +1009,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
assignment: FirVariableAssignment?
|
||||
) {
|
||||
val flow = node.flow
|
||||
val propertyVariable = variableStorage.getOrCreateRealVariableWithoutUnwrappingAlias(
|
||||
flow,
|
||||
property.symbol,
|
||||
assignment ?: property,
|
||||
if (property.isVal) PropertyStability.STABLE_VALUE else PropertyStability.LOCAL_VAR
|
||||
)
|
||||
val propertyVariable = variableStorage.getOrCreateRealVariableWithoutUnwrappingAlias(flow, property.symbol, assignment ?: property)
|
||||
val isAssignment = assignment != null
|
||||
if (isAssignment) {
|
||||
logicSystem.removeLocalVariableAlias(flow, propertyVariable)
|
||||
@@ -1023,21 +1017,13 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
logicSystem.recordNewAssignment(flow, propertyVariable, context.newAssignmentIndex())
|
||||
}
|
||||
|
||||
variableStorage.getOrCreateRealVariable(flow, initializer.symbol, initializer)
|
||||
?.let { initializerVariable ->
|
||||
// TODO: handle capture variable
|
||||
if ((initializerVariable.stability == PropertyStability.STABLE_VALUE || initializerVariable.stability == PropertyStability.LOCAL_VAR) &&
|
||||
(propertyVariable.stability == PropertyStability.STABLE_VALUE || propertyVariable.stability == PropertyStability.LOCAL_VAR)
|
||||
) {
|
||||
logicSystem.addLocalVariableAlias(
|
||||
flow, propertyVariable,
|
||||
RealVariableAndType(initializerVariable, initializer.coneType)
|
||||
)
|
||||
// node.flow.addImplication((propertyVariable notEq null) implies (initializerVariable notEq null))
|
||||
} else {
|
||||
logicSystem.replaceVariableFromConditionInStatements(flow, initializerVariable, propertyVariable)
|
||||
}
|
||||
}
|
||||
variableStorage.getOrCreateRealVariable(flow, initializer.symbol, initializer)?.let { initializerVariable ->
|
||||
logicSystem.addLocalVariableAlias(
|
||||
flow, propertyVariable,
|
||||
RealVariableAndType(initializerVariable, initializer.coneType)
|
||||
)
|
||||
// node.flow.addImplication((propertyVariable notEq null) implies (initializerVariable notEq null))
|
||||
}
|
||||
|
||||
variableStorage.getSyntheticVariable(initializer)?.let { initializerVariable ->
|
||||
/*
|
||||
|
||||
@@ -8,9 +8,7 @@ package org.jetbrains.kotlin.fir.resolve.dfa
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.FirAnonymousObject
|
||||
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor
|
||||
import org.jetbrains.kotlin.fir.declarations.modality
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
@@ -36,24 +34,14 @@ class VariableStorage(private val session: FirSession) {
|
||||
|
||||
fun clear(): VariableStorage = VariableStorage(session)
|
||||
|
||||
fun getOrCreateRealVariableWithoutUnwrappingAlias(
|
||||
flow: Flow,
|
||||
symbol: AbstractFirBasedSymbol<*>,
|
||||
fir: FirElement,
|
||||
stability: PropertyStability
|
||||
): RealVariable {
|
||||
fun getOrCreateRealVariableWithoutUnwrappingAlias(flow: Flow, symbol: AbstractFirBasedSymbol<*>, fir: FirElement): RealVariable {
|
||||
val realFir = fir.unwrapElement()
|
||||
val identifier = getIdentifierBySymbol(flow, symbol, realFir)
|
||||
return _realVariables.getOrPut(identifier) { createRealVariableInternal(flow, identifier, realFir, stability) }
|
||||
return _realVariables.getOrPut(identifier) { createRealVariableInternal(flow, identifier, realFir) }
|
||||
}
|
||||
|
||||
private fun getOrCreateRealVariable(
|
||||
flow: Flow,
|
||||
symbol: AbstractFirBasedSymbol<*>,
|
||||
fir: FirElement,
|
||||
stability: PropertyStability
|
||||
): RealVariable {
|
||||
val variable = getOrCreateRealVariableWithoutUnwrappingAlias(flow, symbol, fir, stability)
|
||||
private fun getOrCreateRealVariable(flow: Flow, symbol: AbstractFirBasedSymbol<*>, fir: FirElement): RealVariable {
|
||||
val variable = getOrCreateRealVariableWithoutUnwrappingAlias(flow, symbol, fir)
|
||||
return flow.directAliasMap[variable]?.variable ?: variable
|
||||
}
|
||||
|
||||
@@ -81,12 +69,7 @@ class VariableStorage(private val session: FirSession) {
|
||||
/**
|
||||
* [originalFir] used for extracting expression under <when_subject> and extracting receiver
|
||||
*/
|
||||
private fun createRealVariableInternal(
|
||||
flow: Flow,
|
||||
identifier: Identifier,
|
||||
originalFir: FirElement,
|
||||
stability: PropertyStability
|
||||
): RealVariable {
|
||||
private fun createRealVariableInternal(flow: Flow, identifier: Identifier, originalFir: FirElement): RealVariable {
|
||||
val receiver: FirExpression?
|
||||
val isThisReference: Boolean
|
||||
val expression: FirQualifiedAccess? = when (originalFir) {
|
||||
@@ -105,12 +88,12 @@ class VariableStorage(private val session: FirSession) {
|
||||
}
|
||||
|
||||
val receiverVariable = receiver?.let { getOrCreateVariable(flow, it) }
|
||||
return RealVariable(identifier, isThisReference, receiverVariable, counter++, stability)
|
||||
return RealVariable(identifier, isThisReference, receiverVariable, counter++)
|
||||
}
|
||||
|
||||
@JvmName("getOrCreateRealVariableOrNull")
|
||||
fun getOrCreateRealVariable(flow: Flow, symbol: AbstractFirBasedSymbol<*>?, fir: FirElement): RealVariable? =
|
||||
symbol.getStability(fir)?.let { getOrCreateRealVariable(flow, symbol!!, fir, it) }
|
||||
symbol.takeIf { it.isStable(fir) }?.let { getOrCreateRealVariable(flow, it, fir) }
|
||||
|
||||
fun createSyntheticVariable(fir: FirElement): SyntheticVariable =
|
||||
SyntheticVariable(fir, counter++).also { syntheticVariables[fir] = it }
|
||||
@@ -118,9 +101,8 @@ class VariableStorage(private val session: FirSession) {
|
||||
fun getOrCreateVariable(flow: Flow, fir: FirElement): DataFlowVariable {
|
||||
val realFir = fir.unwrapElement()
|
||||
val symbol = realFir.symbol
|
||||
val stability = symbol.getStability(realFir)
|
||||
return if (stability != null) {
|
||||
getOrCreateRealVariable(flow, symbol!!, realFir, stability)
|
||||
return if (symbol.isStable(realFir)) {
|
||||
getOrCreateRealVariable(flow, symbol!!, realFir)
|
||||
} else {
|
||||
syntheticVariables[realFir] ?: createSyntheticVariable(realFir)
|
||||
}
|
||||
@@ -128,7 +110,7 @@ class VariableStorage(private val session: FirSession) {
|
||||
|
||||
fun getRealVariableWithoutUnwrappingAlias(symbol: AbstractFirBasedSymbol<*>?, fir: FirElement, flow: Flow): RealVariable? {
|
||||
val realFir = fir.unwrapElement()
|
||||
return symbol.takeIf { it.getStability(realFir) != null }?.let {
|
||||
return symbol.takeIf { it.isStable(realFir) }?.let {
|
||||
_realVariables[getIdentifierBySymbol(flow, it, realFir.unwrapElement())]
|
||||
}
|
||||
}
|
||||
@@ -144,8 +126,7 @@ class VariableStorage(private val session: FirSession) {
|
||||
fun getVariable(fir: FirElement, flow: Flow): DataFlowVariable? {
|
||||
val realFir = fir.unwrapElement()
|
||||
val symbol = realFir.symbol
|
||||
val stability = symbol.getStability(fir)
|
||||
return if (stability != null) {
|
||||
return if (symbol.isStable(fir)) {
|
||||
getRealVariable(symbol, realFir, flow)
|
||||
} else {
|
||||
getSyntheticVariable(fir)
|
||||
@@ -162,40 +143,39 @@ class VariableStorage(private val session: FirSession) {
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
fun AbstractFirBasedSymbol<*>?.getStability(originalFir: FirElement): PropertyStability? {
|
||||
fun AbstractFirBasedSymbol<*>?.isStable(originalFir: FirElement): Boolean {
|
||||
contract {
|
||||
returnsNotNull() implies (this@getStability != null)
|
||||
returns(true) implies(this@isStable != null)
|
||||
}
|
||||
when (this) {
|
||||
is FirAnonymousObjectSymbol -> return null
|
||||
is FirAnonymousObjectSymbol -> return false
|
||||
is FirFunctionSymbol<*>,
|
||||
is FirClassSymbol<*>,
|
||||
is FirBackingFieldSymbol -> return PropertyStability.STABLE_VALUE
|
||||
null -> return null
|
||||
is FirBackingFieldSymbol -> return true
|
||||
null -> return false
|
||||
}
|
||||
if (originalFir is FirThisReceiverExpression) return PropertyStability.STABLE_VALUE
|
||||
if (this !is FirVariableSymbol<*>) return null
|
||||
if (originalFir is FirThisReceiverExpression) return true
|
||||
if (this !is FirVariableSymbol<*>) return false
|
||||
|
||||
val property = this.fir as? FirProperty ?: return PropertyStability.STABLE_VALUE
|
||||
val property = this.fir as? FirProperty ?: return true
|
||||
|
||||
return when {
|
||||
property.delegate != null -> PropertyStability.DELEGATED_PROPERTY
|
||||
property.isLocal -> if (property.isVal) PropertyStability.STABLE_VALUE else PropertyStability.LOCAL_VAR
|
||||
property.isVar -> PropertyStability.MUTABLE_PROPERTY
|
||||
property.receiverTypeRef != null -> PropertyStability.PROPERTY_WITH_GETTER
|
||||
property.getter.let { it != null && it !is FirDefaultPropertyAccessor } -> PropertyStability.PROPERTY_WITH_GETTER
|
||||
property.moduleData.session != session -> PropertyStability.ALIEN_PUBLIC_PROPERTY
|
||||
property.isLocal -> true
|
||||
property.isVar -> false
|
||||
property.receiverTypeRef != null -> false
|
||||
property.getter.let { it != null && it !is FirDefaultPropertyAccessor } -> false
|
||||
property.modality != Modality.FINAL -> {
|
||||
val dispatchReceiver = (originalFir.unwrapElement() as? FirQualifiedAccess)?.dispatchReceiver ?: return null
|
||||
val receiverType = dispatchReceiver.typeRef.coneTypeSafe<ConeClassLikeType>()?.fullyExpandedType(session) ?: return null
|
||||
val receiverSymbol = receiverType.lookupTag.toSymbol(session) ?: return null
|
||||
val dispatchReceiver = (originalFir.unwrapElement() as? FirQualifiedAccess)?.dispatchReceiver ?: return false
|
||||
val receiverType = dispatchReceiver.typeRef.coneTypeSafe<ConeClassLikeType>()?.fullyExpandedType(session) ?: return false
|
||||
val receiverSymbol = receiverType.lookupTag.toSymbol(session) ?: return false
|
||||
when (val receiverFir = receiverSymbol.fir) {
|
||||
is FirAnonymousObject -> PropertyStability.STABLE_VALUE
|
||||
is FirRegularClass -> if (receiverFir.modality == Modality.FINAL) PropertyStability.STABLE_VALUE else PropertyStability.PROPERTY_WITH_GETTER
|
||||
is org.jetbrains.kotlin.fir.declarations.FirAnonymousObject -> true
|
||||
is org.jetbrains.kotlin.fir.declarations.FirRegularClass -> receiverFir.modality == Modality.FINAL
|
||||
else -> throw IllegalStateException("Should not be here: $receiverFir")
|
||||
}
|
||||
|
||||
}
|
||||
else -> PropertyStability.STABLE_VALUE
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassErrorType
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.types.SmartcastStability
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
@@ -34,37 +33,11 @@ sealed class DataFlowVariable(private val variableIndexForDebug: Int) {
|
||||
}
|
||||
}
|
||||
|
||||
enum class PropertyStability(val impliedSmartcastStability: SmartcastStability?) {
|
||||
// Immutable and no custom getter or local.
|
||||
// Smartcast is definitely safe regardless of usage.
|
||||
STABLE_VALUE(SmartcastStability.STABLE_VALUE),
|
||||
|
||||
// Open or custom getter.
|
||||
// Smartcast is always unsafe regardless of usage.
|
||||
PROPERTY_WITH_GETTER(SmartcastStability.PROPERTY_WITH_GETTER),
|
||||
|
||||
// Protected / public member value from another module.
|
||||
// Smartcast is always unsafe regardless of usage.
|
||||
ALIEN_PUBLIC_PROPERTY(SmartcastStability.ALIEN_PUBLIC_PROPERTY),
|
||||
|
||||
// Smartcast may or may not be safe, depending on whether there are concurrent writes to this local variable.
|
||||
LOCAL_VAR(null),
|
||||
|
||||
// Mutable member property of a class or object.
|
||||
// Smartcast is always unsafe regardless of usage.
|
||||
MUTABLE_PROPERTY(SmartcastStability.MUTABLE_PROPERTY),
|
||||
|
||||
// Delegated property of a class or object.
|
||||
// Smartcast is always unsafe regardless of usage.
|
||||
DELEGATED_PROPERTY(SmartcastStability.DELEGATED_PROPERTY),
|
||||
}
|
||||
|
||||
class RealVariable(
|
||||
val identifier: Identifier,
|
||||
val isThisReference: Boolean,
|
||||
val explicitReceiverVariable: DataFlowVariable?,
|
||||
variableIndexForDebug: Int,
|
||||
val stability: PropertyStability,
|
||||
variableIndexForDebug: Int
|
||||
) : DataFlowVariable(variableIndexForDebug) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return this === other
|
||||
|
||||
-2
@@ -467,8 +467,6 @@ object NodeConfigurator : AbstractFieldConfigurator<FirTreeBuilder>(FirTreeBuild
|
||||
+field("originalExpression", qualifiedAccessExpression)
|
||||
+field("typesFromSmartCast", "Collection<ConeKotlinType>", null, customType = coneKotlinTypeType)
|
||||
+field("originalType", typeRef)
|
||||
+field("smartcastType", typeRef)
|
||||
+smartcastStability
|
||||
}
|
||||
|
||||
safeCallExpression.configure {
|
||||
|
||||
+2
-2
@@ -14,13 +14,13 @@ fun test(u: A?, x: A?, y: A?, z: A?, w: A, v: A?) {
|
||||
u!!.b<!UNNECESSARY_SAFE_CALL!>?.<!>foo()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
|
||||
x?.b!!.foo()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
|
||||
// x?.b is not null
|
||||
x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.b<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.foo()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
|
||||
x!!.b<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.foo()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
|
||||
|
||||
y?.nb?.foo()!!
|
||||
y!!.nb?.foo()!!
|
||||
z?.nb!!.foo()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
|
||||
// z?.nb is not null
|
||||
z<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.nb!!.foo()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
|
||||
z!!.nb!!.foo()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
|
||||
|
||||
w.b<!UNNECESSARY_SAFE_CALL!>?.<!>foo()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
|
||||
w.b<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.foo()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ import a.X
|
||||
public fun X.gav(): Int {
|
||||
if (x != null)
|
||||
// Smart cast is not possible if definition is in another module
|
||||
return x<!UNSAFE_CALL!>.<!>length
|
||||
return x.length
|
||||
else
|
||||
return 0
|
||||
}
|
||||
@@ -29,7 +29,7 @@ package a
|
||||
public fun X.gav(): Int {
|
||||
if (x != null)
|
||||
// Even if it's in the same package
|
||||
return x<!UNSAFE_CALL!>.<!>length
|
||||
return x.length
|
||||
else
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ fun case_3(x: Int) = ""
|
||||
fun case_3(x: Int?) = 10
|
||||
fun case_3() {
|
||||
if (case_3_prop != null) {
|
||||
val z = case_3(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int?")!>case_3_prop<!>)
|
||||
val z = case_3(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>case_3_prop<!>)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int")!>z<!>
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,7 @@ fun case_7(x: Int) = ""
|
||||
fun case_7(x: Int?) = 10
|
||||
fun case_7() {
|
||||
if (case_7_prop != null) {
|
||||
val z = case_7(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int?")!>case_7_prop<!>)
|
||||
val z = case_7(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>case_7_prop<!>)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int")!>z<!>
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user