FirReturnsImpliesAnalyzer: find receiver/variable of accessor properly
This commit is contained in:
+2
-2
@@ -11,8 +11,8 @@ fun Any?.isNotNull(): Boolean {
|
|||||||
@OptIn(ExperimentalContracts::class)
|
@OptIn(ExperimentalContracts::class)
|
||||||
val Any?.isNotNull: Boolean
|
val Any?.isNotNull: Boolean
|
||||||
get() {
|
get() {
|
||||||
<!WRONG_IMPLIES_CONDITION!>contract {
|
contract {
|
||||||
returns(true) implies (this@isNotNull != null)
|
returns(true) implies (this@isNotNull != null)
|
||||||
}<!>
|
}
|
||||||
return this@isNotNull != null
|
return this@isNotNull != null
|
||||||
}
|
}
|
||||||
+4
-4
@@ -6,16 +6,16 @@ interface A {
|
|||||||
|
|
||||||
var Any?.isNotNull: Boolean
|
var Any?.isNotNull: Boolean
|
||||||
get() {
|
get() {
|
||||||
<!WRONG_IMPLIES_CONDITION!>contract {
|
contract {
|
||||||
returns(true) implies (this@isNotNull != null)
|
returns(true) implies (this@isNotNull != null)
|
||||||
}<!>
|
}
|
||||||
return this != null
|
return this != null
|
||||||
}
|
}
|
||||||
set(value) {
|
set(value) {
|
||||||
<!WRONG_IMPLIES_CONDITION!>contract {
|
contract {
|
||||||
returns() implies (this@isNotNull != null)
|
returns() implies (this@isNotNull != null)
|
||||||
require(this != null)
|
require(this != null)
|
||||||
}<!>
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test_1(a: A?) {
|
fun test_1(a: A?) {
|
||||||
|
|||||||
+41
-16
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.contracts.coneEffects
|
|||||||
import org.jetbrains.kotlin.fir.contracts.description.*
|
import org.jetbrains.kotlin.fir.contracts.description.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirContractDescriptionOwner
|
import org.jetbrains.kotlin.fir.declarations.FirContractDescriptionOwner
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirFunction
|
import org.jetbrains.kotlin.fir.declarations.FirFunction
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
||||||
import org.jetbrains.kotlin.fir.expressions.*
|
import org.jetbrains.kotlin.fir.expressions.*
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.*
|
import org.jetbrains.kotlin.fir.resolve.dfa.*
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.BlockExitNode
|
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.BlockExitNode
|
||||||
@@ -23,6 +24,7 @@ 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.resolve.dfa.cfg.JumpNode
|
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.JumpNode
|
||||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertyAccessorSymbol
|
||||||
import org.jetbrains.kotlin.fir.typeContext
|
import org.jetbrains.kotlin.fir.typeContext
|
||||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||||
import org.jetbrains.kotlin.fir.types.coneType
|
import org.jetbrains.kotlin.fir.types.coneType
|
||||||
@@ -61,7 +63,7 @@ object FirReturnsImpliesAnalyzer : FirControlFlowChecker() {
|
|||||||
|
|
||||||
effects.forEach { effect ->
|
effects.forEach { effect ->
|
||||||
val wrongCondition = graph.exitNode.previousCfgNodes.any {
|
val wrongCondition = graph.exitNode.previousCfgNodes.any {
|
||||||
isWrongConditionOnNode(it, effect as ConeConditionalEffectDeclaration, function, logicSystem, dataFlowInfo)
|
isWrongConditionOnNode(it, effect as ConeConditionalEffectDeclaration, function, logicSystem, dataFlowInfo, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wrongCondition) {
|
if (wrongCondition) {
|
||||||
@@ -77,7 +79,8 @@ object FirReturnsImpliesAnalyzer : FirControlFlowChecker() {
|
|||||||
effectDeclaration: ConeConditionalEffectDeclaration,
|
effectDeclaration: ConeConditionalEffectDeclaration,
|
||||||
function: FirFunction<*>,
|
function: FirFunction<*>,
|
||||||
logicSystem: LogicSystem<PersistentFlow>,
|
logicSystem: LogicSystem<PersistentFlow>,
|
||||||
dataFlowInfo: DataFlowInfo
|
dataFlowInfo: DataFlowInfo,
|
||||||
|
context: CheckerContext
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val effect = effectDeclaration.effect as ConeReturnsEffectDeclaration
|
val effect = effectDeclaration.effect as ConeReturnsEffectDeclaration
|
||||||
val builtinTypes = function.session.builtinTypes
|
val builtinTypes = function.session.builtinTypes
|
||||||
@@ -92,7 +95,7 @@ object FirReturnsImpliesAnalyzer : FirControlFlowChecker() {
|
|||||||
|
|
||||||
if (isReturn && resultExpression is FirWhenExpression) {
|
if (isReturn && resultExpression is FirWhenExpression) {
|
||||||
return node.collectBranchExits().any {
|
return node.collectBranchExits().any {
|
||||||
isWrongConditionOnNode(it, effectDeclaration, function, logicSystem, dataFlowInfo)
|
isWrongConditionOnNode(it, effectDeclaration, function, logicSystem, dataFlowInfo, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,15 +113,16 @@ object FirReturnsImpliesAnalyzer : FirControlFlowChecker() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val conditionStatements =
|
val conditionStatements = effectDeclaration.condition.buildTypeStatements(
|
||||||
effectDeclaration.condition.buildTypeStatements(function, logicSystem, dataFlowInfo.variableStorage, flow) ?: return false
|
function, logicSystem, dataFlowInfo.variableStorage, flow, context
|
||||||
|
) ?: return false
|
||||||
|
|
||||||
for ((realVar, requiredTypeStatement) in conditionStatements) {
|
for ((realVar, requiredTypeStatement) in conditionStatements) {
|
||||||
val fixedRealVar = typeStatements.keys.find { it.identifier == realVar.identifier } ?: realVar
|
val fixedRealVar = typeStatements.keys.find { it.identifier == realVar.identifier } ?: realVar
|
||||||
val resultTypeStatement = typeStatements[fixedRealVar]
|
val resultTypeStatement = typeStatements[fixedRealVar]
|
||||||
|
|
||||||
val resultType = mutableListOf<ConeKotlinType>().apply {
|
val resultType = mutableListOf<ConeKotlinType>().apply {
|
||||||
addIfNotNull(function.getParameterType(fixedRealVar.identifier.symbol))
|
addIfNotNull(function.getParameterType(fixedRealVar.identifier.symbol, context))
|
||||||
if (resultTypeStatement != null) addAll(resultTypeStatement.exactType)
|
if (resultTypeStatement != null) addAll(resultTypeStatement.exactType)
|
||||||
}.let { typeContext.intersectTypesOrNull(it) }
|
}.let { typeContext.intersectTypesOrNull(it) }
|
||||||
|
|
||||||
@@ -154,11 +158,12 @@ object FirReturnsImpliesAnalyzer : FirControlFlowChecker() {
|
|||||||
function: FirFunction<*>,
|
function: FirFunction<*>,
|
||||||
logicSystem: LogicSystem<*>,
|
logicSystem: LogicSystem<*>,
|
||||||
variableStorage: VariableStorage,
|
variableStorage: VariableStorage,
|
||||||
flow: Flow
|
flow: Flow,
|
||||||
|
context: CheckerContext
|
||||||
): MutableTypeStatements? = when (this) {
|
): MutableTypeStatements? = when (this) {
|
||||||
is ConeBinaryLogicExpression -> {
|
is ConeBinaryLogicExpression -> {
|
||||||
val left = left.buildTypeStatements(function, logicSystem, variableStorage, flow)
|
val left = left.buildTypeStatements(function, logicSystem, variableStorage, flow, context)
|
||||||
val right = right.buildTypeStatements(function, logicSystem, variableStorage, flow)
|
val right = right.buildTypeStatements(function, logicSystem, variableStorage, flow, context)
|
||||||
if (left != null && right != null) {
|
if (left != null && right != null) {
|
||||||
if (kind == LogicOperationKind.AND) {
|
if (kind == LogicOperationKind.AND) {
|
||||||
left.apply { mergeTypeStatements(right) }
|
left.apply { mergeTypeStatements(right) }
|
||||||
@@ -166,16 +171,16 @@ object FirReturnsImpliesAnalyzer : FirControlFlowChecker() {
|
|||||||
} else (left ?: right)
|
} else (left ?: right)
|
||||||
}
|
}
|
||||||
is ConeIsInstancePredicate -> {
|
is ConeIsInstancePredicate -> {
|
||||||
val fir = function.getParameterSymbol(arg.parameterIndex).fir
|
val fir = function.getParameterSymbol(arg.parameterIndex, context).fir
|
||||||
val realVar = variableStorage.getOrCreateRealVariable(flow, fir.symbol, fir)
|
val realVar = variableStorage.getOrCreateRealVariable(flow, fir.symbol, fir)
|
||||||
realVar?.to(simpleTypeStatement(realVar, !isNegated, type))?.let { mutableMapOf(it) }
|
realVar?.to(simpleTypeStatement(realVar, !isNegated, type))?.let { mutableMapOf(it) }
|
||||||
}
|
}
|
||||||
is ConeIsNullPredicate -> {
|
is ConeIsNullPredicate -> {
|
||||||
val fir = function.getParameterSymbol(arg.parameterIndex).fir
|
val fir = function.getParameterSymbol(arg.parameterIndex, context).fir
|
||||||
val realVar = variableStorage.getOrCreateRealVariable(flow, fir.symbol, fir)
|
val realVar = variableStorage.getOrCreateRealVariable(flow, fir.symbol, fir)
|
||||||
realVar?.to(simpleTypeStatement(realVar, isNegated, function.session.builtinTypes.anyType.type))?.let { mutableMapOf(it) }
|
realVar?.to(simpleTypeStatement(realVar, isNegated, function.session.builtinTypes.anyType.type))?.let { mutableMapOf(it) }
|
||||||
}
|
}
|
||||||
is ConeLogicalNot -> arg.buildTypeStatements(function, logicSystem, variableStorage, flow)
|
is ConeLogicalNot -> arg.buildTypeStatements(function, logicSystem, variableStorage, flow, context)
|
||||||
?.mapValuesTo(mutableMapOf()) { (_, value) -> value.invert() }
|
?.mapValuesTo(mutableMapOf()) { (_, value) -> value.invert() }
|
||||||
|
|
||||||
else -> null
|
else -> null
|
||||||
@@ -212,11 +217,31 @@ object FirReturnsImpliesAnalyzer : FirControlFlowChecker() {
|
|||||||
return nodes
|
return nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun FirFunction<*>.getParameterType(symbol: AbstractFirBasedSymbol<*>): ConeKotlinType? {
|
private val CheckerContext.containingProperty: FirProperty?
|
||||||
return (if (this.symbol == symbol) receiverTypeRef else valueParameters.find { it.symbol == symbol }?.returnTypeRef)?.coneType
|
get() = (containingDeclarations.asReversed().firstOrNull { it is FirProperty } as? FirProperty)
|
||||||
|
|
||||||
|
private fun FirFunction<*>.getParameterType(symbol: AbstractFirBasedSymbol<*>, context: CheckerContext): ConeKotlinType? {
|
||||||
|
val typeRef = if (this.symbol == symbol) {
|
||||||
|
if (symbol is FirPropertyAccessorSymbol) {
|
||||||
|
context.containingProperty?.receiverTypeRef
|
||||||
|
} else {
|
||||||
|
receiverTypeRef
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
valueParameters.find { it.symbol == symbol }?.returnTypeRef
|
||||||
|
}
|
||||||
|
return typeRef?.coneType
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun FirFunction<*>.getParameterSymbol(index: Int): AbstractFirBasedSymbol<*> {
|
private fun FirFunction<*>.getParameterSymbol(index: Int, context: CheckerContext): AbstractFirBasedSymbol<*> {
|
||||||
return if (index == -1) this.symbol else this.valueParameters[index].symbol
|
return if (index == -1) {
|
||||||
|
if (symbol !is FirPropertyAccessorSymbol) {
|
||||||
|
symbol
|
||||||
|
} else {
|
||||||
|
context.containingProperty?.symbol ?: symbol
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.valueParameters[index].symbol
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user