[FIR] Implement new bound smartcast algorithm
#KT-36055 Fixed
This commit is contained in:
@@ -448,7 +448,7 @@ private fun BodyResolveComponents.typeFromSymbol(symbol: AbstractFirBasedSymbol<
|
||||
|
||||
fun BodyResolveComponents.transformQualifiedAccessUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): FirQualifiedAccessExpression {
|
||||
val typesFromSmartCast = dataFlowAnalyzer.getTypeUsingSmartcastInfo(qualifiedAccessExpression) ?: return qualifiedAccessExpression
|
||||
val allTypes = typesFromSmartCast.toMutableList().also {
|
||||
val allTypes = typesFromSmartCast.also {
|
||||
it += qualifiedAccessExpression.resultType.coneTypeUnsafe<ConeKotlinType>()
|
||||
}
|
||||
val intersectedType = ConeTypeIntersector.intersectTypes(inferenceComponents.ctx as ConeInferenceContext, allTypes)
|
||||
|
||||
+79
-57
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.resolve.dfa
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.contracts.description.ConeBooleanConstantReference
|
||||
import org.jetbrains.kotlin.fir.contracts.description.ConeConditionalEffectDeclaration
|
||||
import org.jetbrains.kotlin.fir.contracts.description.ConeConstantReference
|
||||
@@ -14,8 +15,8 @@ import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.resolve.ImplicitReceiverStackImpl
|
||||
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.ClassDispatchReceiverValue
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext
|
||||
import org.jetbrains.kotlin.fir.resolve.defaultType
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.contracts.buildContractFir
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.contracts.createArgumentsMapping
|
||||
@@ -29,7 +30,6 @@ import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.visitors.transformSingle
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
@UseExperimental(DfaInternals::class)
|
||||
abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
@@ -61,7 +61,11 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
}
|
||||
|
||||
override fun updateAllReceivers(flow: PersistentFlow) {
|
||||
receiverStack.mapNotNull { variableStorage[it.boundSymbol, it.receiverExpression] }.forEach { processUpdatedReceiverVariable(flow, it) }
|
||||
receiverStack.forEach {
|
||||
variableStorage.getRealVariable(it.boundSymbol, it.receiverExpression, flow)?.let { variable ->
|
||||
processUpdatedReceiverVariable(flow, variable)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,7 +75,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
private val context: ConeInferenceContext = components.inferenceComponents.ctx
|
||||
|
||||
private val graphBuilder = ControlFlowGraphBuilder()
|
||||
protected val variableStorage = VariableStorage()
|
||||
protected val variableStorage: VariableStorage = VariableStorage(components.session)
|
||||
private val flowOnNodes = mutableMapOf<CFGNode<*>, FLOW>()
|
||||
|
||||
private val variablesForWhenConditions = mutableMapOf<WhenBranchConditionExitNode, DataFlowVariable>()
|
||||
@@ -83,14 +87,21 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
|
||||
// ----------------------------------- Requests -----------------------------------
|
||||
|
||||
fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): Collection<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
|
||||
*/
|
||||
val symbol: AbstractFirBasedSymbol<*> = qualifiedAccessExpression.symbol ?: return null
|
||||
val variable = variableStorage[symbol, qualifiedAccessExpression] ?: return null
|
||||
return graphBuilder.lastNode.flow.getTypeStatement(variable)?.exactType
|
||||
val flow = graphBuilder.lastNode.flow
|
||||
var variable = variableStorage.getRealVariableWithoutUnwrappingAlias(symbol, qualifiedAccessExpression, flow) ?: return null
|
||||
val result = mutableListOf<ConeKotlinType>()
|
||||
flow.directAliasMap[variable]?.let {
|
||||
result += it.originalType
|
||||
variable = it
|
||||
}
|
||||
flow.getTypeStatement(variable)?.exactType?.let { result += it }
|
||||
return result.takeIf { it.isNotEmpty() }
|
||||
}
|
||||
|
||||
fun returnExpressionsOfAnonymousFunction(function: FirAnonymousFunction): List<FirStatement> {
|
||||
@@ -164,7 +175,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
val node = graphBuilder.exitTypeOperatorCall(typeOperatorCall).mergeIncomingFlow()
|
||||
if (typeOperatorCall.operation !in FirOperation.TYPES) return
|
||||
val type = typeOperatorCall.conversionTypeRef.coneTypeUnsafe<ConeKotlinType>()
|
||||
val operandVariable = variableStorage.getOrCreateVariable(typeOperatorCall.argument)
|
||||
val operandVariable = variableStorage.getOrCreateVariable(node.previousFlow, typeOperatorCall.argument)
|
||||
val flow = node.flow
|
||||
|
||||
when (val operation = typeOperatorCall.operation) {
|
||||
@@ -251,7 +262,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
val isEq = operation.isEq()
|
||||
val expressionVariable = variableStorage.createSyntheticVariable(node.fir)
|
||||
val flow = node.flow
|
||||
val operandVariable = variableStorage.getOrCreateVariable(operand)
|
||||
val operandVariable = variableStorage.getOrCreateVariable(node.previousFlow, operand)
|
||||
// expression == const -> expression != null
|
||||
flow.addImplication((expressionVariable eq isEq) implies (operandVariable notEq null))
|
||||
if (operandVariable is RealVariable) {
|
||||
@@ -296,7 +307,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
private fun processEqNull(node: OperatorCallNode, operand: FirExpression, operation: FirOperation) {
|
||||
val flow = node.flow
|
||||
val expressionVariable = variableStorage.createSyntheticVariable(node.fir)
|
||||
val operandVariable = variableStorage.getOrCreateVariable(operand)
|
||||
val operandVariable = variableStorage.getOrCreateVariable(node.previousFlow, operand)
|
||||
|
||||
val isEq = operation.isEq()
|
||||
|
||||
@@ -336,7 +347,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
// Add `Any` to the set of possible types; the intersection type `T? & Any` will be reduced to `T` after smartcast.
|
||||
val node = graphBuilder.exitCheckNotNullCall(checkNotNullCall).mergeIncomingFlow()
|
||||
val argument = checkNotNullCall.argument
|
||||
val operandVariable = variableStorage.getOrCreateRealVariable(argument.symbol, argument) ?: return
|
||||
val operandVariable = variableStorage.getOrCreateRealVariable(node.previousFlow, argument.symbol, argument) ?: return
|
||||
node.flow.addTypeStatement(operandVariable typeEq any)
|
||||
logicSystem.approveStatementsInsideFlow(
|
||||
node.flow,
|
||||
@@ -370,10 +381,11 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
val (conditionExitNode, branchEnterNode) = graphBuilder.exitWhenBranchCondition(whenBranch)
|
||||
conditionExitNode.mergeIncomingFlow()
|
||||
|
||||
val conditionVariable = variableStorage.getOrCreateVariable(whenBranch.condition)
|
||||
val conditionExitFlow = conditionExitNode.flow
|
||||
val conditionVariable = variableStorage.getOrCreateVariable(conditionExitFlow, whenBranch.condition)
|
||||
variablesForWhenConditions[conditionExitNode] = conditionVariable
|
||||
branchEnterNode.flow = logicSystem.approveStatementsInsideFlow(
|
||||
conditionExitNode.flow,
|
||||
conditionExitFlow,
|
||||
conditionVariable eq true,
|
||||
shouldForkFlow = true,
|
||||
shouldRemoveSynthetics = false
|
||||
@@ -387,18 +399,19 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
fun exitWhenExpression(whenExpression: FirWhenExpression) {
|
||||
val (whenExitNode, syntheticElseNode) = graphBuilder.exitWhenExpression(whenExpression)
|
||||
if (syntheticElseNode != null) {
|
||||
syntheticElseNode.mergeIncomingFlow()
|
||||
val previousConditionExitNode = syntheticElseNode.firstPreviousNode as? WhenBranchConditionExitNode
|
||||
// previous node for syntheticElseNode can be not WhenBranchConditionExitNode in case of `when` without any branches
|
||||
// in that case there will be when enter or subject access node
|
||||
if (previousConditionExitNode != null) {
|
||||
val conditionVariable = variablesForWhenConditions.remove(previousConditionExitNode)!!
|
||||
syntheticElseNode.flow = logicSystem.approveStatementsInsideFlow(
|
||||
syntheticElseNode.flow,
|
||||
previousConditionExitNode.flow,
|
||||
conditionVariable eq false,
|
||||
shouldForkFlow = true,
|
||||
shouldRemoveSynthetics = true
|
||||
)
|
||||
} else {
|
||||
syntheticElseNode.mergeIncomingFlow()
|
||||
}
|
||||
}
|
||||
whenExitNode.mergeIncomingFlow(updateReceivers = true)
|
||||
@@ -409,7 +422,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
private fun exitCommonLoop(exitNode: LoopExitNode) {
|
||||
val singlePreviousNode = exitNode.previousNodes.singleOrNull { !it.isDead }
|
||||
if (singlePreviousNode is LoopConditionExitNode) {
|
||||
val variable = variableStorage.getOrCreateVariable(singlePreviousNode.fir)
|
||||
val variable = variableStorage.getOrCreateVariable(exitNode.previousFlow, singlePreviousNode.fir)
|
||||
exitNode.flow = logicSystem.approveStatementsInsideFlow(
|
||||
exitNode.flow,
|
||||
variable eq false,
|
||||
@@ -428,15 +441,15 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
fun exitWhileLoopCondition(loop: FirLoop) {
|
||||
val (loopConditionExitNode, loopBlockEnterNode) = graphBuilder.exitWhileLoopCondition(loop)
|
||||
loopConditionExitNode.mergeIncomingFlow()
|
||||
loopBlockEnterNode.mergeIncomingFlow()
|
||||
variableStorage[loop.condition]?.let { conditionVariable ->
|
||||
loopBlockEnterNode.flow = logicSystem.approveStatementsInsideFlow(
|
||||
loopBlockEnterNode.flow,
|
||||
val conditionExitFlow = loopConditionExitNode.flow
|
||||
loopBlockEnterNode.flow = variableStorage.getVariable(loop.condition, conditionExitFlow)?.let { conditionVariable ->
|
||||
logicSystem.approveStatementsInsideFlow(
|
||||
conditionExitFlow,
|
||||
conditionVariable eq true,
|
||||
shouldForkFlow = false,
|
||||
shouldForkFlow = true,
|
||||
shouldRemoveSynthetics = false
|
||||
)
|
||||
}
|
||||
} ?: logicSystem.forkFlow(conditionExitFlow)
|
||||
}
|
||||
|
||||
fun exitWhileLoop(loop: FirLoop) {
|
||||
@@ -526,7 +539,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
?.withNullability(ConeNullability.NOT_NULL)
|
||||
?: return@let
|
||||
|
||||
val variable = variableStorage.getOrCreateVariable(receiver)
|
||||
val variable = variableStorage.getOrCreateVariable(flow, receiver)
|
||||
if (variable is RealVariable) {
|
||||
if (shouldFork) {
|
||||
flow = logicSystem.forkFlow(flow)
|
||||
@@ -563,11 +576,12 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
private fun exitSafeCall(qualifiedAccess: FirQualifiedAccess) {
|
||||
if (!qualifiedAccess.safe) return
|
||||
val node = graphBuilder.exitSafeCall(qualifiedAccess).mergeIncomingFlow()
|
||||
val variable = variableStorage.getOrCreateVariable(qualifiedAccess)
|
||||
val previousFlow = node.previousFlow
|
||||
val variable = variableStorage.getOrCreateVariable(previousFlow, qualifiedAccess)
|
||||
val receiverVariable = when (variable) {
|
||||
// There is some bug with invokes. See KT-36014
|
||||
is RealVariable -> variable.explicitReceiverVariable ?: return
|
||||
is SyntheticVariable -> variableStorage.getOrCreateVariable(qualifiedAccess.explicitReceiver!!)
|
||||
is SyntheticVariable -> variableStorage.getOrCreateVariable(previousFlow, qualifiedAccess.explicitReceiver!!)
|
||||
}
|
||||
logicSystem.addImplication(node.flow, (variable notEq null) implies (receiverVariable notEq null))
|
||||
if (receiverVariable.isReal()) {
|
||||
@@ -582,12 +596,13 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
val argumentsMapping = createArgumentsMapping(functionCall) ?: return
|
||||
contractDescriptionVisitingMode = true
|
||||
graphBuilder.enterContract(functionCall).mergeIncomingFlow()
|
||||
val functionCallVariable = variableStorage.getOrCreateVariable(functionCall)
|
||||
val lastFlow = graphBuilder.lastNode.flow
|
||||
val functionCallVariable = variableStorage.getOrCreateVariable(lastFlow, functionCall)
|
||||
for (conditionalEffect in conditionalEffects) {
|
||||
val fir = conditionalEffect.buildContractFir(argumentsMapping) ?: continue
|
||||
val effect = conditionalEffect.effect as? ConeReturnsEffectDeclaration ?: continue
|
||||
fir.transformSingle(components.transformer, ResolutionMode.ContextDependent)
|
||||
val argumentVariable = variableStorage.getOrCreateVariable(fir)
|
||||
val argumentVariable = variableStorage.getOrCreateVariable(lastFlow, fir)
|
||||
val lastNode = graphBuilder.lastNode
|
||||
when (val value = effect.value) {
|
||||
ConeConstantReference.WILDCARD -> {
|
||||
@@ -644,21 +659,21 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
exitVariableInitialization(node, assignment.rValue, property, assignment)
|
||||
}
|
||||
|
||||
private fun exitVariableInitialization(node: CFGNode<*>, initializer: FirExpression, variable: FirProperty, assignment: FirVariableAssignment?) {
|
||||
var propertyVariable = variableStorage.getOrCreateRealVariable(variable.symbol, assignment ?: variable)
|
||||
val isVariableDeclaration = assignment == null
|
||||
if (!isVariableDeclaration) {
|
||||
// Don't remove statements for variable under alias
|
||||
if (propertyVariable.identifier.symbol == variable.symbol) {
|
||||
node.flow.removeAllAboutVariable(propertyVariable)
|
||||
} else {
|
||||
variableStorage.unboundPossiblyAliasedVariable(variable.symbol)
|
||||
// We should create new dataFlowVariable for local variable without alias
|
||||
propertyVariable = variableStorage.getOrCreateRealVariable(variable.symbol, assignment ?: variable)
|
||||
}
|
||||
private fun exitVariableInitialization(node: CFGNode<*>, initializer: FirExpression, property: FirProperty, assignment: FirVariableAssignment?) {
|
||||
val flow = node.flow
|
||||
val propertyVariable = variableStorage.getOrCreateRealVariableWithoutUnwrappingAlias(flow, property.symbol, assignment ?: property)
|
||||
val isAssignment = assignment != null
|
||||
if (isAssignment) {
|
||||
logicSystem.removeLocalVariableAlias(flow, propertyVariable)
|
||||
flow.removeAllAboutVariable(propertyVariable)
|
||||
}
|
||||
|
||||
variableStorage[initializer]?.safeAs<SyntheticVariable>()?.let { initializerVariable ->
|
||||
variableStorage.getOrCreateRealVariable(flow, initializer.symbol, initializer)?.let { initializerVariable ->
|
||||
logicSystem.addLocalVariableAlias(flow, propertyVariable, initializerVariable)
|
||||
// node.flow.addImplication((propertyVariable notEq null) implies (initializerVariable notEq null))
|
||||
}
|
||||
|
||||
variableStorage.getSyntheticVariable(initializer)?.let { initializerVariable ->
|
||||
/*
|
||||
* That part is needed for cases like that:
|
||||
*
|
||||
@@ -668,19 +683,12 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
* x.length
|
||||
* }
|
||||
*/
|
||||
logicSystem.replaceVariableFromConditionInStatements(node.flow, initializerVariable, propertyVariable)
|
||||
return
|
||||
logicSystem.replaceVariableFromConditionInStatements(flow, initializerVariable, propertyVariable)
|
||||
}
|
||||
|
||||
variableStorage.getOrCreateRealVariable(initializer.symbol, initializer)?.let { initializerVariable ->
|
||||
if (initializerVariable.isStable) {
|
||||
variableStorage.attachSymbolToVariable(variable.symbol, initializerVariable)
|
||||
}
|
||||
node.flow.addImplication((propertyVariable notEq null) implies (initializerVariable notEq null))
|
||||
}
|
||||
|
||||
if (!isVariableDeclaration) {
|
||||
node.flow.addTypeStatement(propertyVariable typeEq initializer.typeRef.coneTypeUnsafe<ConeKotlinType>())
|
||||
if (isAssignment) {
|
||||
if (initializer is FirConstExpression<*> && initializer.kind == FirConstKind.Null) return
|
||||
flow.addTypeStatement(propertyVariable typeEq initializer.typeRef.coneTypeUnsafe<ConeKotlinType>())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -722,7 +730,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
private fun exitLeftArgumentOfBinaryBooleanOperator(leftNode: CFGNode<*>, rightNode: CFGNode<*>, isAnd: Boolean) {
|
||||
val parentFlow = leftNode.firstPreviousNode.flow
|
||||
leftNode.flow = logicSystem.forkFlow(parentFlow)
|
||||
val leftOperandVariable = variableStorage.getOrCreateVariable(leftNode.firstPreviousNode.fir)
|
||||
val leftOperandVariable = variableStorage.getOrCreateVariable(parentFlow, leftNode.firstPreviousNode.fir)
|
||||
rightNode.flow = logicSystem.approveStatementsInsideFlow(
|
||||
parentFlow,
|
||||
leftOperandVariable eq isAnd,
|
||||
@@ -750,9 +758,9 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
* But since conditions with const are rare it can be delayed
|
||||
*/
|
||||
|
||||
val leftVariable = variableStorage.getOrCreateVariable(binaryLogicExpression.leftOperand)
|
||||
val rightVariable = variableStorage.getOrCreateVariable(binaryLogicExpression.rightOperand)
|
||||
val operatorVariable = variableStorage.getOrCreateVariable(binaryLogicExpression)
|
||||
val leftVariable = variableStorage.getOrCreateVariable(flow, binaryLogicExpression.leftOperand)
|
||||
val rightVariable = variableStorage.getOrCreateVariable(flow, binaryLogicExpression.rightOperand)
|
||||
val operatorVariable = variableStorage.getOrCreateVariable(flow, binaryLogicExpression)
|
||||
|
||||
val (conditionalFromLeft, conditionalFromRight, approvedFromRight) = logicSystem.collectInfoForBooleanOperator(
|
||||
flowFromLeft,
|
||||
@@ -792,8 +800,9 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
|
||||
|
||||
private fun exitBooleanNot(functionCall: FirFunctionCall, node: FunctionCallNode) {
|
||||
val booleanExpressionVariable = variableStorage.getOrCreateVariable(node.firstPreviousNode.fir)
|
||||
val variable = variableStorage.getOrCreateVariable(functionCall)
|
||||
val previousFlow = node.previousFlow
|
||||
val booleanExpressionVariable = variableStorage.getOrCreateVariable(previousFlow, node.firstPreviousNode.fir)
|
||||
val variable = variableStorage.getOrCreateVariable(previousFlow, functionCall)
|
||||
logicSystem.replaceVariableFromConditionInStatements(
|
||||
node.flow,
|
||||
booleanExpressionVariable,
|
||||
@@ -856,4 +865,17 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
if (variable == null) return
|
||||
logicSystem.removeAllAboutVariable(this, variable)
|
||||
}
|
||||
|
||||
private val CFGNode<*>.previousFlow : FLOW
|
||||
get() = firstPreviousNode.flow
|
||||
}
|
||||
|
||||
@DfaInternals
|
||||
fun FirElement.extractReturnType(): ConeKotlinType = when (this) {
|
||||
is FirVariable<*> -> returnTypeRef.coneTypeUnsafe()
|
||||
is FirSimpleFunction -> receiverTypeRef?.coneTypeUnsafe()
|
||||
is FirAnonymousFunction -> receiverTypeRef?.coneTypeUnsafe()
|
||||
is FirRegularClass -> defaultType()
|
||||
is FirAnonymousObject -> typeRef.coneTypeUnsafe()
|
||||
else -> null
|
||||
} ?: throw IllegalArgumentException("Unsupported fir: $this")
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.fir.resolve.dfa
|
||||
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.commonSuperTypeOrNull
|
||||
|
||||
@@ -14,6 +15,13 @@ abstract class Flow {
|
||||
abstract fun getImplications(variable: DataFlowVariable): Collection<Implication>
|
||||
abstract fun getVariablesInTypeStatements(): Collection<RealVariable>
|
||||
abstract fun removeOperations(variable: DataFlowVariable): Collection<Implication>
|
||||
|
||||
abstract val directAliasMap: Map<RealVariable, RealVariable>
|
||||
abstract val backwardsAliasMap: Map<RealVariable, List<RealVariable>>
|
||||
}
|
||||
|
||||
fun Flow.unwrapVariable(variable: RealVariable): RealVariable {
|
||||
return directAliasMap[variable] ?: variable
|
||||
}
|
||||
|
||||
abstract class LogicSystem<FLOW : Flow>(protected val context: ConeInferenceContext) {
|
||||
@@ -45,6 +53,9 @@ abstract class LogicSystem<FLOW : Flow>(protected val context: ConeInferenceCont
|
||||
shouldRemoveSynthetics: Boolean,
|
||||
): FLOW
|
||||
|
||||
abstract fun addLocalVariableAlias(flow: FLOW, alias: RealVariable, underlyingVariable: RealVariable)
|
||||
abstract fun removeLocalVariableAlias(flow: FLOW, alias: RealVariable)
|
||||
|
||||
protected abstract fun getImplicationsWithVariable(flow: FLOW, variable: DataFlowVariable): Collection<Implication>
|
||||
|
||||
// ------------------------------- Callbacks for updating implicit receiver stack -------------------------------
|
||||
|
||||
+79
-7
@@ -7,8 +7,11 @@ package org.jetbrains.kotlin.fir.resolve.dfa
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap
|
||||
import kotlinx.collections.immutable.*
|
||||
import org.jetbrains.kotlin.fir.declarations.FirVariable
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.coneTypeUnsafe
|
||||
import java.util.*
|
||||
import kotlin.NoSuchElementException
|
||||
|
||||
@@ -42,12 +45,26 @@ class PersistentFlow : Flow {
|
||||
var logicStatements: PersistentImplications
|
||||
val level: Int
|
||||
var approvedTypeStatementsDiff: PersistentApprovedTypeStatements = persistentHashMapOf()
|
||||
var updatedAliasDiff: PersistentSet<RealVariable> = persistentSetOf()
|
||||
|
||||
/*
|
||||
* val x = a
|
||||
* val y = a
|
||||
*
|
||||
* directAliasMap: { x -> a, y -> a}
|
||||
* backwardsAliasMap: { a -> [x, y] }
|
||||
*/
|
||||
override var directAliasMap: PersistentMap<RealVariable, RealVariable>
|
||||
override var backwardsAliasMap: PersistentMap<RealVariable, PersistentList<RealVariable>>
|
||||
|
||||
constructor(previousFlow: PersistentFlow) {
|
||||
this.previousFlow = previousFlow
|
||||
approvedTypeStatements = previousFlow.approvedTypeStatements
|
||||
logicStatements = previousFlow.logicStatements
|
||||
level = previousFlow.level + 1
|
||||
|
||||
directAliasMap = previousFlow.directAliasMap
|
||||
backwardsAliasMap = previousFlow.backwardsAliasMap
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@@ -55,6 +72,9 @@ class PersistentFlow : Flow {
|
||||
approvedTypeStatements = persistentHashMapOf()
|
||||
logicStatements = persistentHashMapOf()
|
||||
level = 1
|
||||
|
||||
directAliasMap = persistentMapOf()
|
||||
backwardsAliasMap = persistentMapOf()
|
||||
}
|
||||
|
||||
override fun getTypeStatement(variable: RealVariable): TypeStatement? {
|
||||
@@ -90,8 +110,22 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
|
||||
override fun joinFlow(flows: Collection<PersistentFlow>): PersistentFlow {
|
||||
if (flows.isEmpty()) return createEmptyFlow()
|
||||
flows.singleOrNull()?.let { return it }
|
||||
val flowsSize = flows.size
|
||||
|
||||
val aliasedVariablesThatDontChangeAlias = mutableMapOf<RealVariable, RealVariable>()
|
||||
flows.flatMapTo(mutableSetOf()) { it.directAliasMap.keys }.forEach { aliasedVariable ->
|
||||
val originals = flows.map { it.directAliasMap[aliasedVariable] ?: return@forEach }
|
||||
if (originals.size != flowsSize) return@forEach
|
||||
val firstOriginal = originals.first()
|
||||
if (originals.all { it == firstOriginal }) {
|
||||
aliasedVariablesThatDontChangeAlias[aliasedVariable] = firstOriginal
|
||||
}
|
||||
}
|
||||
|
||||
val commonFlow = flows.reduce(::lowestCommonFlow)
|
||||
val commonVariables = flows.map { it.diffVariablesIterable(commonFlow).toList() }
|
||||
val commonVariables = flows.map {
|
||||
it.diffVariablesIterable(commonFlow, aliasedVariablesThatDontChangeAlias.keys).toList()
|
||||
}
|
||||
.intersectSets()
|
||||
.takeIf { it.isNotEmpty() }
|
||||
?: return commonFlow
|
||||
@@ -105,19 +139,48 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
|
||||
}
|
||||
}
|
||||
|
||||
for ((alias, underlyingVariable) in aliasedVariablesThatDontChangeAlias) {
|
||||
addLocalVariableAlias(commonFlow,alias, underlyingVariable)
|
||||
}
|
||||
|
||||
updateAllReceivers(commonFlow)
|
||||
|
||||
return commonFlow
|
||||
}
|
||||
|
||||
override fun addLocalVariableAlias(flow: PersistentFlow, alias: RealVariable, underlyingVariable: RealVariable) {
|
||||
removeLocalVariableAlias(flow, alias)
|
||||
flow.directAliasMap = flow.directAliasMap.put(alias, underlyingVariable)
|
||||
flow.backwardsAliasMap = flow.backwardsAliasMap.put(
|
||||
underlyingVariable,
|
||||
{ persistentListOf(alias) },
|
||||
{ variables -> variables + alias }
|
||||
)
|
||||
}
|
||||
|
||||
override fun removeLocalVariableAlias(flow: PersistentFlow, alias: RealVariable) {
|
||||
flow.updatedAliasDiff += alias
|
||||
val original = flow.directAliasMap[alias] ?: return
|
||||
flow.directAliasMap = flow.directAliasMap.remove(alias)
|
||||
val variables = flow.backwardsAliasMap.getValue(original)
|
||||
flow.backwardsAliasMap = flow.backwardsAliasMap.put(original, variables - alias)
|
||||
}
|
||||
|
||||
@UseExperimental(DfaInternals::class)
|
||||
private fun PersistentFlow.getApprovedTypeStatementsDiff(variable: RealVariable, parentFlow: PersistentFlow): MutableTypeStatement {
|
||||
var flow = this
|
||||
val result = MutableTypeStatement(variable)
|
||||
while (flow != parentFlow) {
|
||||
flow.approvedTypeStatementsDiff[variable]?.let {
|
||||
result += it
|
||||
val variableUnderAlias = directAliasMap[variable]
|
||||
if (variableUnderAlias == null) {
|
||||
while (flow != parentFlow) {
|
||||
flow.approvedTypeStatementsDiff[variable]?.let {
|
||||
result += it
|
||||
}
|
||||
flow = flow.previousFlow!!
|
||||
}
|
||||
flow = flow.previousFlow!!
|
||||
} else {
|
||||
result.exactType += variableUnderAlias.originalType
|
||||
flow.approvedTypeStatements[variableUnderAlias]?.let { result += it }
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -126,10 +189,19 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
|
||||
* This is an iterable over real variable that has known facts in flow range
|
||||
* from [this] to [parentFlow]
|
||||
*/
|
||||
private fun PersistentFlow.diffVariablesIterable(parentFlow: PersistentFlow): Iterable<RealVariable> =
|
||||
private fun PersistentFlow.diffVariablesIterable(
|
||||
parentFlow: PersistentFlow,
|
||||
aliasedVariablesThatDontChangeAlias: Set<RealVariable>
|
||||
): Iterable<RealVariable> =
|
||||
object : DiffIterable<RealVariable>(parentFlow, this) {
|
||||
override fun extractIterator(flow: PersistentFlow): Iterator<RealVariable> {
|
||||
return flow.approvedTypeStatementsDiff.keys.iterator()
|
||||
val variablesWithNewInfo = flow.approvedTypeStatementsDiff.keys
|
||||
val updatedVariables = ArrayList(variablesWithNewInfo)
|
||||
updatedVariables += flow.updatedAliasDiff
|
||||
variablesWithNewInfo.flatMapTo(updatedVariables) { variableWithNewInfo ->
|
||||
flow.backwardsAliasMap[variableWithNewInfo]?.filter { it !in aliasedVariablesThatDontChangeAlias } ?: emptyList()
|
||||
}
|
||||
return updatedVariables.iterator()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,23 +5,40 @@
|
||||
|
||||
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.modality
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor
|
||||
import org.jetbrains.kotlin.fir.references.FirThisReference
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
@UseExperimental(DfaInternals::class)
|
||||
class VariableStorage {
|
||||
class VariableStorage(val session: FirSession) {
|
||||
private var counter = 1
|
||||
private val realVariables: MutableMap<Identifier, RealVariable> = HashMap()
|
||||
private val syntheticVariables: MutableMap<FirElement, SyntheticVariable> = HashMap()
|
||||
private val localVariableAliases: MutableMap<AbstractFirBasedSymbol<*>, Identifier> = HashMap()
|
||||
|
||||
fun getOrCreateRealVariable(symbol: AbstractFirBasedSymbol<*>, fir: FirElement): RealVariable {
|
||||
fun getOrCreateRealVariableWithoutUnwrappingAlias(flow: Flow, symbol: AbstractFirBasedSymbol<*>, fir: FirElement): RealVariable {
|
||||
val realFir = fir.unwrapElement()
|
||||
val identifier = getIdentifierBySymbol(symbol, realFir)
|
||||
return realVariables.getOrPut(identifier) { createRealVariableInternal(identifier, realFir) }
|
||||
val identifier = getIdentifierBySymbol(flow, symbol, realFir)
|
||||
return realVariables.getOrPut(identifier) { createRealVariableInternal(flow, identifier, realFir) }
|
||||
}
|
||||
|
||||
private fun getOrCreateRealVariable(flow: Flow, symbol: AbstractFirBasedSymbol<*>, fir: FirElement): RealVariable {
|
||||
val variable = getOrCreateRealVariableWithoutUnwrappingAlias(flow, symbol, fir)
|
||||
return flow.directAliasMap[variable] ?: variable
|
||||
}
|
||||
|
||||
private fun FirElement.unwrapElement(): FirElement = when (this) {
|
||||
@@ -31,23 +48,22 @@ class VariableStorage {
|
||||
}
|
||||
|
||||
private fun getIdentifierBySymbol(
|
||||
flow: Flow,
|
||||
symbol: AbstractFirBasedSymbol<*>,
|
||||
fir: FirElement,
|
||||
): Identifier {
|
||||
return localVariableAliases[symbol] ?: run {
|
||||
val expression = fir as? FirQualifiedAccess
|
||||
Identifier(
|
||||
symbol,
|
||||
expression?.dispatchReceiver?.takeIf { it != FirNoReceiverExpression }?.let(this::getOrCreateVariable),
|
||||
expression?.extensionReceiver?.takeIf { it != FirNoReceiverExpression }?.let(this::getOrCreateVariable)
|
||||
)
|
||||
}
|
||||
val expression = fir as? FirQualifiedAccess
|
||||
return Identifier(
|
||||
symbol,
|
||||
expression?.dispatchReceiver?.takeIf { it != FirNoReceiverExpression }?.let { getOrCreateVariable(flow, it) },
|
||||
expression?.extensionReceiver?.takeIf { it != FirNoReceiverExpression }?.let { getOrCreateVariable(flow, it) }
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* [originalFir] used for extracting expression under <when_subject> and extracting receiver
|
||||
*/
|
||||
private fun createRealVariableInternal(identifier: Identifier, originalFir: FirElement): RealVariable {
|
||||
private fun createRealVariableInternal(flow: Flow, identifier: Identifier, originalFir: FirElement): RealVariable {
|
||||
val receiver: FirExpression?
|
||||
val isThisReference: Boolean
|
||||
val expression: FirQualifiedAccess? = when (originalFir) {
|
||||
@@ -66,57 +82,62 @@ class VariableStorage {
|
||||
isThisReference = false
|
||||
}
|
||||
|
||||
val receiverVariable = receiver?.let { getOrCreateVariable(it) }
|
||||
return RealVariable(identifier, isThisReference, receiverVariable, counter++)
|
||||
val receiverVariable = receiver?.let { getOrCreateVariable(flow, it) }
|
||||
val originalType: ConeKotlinType = when (originalFir) {
|
||||
is FirExpression -> originalFir.typeRef.coneTypeUnsafe()
|
||||
is FirProperty -> originalFir.returnTypeRef.coneTypeUnsafe()
|
||||
is FirVariableAssignment -> identifier.symbol.fir.extractReturnType()
|
||||
else -> throw IllegalStateException("Should not be here: $originalFir")
|
||||
}
|
||||
return RealVariable(identifier, isThisReference, receiverVariable, originalType, counter++)
|
||||
}
|
||||
|
||||
@JvmName("getOrCreateRealVariableOrNull")
|
||||
fun getOrCreateRealVariable(symbol: AbstractFirBasedSymbol<*>?, fir: FirElement): RealVariable? =
|
||||
symbol?.let { getOrCreateRealVariable(it, fir) }
|
||||
fun getOrCreateRealVariable(flow: Flow, symbol: AbstractFirBasedSymbol<*>?, fir: FirElement): RealVariable? =
|
||||
symbol.takeIf { it.isStable(fir) }?.let { getOrCreateRealVariable(flow, it, fir) }
|
||||
|
||||
fun createSyntheticVariable(fir: FirElement): SyntheticVariable =
|
||||
SyntheticVariable(fir, counter++).also { syntheticVariables[fir] = it }
|
||||
|
||||
fun getOrCreateVariable(fir: FirElement): DataFlowVariable {
|
||||
fun getOrCreateVariable(flow: Flow, fir: FirElement): DataFlowVariable {
|
||||
val realFir = fir.unwrapElement()
|
||||
return when (val symbol = realFir.symbol) {
|
||||
null -> syntheticVariables[realFir] ?: createSyntheticVariable(realFir)
|
||||
else -> getOrCreateRealVariable(symbol, realFir)
|
||||
val symbol = realFir.symbol
|
||||
return if (symbol.isStable(realFir)) {
|
||||
getOrCreateRealVariable(flow, symbol!!, realFir)
|
||||
} else {
|
||||
syntheticVariables[realFir] ?: createSyntheticVariable(realFir)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Also removes existing real variable for [varSymbol] if it exists
|
||||
*/
|
||||
fun attachSymbolToVariable(varSymbol: AbstractFirBasedSymbol<*>, targetVariable: RealVariable) {
|
||||
localVariableAliases[varSymbol] = targetVariable.identifier
|
||||
realVariables.remove(Identifier(varSymbol, null, null))
|
||||
fun getRealVariableWithoutUnwrappingAlias(symbol: AbstractFirBasedSymbol<*>?, fir: FirElement, flow: Flow): RealVariable? {
|
||||
val realFir = fir.unwrapElement()
|
||||
return symbol.takeIf { it.isStable(realFir) }?.let {
|
||||
realVariables[getIdentifierBySymbol(flow, it, realFir.unwrapElement())]
|
||||
}
|
||||
}
|
||||
|
||||
operator fun get(symbol: AbstractFirBasedSymbol<*>?, fir: FirElement): RealVariable? {
|
||||
return symbol?.let { realVariables[getIdentifierBySymbol(it, fir.unwrapElement())] }
|
||||
fun getRealVariable(symbol: AbstractFirBasedSymbol<*>?, fir: FirElement, flow: Flow): RealVariable? {
|
||||
return getRealVariableWithoutUnwrappingAlias(symbol, fir, flow)?.let { flow.unwrapVariable(it) }
|
||||
}
|
||||
|
||||
operator fun get(fir: FirElement): DataFlowVariable? {
|
||||
fun getSyntheticVariable(fir: FirElement): SyntheticVariable? {
|
||||
return syntheticVariables[fir.unwrapElement()]
|
||||
}
|
||||
|
||||
fun getVariable(fir: FirElement, flow: Flow): DataFlowVariable? {
|
||||
val realFir = fir.unwrapElement()
|
||||
val symbol = realFir.symbol
|
||||
return if (symbol != null) {
|
||||
get(symbol, realFir)
|
||||
return if (symbol.isStable(fir)) {
|
||||
getRealVariable(symbol, realFir, flow)
|
||||
} else {
|
||||
syntheticVariables[realFir]
|
||||
getSyntheticVariable(fir)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeRealVariable(symbol: AbstractFirBasedSymbol<*>) {
|
||||
// TODO: this shit fails
|
||||
// assert(!localVariableAliases.containsValue(symbol))
|
||||
realVariables.remove(Identifier(symbol, null, null))
|
||||
}
|
||||
|
||||
fun unboundPossiblyAliasedVariable(symbol: AbstractFirBasedSymbol<*>) {
|
||||
localVariableAliases.remove(symbol)
|
||||
}
|
||||
|
||||
fun removeSyntheticVariable(variable: DataFlowVariable) {
|
||||
if (variable !is SyntheticVariable) return
|
||||
syntheticVariables.remove(variable.fir)
|
||||
@@ -126,6 +147,44 @@ class VariableStorage {
|
||||
counter = 0
|
||||
realVariables.clear()
|
||||
syntheticVariables.clear()
|
||||
localVariableAliases.clear()
|
||||
}
|
||||
|
||||
@UseExperimental(ExperimentalContracts::class)
|
||||
fun AbstractFirBasedSymbol<*>?.isStable(originalFir: FirElement): Boolean {
|
||||
contract {
|
||||
returns(true) implies(this@isStable != null)
|
||||
}
|
||||
when (this) {
|
||||
is FirFunctionSymbol<*>,
|
||||
is FirClassSymbol<*>,
|
||||
is FirBackingFieldSymbol -> return true
|
||||
null -> return false
|
||||
}
|
||||
if (originalFir is FirThisReceiverExpression) return true
|
||||
if (this !is FirVariableSymbol<*>) return false
|
||||
|
||||
val property = this.fir as? FirProperty ?: return true
|
||||
|
||||
return when {
|
||||
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 as? FirQualifiedAccess)?.dispatchReceiver ?: return false
|
||||
val propertyClassName = (this as FirPropertySymbol).callableId.classId
|
||||
val receiverType = dispatchReceiver.typeRef.coneTypeSafe<ConeClassLikeType>()?.fullyExpandedType(session) ?: return false
|
||||
val receiverSymbol = receiverType.fullyExpandedType(session).lookupTag.toSymbol(session) ?: return false
|
||||
val receiverClassName = receiverSymbol.classId
|
||||
if (propertyClassName != receiverClassName) {
|
||||
when (val receiverFir = receiverSymbol.fir) {
|
||||
is FirAnonymousObject -> true
|
||||
is FirRegularClass -> receiverFir.modality == Modality.FINAL
|
||||
else -> throw IllegalStateException("Should not be here: $receiverFir")
|
||||
}
|
||||
} else false
|
||||
}
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -93,7 +93,7 @@ sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level:
|
||||
}
|
||||
}
|
||||
|
||||
val CFGNode<*>.firstPreviousNode: CFGNode<*> get() = previousNodes.first()
|
||||
val CFGNode<*>.firstPreviousNode: CFGNode<*> get() = previousNodes[0]
|
||||
|
||||
interface EnterNodeMarker
|
||||
interface ExitNodeMarker
|
||||
|
||||
@@ -41,6 +41,7 @@ class RealVariable(
|
||||
val identifier: Identifier,
|
||||
val isThisReference: Boolean,
|
||||
val explicitReceiverVariable: DataFlowVariable?,
|
||||
val originalType: ConeKotlinType,
|
||||
variableIndexForDebug: Int
|
||||
) : DataFlowVariable(variableIndexForDebug) {
|
||||
override val isStable: Boolean by lazy {
|
||||
@@ -51,6 +52,8 @@ class RealVariable(
|
||||
property.isLocal -> true
|
||||
property.isVar -> false
|
||||
property.modality != Modality.FINAL -> false
|
||||
property.receiverTypeRef != null -> false
|
||||
property.getter != null -> false
|
||||
// TODO: getters, delegates
|
||||
else -> true
|
||||
}
|
||||
|
||||
@@ -5,18 +5,26 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.resolve.dfa
|
||||
|
||||
import kotlinx.collections.immutable.PersistentMap
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.FirSymbolOwner
|
||||
import org.jetbrains.kotlin.fir.contracts.description.ConeBooleanConstantReference
|
||||
import org.jetbrains.kotlin.fir.contracts.description.ConeConstantReference
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.FirThisReference
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate
|
||||
import org.jetbrains.kotlin.fir.resolve.directExpansionType
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirAccessorSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||
import org.jetbrains.kotlin.fir.types.ConeFlexibleType
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.coneTypeSafe
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
@@ -67,6 +75,20 @@ internal inline fun <K, V> MutableMap<K, V>.put(key: K, value: V, remappingFunct
|
||||
}
|
||||
}
|
||||
|
||||
@UseExperimental(ExperimentalContracts::class)
|
||||
internal inline fun <K, V> PersistentMap<K, V>.put(key: K, valueProducer: () -> V, remappingFunction: (existing: V) -> V): PersistentMap<K, V> {
|
||||
contract {
|
||||
callsInPlace(remappingFunction, InvocationKind.AT_MOST_ONCE)
|
||||
callsInPlace(valueProducer, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
val existing = this[key]
|
||||
return if (existing == null) {
|
||||
put(key, valueProducer())
|
||||
} else {
|
||||
put(key, remappingFunction(existing))
|
||||
}
|
||||
}
|
||||
|
||||
@DfaInternals
|
||||
internal fun FirOperation.invert(): FirOperation = when (this) {
|
||||
FirOperation.EQ -> FirOperation.NOT_EQ
|
||||
@@ -119,3 +141,8 @@ internal val FirResolvable.symbol: AbstractFirBasedSymbol<*>?
|
||||
is FirNamedReferenceWithCandidate -> reference.candidateSymbol
|
||||
else -> null
|
||||
}
|
||||
|
||||
//val ConeKotlinType.isNothingOrNullableNothing: Boolean = when (this) {
|
||||
// is ConeFlexibleType -> lowerBound.isNothingOrNullableNothing
|
||||
// else -> false
|
||||
//}
|
||||
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
interface A {
|
||||
val s: String
|
||||
}
|
||||
|
||||
fun test(list: List<A>) {
|
||||
var goodA: A? = null
|
||||
for (a in list) {
|
||||
if (goodA == null) {
|
||||
goodA = a
|
||||
continue
|
||||
}
|
||||
goodA.<!INAPPLICABLE_CANDIDATE!>s<!>
|
||||
}
|
||||
}
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
FILE: noBoundSmartcastAfterContinue.kt
|
||||
public abstract interface A : R|kotlin/Any| {
|
||||
public abstract val s: R|kotlin/String|
|
||||
public get(): R|kotlin/String|
|
||||
|
||||
}
|
||||
public final fun test(list: R|kotlin/collections/List<A>|): R|kotlin/Unit| {
|
||||
lvar goodA: R|A?| = Null(null)
|
||||
lval <range>: R|kotlin/collections/List<A>| = R|<local>/list|
|
||||
lval <iterator>: R|kotlin/collections/Iterator<A>| = R|<local>/<range>|.R|FakeOverride<kotlin/collections/List.iterator: R|kotlin/collections/Iterator<A>|>|()
|
||||
while(R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()) {
|
||||
lval a: R|A| = R|<local>/<iterator>|.R|FakeOverride<kotlin/collections/Iterator.next: R|A|>|()
|
||||
when () {
|
||||
==(R|<local>/goodA|, Null(null)) -> {
|
||||
R|<local>/goodA| = R|<local>/a|
|
||||
continue@@@[R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()]
|
||||
}
|
||||
}
|
||||
|
||||
R|<local>/goodA|.<Inapplicable(WRONG_RECEIVER): [/A.s]>#
|
||||
}
|
||||
|
||||
}
|
||||
+809
@@ -0,0 +1,809 @@
|
||||
digraph noBoundSmartcastAfterContinue_kt {
|
||||
graph [splines=ortho nodesep=3]
|
||||
node [shape=box penwidth=2]
|
||||
edge [penwidth=2]
|
||||
|
||||
subgraph cluster_0 {
|
||||
color=red
|
||||
0 [label="Enter function <init>" style="filled" fillcolor=red];
|
||||
1 [label="Exit function <init>" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
0 -> {1};
|
||||
|
||||
subgraph cluster_1 {
|
||||
color=red
|
||||
2 [label="Enter function getter" style="filled" fillcolor=red];
|
||||
3 [label="Exit function getter" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
2 -> {3};
|
||||
|
||||
subgraph cluster_2 {
|
||||
color=red
|
||||
4 [label="Enter property" style="filled" fillcolor=red];
|
||||
5 [label="Const: String()"];
|
||||
6 [label="Exit property" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
4 -> {5};
|
||||
5 -> {6};
|
||||
|
||||
subgraph cluster_3 {
|
||||
color=red
|
||||
7 [label="Enter function test_0" style="filled" fillcolor=red];
|
||||
8 [label="Const: Null(null)"];
|
||||
9 [label="Variable declaration: lvar goodA: R|A?|"];
|
||||
10 [label="Access variable R|<local>/list|"];
|
||||
11 [label="Variable declaration: lval <range>: R|kotlin/collections/List<A>|"];
|
||||
12 [label="Access variable R|<local>/<range>|"];
|
||||
13 [label="Function call: R|<local>/<range>|.R|FakeOverride<kotlin/collections/List.iterator: R|kotlin/collections/Iterator<A>|>|()"];
|
||||
14 [label="Variable declaration: lval <iterator>: R|kotlin/collections/Iterator<A>|"];
|
||||
subgraph cluster_4 {
|
||||
color=blue
|
||||
15 [label="Enter while loop"];
|
||||
subgraph cluster_5 {
|
||||
color=blue
|
||||
16 [label="Enter loop condition"];
|
||||
17 [label="Access variable R|<local>/<iterator>|"];
|
||||
18 [label="Function call: R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()"];
|
||||
19 [label="Exit loop condition"];
|
||||
}
|
||||
subgraph cluster_6 {
|
||||
color=blue
|
||||
20 [label="Enter loop block"];
|
||||
subgraph cluster_7 {
|
||||
color=blue
|
||||
21 [label="Enter block"];
|
||||
22 [label="Access variable R|<local>/<iterator>|"];
|
||||
23 [label="Function call: R|<local>/<iterator>|.R|FakeOverride<kotlin/collections/Iterator.next: R|A|>|()"];
|
||||
24 [label="Variable declaration: lval a: R|A|"];
|
||||
subgraph cluster_8 {
|
||||
color=blue
|
||||
25 [label="Enter when"];
|
||||
subgraph cluster_9 {
|
||||
color=blue
|
||||
26 [label="Enter when branch condition "];
|
||||
27 [label="Access variable R|<local>/goodA|"];
|
||||
28 [label="Const: Null(null)"];
|
||||
29 [label="Operator =="];
|
||||
30 [label="Exit when branch condition"];
|
||||
}
|
||||
31 [label="Synthetic else branch"];
|
||||
32 [label="Enter when branch result"];
|
||||
subgraph cluster_10 {
|
||||
color=blue
|
||||
33 [label="Enter block"];
|
||||
34 [label="Access variable R|<local>/a|"];
|
||||
35 [label="Assignmenet: R|<local>/goodA|"];
|
||||
36 [label="Jump: continue@@@[R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()] "];
|
||||
37 [label="Stub" style="filled" fillcolor=gray];
|
||||
38 [label="Exit block" style="filled" fillcolor=gray];
|
||||
}
|
||||
39 [label="Exit when branch result" style="filled" fillcolor=gray];
|
||||
40 [label="Exit when"];
|
||||
}
|
||||
41 [label="Access variable R|<local>/goodA|"];
|
||||
42 [label="Access variable R|/A.s|"];
|
||||
43 [label="Exit block"];
|
||||
}
|
||||
44 [label="Exit loop block"];
|
||||
}
|
||||
45 [label="Exit whileloop"];
|
||||
}
|
||||
46 [label="Exit function test_0" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
7 -> {8};
|
||||
8 -> {9};
|
||||
9 -> {10};
|
||||
10 -> {11};
|
||||
11 -> {12};
|
||||
12 -> {13};
|
||||
13 -> {14};
|
||||
14 -> {15};
|
||||
15 -> {16};
|
||||
16 -> {17};
|
||||
17 -> {18};
|
||||
18 -> {19};
|
||||
19 -> {45 20};
|
||||
20 -> {21};
|
||||
21 -> {22};
|
||||
22 -> {23};
|
||||
23 -> {24};
|
||||
24 -> {25};
|
||||
25 -> {26};
|
||||
26 -> {27};
|
||||
27 -> {28};
|
||||
28 -> {29};
|
||||
29 -> {30};
|
||||
30 -> {32 31};
|
||||
31 -> {40};
|
||||
32 -> {33};
|
||||
33 -> {34};
|
||||
34 -> {35};
|
||||
35 -> {36};
|
||||
36 -> {15};
|
||||
36 -> {37} [style=dotted];
|
||||
37 -> {38} [style=dotted];
|
||||
38 -> {39} [style=dotted];
|
||||
39 -> {40} [style=dotted];
|
||||
40 -> {41};
|
||||
41 -> {42};
|
||||
42 -> {43};
|
||||
43 -> {44};
|
||||
44 -> {16};
|
||||
45 -> {46};
|
||||
|
||||
subgraph cluster_11 {
|
||||
color=red
|
||||
47 [label="Enter function test_1" style="filled" fillcolor=red];
|
||||
48 [label="Variable declaration: lval x: R|kotlin/Any|"];
|
||||
subgraph cluster_12 {
|
||||
color=blue
|
||||
49 [label="Enter when"];
|
||||
subgraph cluster_13 {
|
||||
color=blue
|
||||
50 [label="Enter when branch condition "];
|
||||
51 [label="Access variable R|<local>/b|"];
|
||||
52 [label="Exit when branch condition"];
|
||||
}
|
||||
subgraph cluster_14 {
|
||||
color=blue
|
||||
53 [label="Enter when branch condition else"];
|
||||
54 [label="Exit when branch condition"];
|
||||
}
|
||||
55 [label="Enter when branch result"];
|
||||
subgraph cluster_15 {
|
||||
color=blue
|
||||
56 [label="Enter block"];
|
||||
57 [label="Access variable R|<local>/a|"];
|
||||
58 [label="Assignmenet: R|<local>/x|"];
|
||||
59 [label="Exit block"];
|
||||
}
|
||||
60 [label="Exit when branch result"];
|
||||
61 [label="Enter when branch result"];
|
||||
subgraph cluster_16 {
|
||||
color=blue
|
||||
62 [label="Enter block"];
|
||||
63 [label="Function call: R|/A.A|()"];
|
||||
64 [label="Assignmenet: R|<local>/x|"];
|
||||
65 [label="Exit block"];
|
||||
}
|
||||
66 [label="Exit when branch result"];
|
||||
67 [label="Exit when"];
|
||||
}
|
||||
68 [label="Access variable R|<local>/x|"];
|
||||
69 [label="Access variable R|/A.s|"];
|
||||
70 [label="Exit function test_1" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
47 -> {48};
|
||||
48 -> {49};
|
||||
49 -> {50};
|
||||
50 -> {51};
|
||||
51 -> {52};
|
||||
52 -> {61 53};
|
||||
53 -> {54};
|
||||
54 -> {55};
|
||||
55 -> {56};
|
||||
56 -> {57};
|
||||
57 -> {58};
|
||||
58 -> {59};
|
||||
59 -> {60};
|
||||
60 -> {67};
|
||||
61 -> {62};
|
||||
62 -> {63};
|
||||
63 -> {64};
|
||||
64 -> {65};
|
||||
65 -> {66};
|
||||
66 -> {67};
|
||||
67 -> {68};
|
||||
68 -> {69};
|
||||
69 -> {70};
|
||||
|
||||
subgraph cluster_17 {
|
||||
color=red
|
||||
71 [label="Enter function test_2" style="filled" fillcolor=red];
|
||||
72 [label="Variable declaration: lval x: R|kotlin/Any|"];
|
||||
subgraph cluster_18 {
|
||||
color=blue
|
||||
73 [label="Enter when"];
|
||||
subgraph cluster_19 {
|
||||
color=blue
|
||||
74 [label="Enter when branch condition "];
|
||||
75 [label="Access variable R|<local>/b|"];
|
||||
76 [label="Exit when branch condition"];
|
||||
}
|
||||
subgraph cluster_20 {
|
||||
color=blue
|
||||
77 [label="Enter when branch condition else"];
|
||||
78 [label="Exit when branch condition"];
|
||||
}
|
||||
79 [label="Enter when branch result"];
|
||||
subgraph cluster_21 {
|
||||
color=blue
|
||||
80 [label="Enter block"];
|
||||
81 [label="Access variable R|<local>/a|"];
|
||||
82 [label="Assignmenet: R|<local>/x|"];
|
||||
83 [label="Access variable R|<local>/a|"];
|
||||
84 [label="Type operator: a as A"];
|
||||
85 [label="Exit block"];
|
||||
}
|
||||
86 [label="Exit when branch result"];
|
||||
87 [label="Enter when branch result"];
|
||||
subgraph cluster_22 {
|
||||
color=blue
|
||||
88 [label="Enter block"];
|
||||
89 [label="Function call: R|/A.A|()"];
|
||||
90 [label="Assignmenet: R|<local>/x|"];
|
||||
91 [label="Exit block"];
|
||||
}
|
||||
92 [label="Exit when branch result"];
|
||||
93 [label="Exit when"];
|
||||
}
|
||||
94 [label="Access variable R|<local>/x|"];
|
||||
95 [label="Access variable R|/A.s|"];
|
||||
96 [label="Exit function test_2" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
71 -> {72};
|
||||
72 -> {73};
|
||||
73 -> {74};
|
||||
74 -> {75};
|
||||
75 -> {76};
|
||||
76 -> {87 77};
|
||||
77 -> {78};
|
||||
78 -> {79};
|
||||
79 -> {80};
|
||||
80 -> {81};
|
||||
81 -> {82};
|
||||
82 -> {83};
|
||||
83 -> {84};
|
||||
84 -> {85};
|
||||
85 -> {86};
|
||||
86 -> {93};
|
||||
87 -> {88};
|
||||
88 -> {89};
|
||||
89 -> {90};
|
||||
90 -> {91};
|
||||
91 -> {92};
|
||||
92 -> {93};
|
||||
93 -> {94};
|
||||
94 -> {95};
|
||||
95 -> {96};
|
||||
|
||||
subgraph cluster_23 {
|
||||
color=red
|
||||
97 [label="Enter function test_3" style="filled" fillcolor=red];
|
||||
98 [label="Variable declaration: lval x: R|kotlin/Any|"];
|
||||
subgraph cluster_24 {
|
||||
color=blue
|
||||
99 [label="Enter when"];
|
||||
subgraph cluster_25 {
|
||||
color=blue
|
||||
100 [label="Enter when branch condition "];
|
||||
101 [label="Access variable R|<local>/b|"];
|
||||
102 [label="Exit when branch condition"];
|
||||
}
|
||||
subgraph cluster_26 {
|
||||
color=blue
|
||||
103 [label="Enter when branch condition else"];
|
||||
104 [label="Exit when branch condition"];
|
||||
}
|
||||
105 [label="Enter when branch result"];
|
||||
subgraph cluster_27 {
|
||||
color=blue
|
||||
106 [label="Enter block"];
|
||||
107 [label="Access variable R|<local>/a|"];
|
||||
108 [label="Type operator: a as A"];
|
||||
109 [label="Access variable R|<local>/a|"];
|
||||
110 [label="Assignmenet: R|<local>/x|"];
|
||||
111 [label="Exit block"];
|
||||
}
|
||||
112 [label="Exit when branch result"];
|
||||
113 [label="Enter when branch result"];
|
||||
subgraph cluster_28 {
|
||||
color=blue
|
||||
114 [label="Enter block"];
|
||||
115 [label="Function call: R|/A.A|()"];
|
||||
116 [label="Assignmenet: R|<local>/x|"];
|
||||
117 [label="Exit block"];
|
||||
}
|
||||
118 [label="Exit when branch result"];
|
||||
119 [label="Exit when"];
|
||||
}
|
||||
120 [label="Access variable R|<local>/x|"];
|
||||
121 [label="Access variable R|/A.s|"];
|
||||
122 [label="Exit function test_3" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
97 -> {98};
|
||||
98 -> {99};
|
||||
99 -> {100};
|
||||
100 -> {101};
|
||||
101 -> {102};
|
||||
102 -> {113 103};
|
||||
103 -> {104};
|
||||
104 -> {105};
|
||||
105 -> {106};
|
||||
106 -> {107};
|
||||
107 -> {108};
|
||||
108 -> {109};
|
||||
109 -> {110};
|
||||
110 -> {111};
|
||||
111 -> {112};
|
||||
112 -> {119};
|
||||
113 -> {114};
|
||||
114 -> {115};
|
||||
115 -> {116};
|
||||
116 -> {117};
|
||||
117 -> {118};
|
||||
118 -> {119};
|
||||
119 -> {120};
|
||||
120 -> {121};
|
||||
121 -> {122};
|
||||
|
||||
subgraph cluster_29 {
|
||||
color=red
|
||||
123 [label="Enter function test_4" style="filled" fillcolor=red];
|
||||
124 [label="Variable declaration: lval x: R|kotlin/Any|"];
|
||||
subgraph cluster_30 {
|
||||
color=blue
|
||||
125 [label="Enter when"];
|
||||
subgraph cluster_31 {
|
||||
color=blue
|
||||
126 [label="Enter when branch condition "];
|
||||
127 [label="Access variable R|<local>/b|"];
|
||||
128 [label="Exit when branch condition"];
|
||||
}
|
||||
subgraph cluster_32 {
|
||||
color=blue
|
||||
129 [label="Enter when branch condition else"];
|
||||
130 [label="Exit when branch condition"];
|
||||
}
|
||||
131 [label="Enter when branch result"];
|
||||
subgraph cluster_33 {
|
||||
color=blue
|
||||
132 [label="Enter block"];
|
||||
133 [label="Access variable R|<local>/a|"];
|
||||
134 [label="Assignmenet: R|<local>/x|"];
|
||||
135 [label="Exit block"];
|
||||
}
|
||||
136 [label="Exit when branch result"];
|
||||
137 [label="Enter when branch result"];
|
||||
subgraph cluster_34 {
|
||||
color=blue
|
||||
138 [label="Enter block"];
|
||||
139 [label="Access variable R|<local>/a|"];
|
||||
140 [label="Assignmenet: R|<local>/x|"];
|
||||
141 [label="Exit block"];
|
||||
}
|
||||
142 [label="Exit when branch result"];
|
||||
143 [label="Exit when"];
|
||||
}
|
||||
144 [label="Access variable R|<local>/x|"];
|
||||
145 [label="Type operator: x as A"];
|
||||
146 [label="Access variable R|<local>/x|"];
|
||||
147 [label="Access variable R|/A.s|"];
|
||||
148 [label="Access variable R|<local>/a|"];
|
||||
149 [label="Access variable R|/A.s|"];
|
||||
150 [label="Exit function test_4" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
123 -> {124};
|
||||
124 -> {125};
|
||||
125 -> {126};
|
||||
126 -> {127};
|
||||
127 -> {128};
|
||||
128 -> {137 129};
|
||||
129 -> {130};
|
||||
130 -> {131};
|
||||
131 -> {132};
|
||||
132 -> {133};
|
||||
133 -> {134};
|
||||
134 -> {135};
|
||||
135 -> {136};
|
||||
136 -> {143};
|
||||
137 -> {138};
|
||||
138 -> {139};
|
||||
139 -> {140};
|
||||
140 -> {141};
|
||||
141 -> {142};
|
||||
142 -> {143};
|
||||
143 -> {144};
|
||||
144 -> {145};
|
||||
145 -> {146};
|
||||
146 -> {147};
|
||||
147 -> {148};
|
||||
148 -> {149};
|
||||
149 -> {150};
|
||||
|
||||
subgraph cluster_35 {
|
||||
color=red
|
||||
151 [label="Enter function test_5" style="filled" fillcolor=red];
|
||||
152 [label="Variable declaration: lval x: R|kotlin/Any|"];
|
||||
subgraph cluster_36 {
|
||||
color=blue
|
||||
153 [label="Enter when"];
|
||||
subgraph cluster_37 {
|
||||
color=blue
|
||||
154 [label="Enter when branch condition "];
|
||||
155 [label="Access variable R|<local>/b|"];
|
||||
156 [label="Exit when branch condition"];
|
||||
}
|
||||
subgraph cluster_38 {
|
||||
color=blue
|
||||
157 [label="Enter when branch condition else"];
|
||||
158 [label="Exit when branch condition"];
|
||||
}
|
||||
159 [label="Enter when branch result"];
|
||||
subgraph cluster_39 {
|
||||
color=blue
|
||||
160 [label="Enter block"];
|
||||
161 [label="Access variable R|<local>/a|"];
|
||||
162 [label="Assignmenet: R|<local>/x|"];
|
||||
163 [label="Exit block"];
|
||||
}
|
||||
164 [label="Exit when branch result"];
|
||||
165 [label="Enter when branch result"];
|
||||
subgraph cluster_40 {
|
||||
color=blue
|
||||
166 [label="Enter block"];
|
||||
167 [label="Access variable R|<local>/a|"];
|
||||
168 [label="Assignmenet: R|<local>/x|"];
|
||||
169 [label="Exit block"];
|
||||
}
|
||||
170 [label="Exit when branch result"];
|
||||
171 [label="Exit when"];
|
||||
}
|
||||
172 [label="Access variable R|<local>/a|"];
|
||||
173 [label="Type operator: a as A"];
|
||||
174 [label="Access variable R|<local>/x|"];
|
||||
175 [label="Access variable R|/A.s|"];
|
||||
176 [label="Access variable R|<local>/a|"];
|
||||
177 [label="Access variable R|/A.s|"];
|
||||
178 [label="Exit function test_5" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
151 -> {152};
|
||||
152 -> {153};
|
||||
153 -> {154};
|
||||
154 -> {155};
|
||||
155 -> {156};
|
||||
156 -> {165 157};
|
||||
157 -> {158};
|
||||
158 -> {159};
|
||||
159 -> {160};
|
||||
160 -> {161};
|
||||
161 -> {162};
|
||||
162 -> {163};
|
||||
163 -> {164};
|
||||
164 -> {171};
|
||||
165 -> {166};
|
||||
166 -> {167};
|
||||
167 -> {168};
|
||||
168 -> {169};
|
||||
169 -> {170};
|
||||
170 -> {171};
|
||||
171 -> {172};
|
||||
172 -> {173};
|
||||
173 -> {174};
|
||||
174 -> {175};
|
||||
175 -> {176};
|
||||
176 -> {177};
|
||||
177 -> {178};
|
||||
|
||||
subgraph cluster_41 {
|
||||
color=red
|
||||
179 [label="Enter function test_6" style="filled" fillcolor=red];
|
||||
180 [label="Variable declaration: lval x: R|kotlin/Any|"];
|
||||
181 [label="Access variable R|<local>/a|"];
|
||||
182 [label="Assignmenet: R|<local>/x|"];
|
||||
183 [label="Access variable R|<local>/x|"];
|
||||
184 [label="Access variable R|/A.s|"];
|
||||
185 [label="Exit function test_6" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
179 -> {180};
|
||||
180 -> {181};
|
||||
181 -> {182};
|
||||
182 -> {183};
|
||||
183 -> {184};
|
||||
184 -> {185};
|
||||
|
||||
subgraph cluster_42 {
|
||||
color=red
|
||||
186 [label="Enter function test_7" style="filled" fillcolor=red];
|
||||
187 [label="Const: Null(null)"];
|
||||
188 [label="Variable declaration: lval z: R|kotlin/String?|"];
|
||||
189 [label="Access variable R|<local>/z|"];
|
||||
190 [label="Variable declaration: lvar y: R|kotlin/String?|"];
|
||||
191 [label="Access variable R|<local>/y|"];
|
||||
192 [label="Variable declaration: lval x: R|kotlin/String?|"];
|
||||
subgraph cluster_43 {
|
||||
color=blue
|
||||
193 [label="Enter when"];
|
||||
subgraph cluster_44 {
|
||||
color=blue
|
||||
194 [label="Enter when branch condition "];
|
||||
195 [label="Access variable R|<local>/x|"];
|
||||
196 [label="Const: Null(null)"];
|
||||
197 [label="Operator !="];
|
||||
198 [label="Exit when branch condition"];
|
||||
}
|
||||
199 [label="Synthetic else branch"];
|
||||
200 [label="Enter when branch result"];
|
||||
subgraph cluster_45 {
|
||||
color=blue
|
||||
201 [label="Enter block"];
|
||||
202 [label="Access variable R|<local>/x|"];
|
||||
203 [label="Access variable R|kotlin/String.length|"];
|
||||
204 [label="Access variable R|<local>/y|"];
|
||||
205 [label="Access variable R|kotlin/String.length|"];
|
||||
206 [label="Access variable R|<local>/z|"];
|
||||
207 [label="Access variable R|kotlin/String.length|"];
|
||||
208 [label="Exit block"];
|
||||
}
|
||||
209 [label="Exit when branch result"];
|
||||
210 [label="Exit when"];
|
||||
}
|
||||
subgraph cluster_46 {
|
||||
color=blue
|
||||
211 [label="Enter when"];
|
||||
subgraph cluster_47 {
|
||||
color=blue
|
||||
212 [label="Enter when branch condition "];
|
||||
213 [label="Access variable R|<local>/y|"];
|
||||
214 [label="Const: Null(null)"];
|
||||
215 [label="Operator !="];
|
||||
216 [label="Exit when branch condition"];
|
||||
}
|
||||
217 [label="Synthetic else branch"];
|
||||
218 [label="Enter when branch result"];
|
||||
subgraph cluster_48 {
|
||||
color=blue
|
||||
219 [label="Enter block"];
|
||||
220 [label="Access variable R|<local>/x|"];
|
||||
221 [label="Access variable R|kotlin/String.length|"];
|
||||
222 [label="Access variable R|<local>/y|"];
|
||||
223 [label="Access variable R|kotlin/String.length|"];
|
||||
224 [label="Access variable R|<local>/z|"];
|
||||
225 [label="Access variable R|kotlin/String.length|"];
|
||||
226 [label="Exit block"];
|
||||
}
|
||||
227 [label="Exit when branch result"];
|
||||
228 [label="Exit when"];
|
||||
}
|
||||
subgraph cluster_49 {
|
||||
color=blue
|
||||
229 [label="Enter when"];
|
||||
subgraph cluster_50 {
|
||||
color=blue
|
||||
230 [label="Enter when branch condition "];
|
||||
231 [label="Access variable R|<local>/z|"];
|
||||
232 [label="Const: Null(null)"];
|
||||
233 [label="Operator !="];
|
||||
234 [label="Exit when branch condition"];
|
||||
}
|
||||
235 [label="Synthetic else branch"];
|
||||
236 [label="Enter when branch result"];
|
||||
subgraph cluster_51 {
|
||||
color=blue
|
||||
237 [label="Enter block"];
|
||||
238 [label="Access variable R|<local>/x|"];
|
||||
239 [label="Access variable R|kotlin/String.length|"];
|
||||
240 [label="Access variable R|<local>/y|"];
|
||||
241 [label="Access variable R|kotlin/String.length|"];
|
||||
242 [label="Access variable R|<local>/z|"];
|
||||
243 [label="Access variable R|kotlin/String.length|"];
|
||||
244 [label="Exit block"];
|
||||
}
|
||||
245 [label="Exit when branch result"];
|
||||
246 [label="Exit when"];
|
||||
}
|
||||
247 [label="Const: Null(null)"];
|
||||
248 [label="Assignmenet: R|<local>/y|"];
|
||||
subgraph cluster_52 {
|
||||
color=blue
|
||||
249 [label="Enter when"];
|
||||
subgraph cluster_53 {
|
||||
color=blue
|
||||
250 [label="Enter when branch condition "];
|
||||
251 [label="Access variable R|<local>/x|"];
|
||||
252 [label="Const: Null(null)"];
|
||||
253 [label="Operator !="];
|
||||
254 [label="Exit when branch condition"];
|
||||
}
|
||||
255 [label="Synthetic else branch"];
|
||||
256 [label="Enter when branch result"];
|
||||
subgraph cluster_54 {
|
||||
color=blue
|
||||
257 [label="Enter block"];
|
||||
258 [label="Access variable R|<local>/x|"];
|
||||
259 [label="Access variable R|kotlin/String.length|"];
|
||||
260 [label="Access variable R|<local>/y|"];
|
||||
261 [label="Access variable <Inapplicable(WRONG_RECEIVER): [kotlin/String.length]>#"];
|
||||
262 [label="Access variable R|<local>/z|"];
|
||||
263 [label="Access variable R|kotlin/String.length|"];
|
||||
264 [label="Exit block"];
|
||||
}
|
||||
265 [label="Exit when branch result"];
|
||||
266 [label="Exit when"];
|
||||
}
|
||||
subgraph cluster_55 {
|
||||
color=blue
|
||||
267 [label="Enter when"];
|
||||
subgraph cluster_56 {
|
||||
color=blue
|
||||
268 [label="Enter when branch condition "];
|
||||
269 [label="Access variable R|<local>/y|"];
|
||||
270 [label="Const: Null(null)"];
|
||||
271 [label="Operator !="];
|
||||
272 [label="Exit when branch condition"];
|
||||
}
|
||||
273 [label="Synthetic else branch"];
|
||||
274 [label="Enter when branch result"];
|
||||
subgraph cluster_57 {
|
||||
color=blue
|
||||
275 [label="Enter block"];
|
||||
276 [label="Access variable R|<local>/x|"];
|
||||
277 [label="Access variable <Inapplicable(WRONG_RECEIVER): [kotlin/String.length]>#"];
|
||||
278 [label="Access variable R|<local>/y|"];
|
||||
279 [label="Access variable R|kotlin/String.length|"];
|
||||
280 [label="Access variable R|<local>/z|"];
|
||||
281 [label="Access variable <Inapplicable(WRONG_RECEIVER): [kotlin/String.length]>#"];
|
||||
282 [label="Exit block"];
|
||||
}
|
||||
283 [label="Exit when branch result"];
|
||||
284 [label="Exit when"];
|
||||
}
|
||||
subgraph cluster_58 {
|
||||
color=blue
|
||||
285 [label="Enter when"];
|
||||
subgraph cluster_59 {
|
||||
color=blue
|
||||
286 [label="Enter when branch condition "];
|
||||
287 [label="Access variable R|<local>/z|"];
|
||||
288 [label="Const: Null(null)"];
|
||||
289 [label="Operator !="];
|
||||
290 [label="Exit when branch condition"];
|
||||
}
|
||||
291 [label="Synthetic else branch"];
|
||||
292 [label="Enter when branch result"];
|
||||
subgraph cluster_60 {
|
||||
color=blue
|
||||
293 [label="Enter block"];
|
||||
294 [label="Access variable R|<local>/x|"];
|
||||
295 [label="Access variable R|kotlin/String.length|"];
|
||||
296 [label="Access variable R|<local>/y|"];
|
||||
297 [label="Access variable <Inapplicable(WRONG_RECEIVER): [kotlin/String.length]>#"];
|
||||
298 [label="Access variable R|<local>/z|"];
|
||||
299 [label="Access variable R|kotlin/String.length|"];
|
||||
300 [label="Exit block"];
|
||||
}
|
||||
301 [label="Exit when branch result"];
|
||||
302 [label="Exit when"];
|
||||
}
|
||||
303 [label="Exit function test_7" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
186 -> {187};
|
||||
187 -> {188};
|
||||
188 -> {189};
|
||||
189 -> {190};
|
||||
190 -> {191};
|
||||
191 -> {192};
|
||||
192 -> {193};
|
||||
193 -> {194};
|
||||
194 -> {195};
|
||||
195 -> {196};
|
||||
196 -> {197};
|
||||
197 -> {198};
|
||||
198 -> {200 199};
|
||||
199 -> {210};
|
||||
200 -> {201};
|
||||
201 -> {202};
|
||||
202 -> {203};
|
||||
203 -> {204};
|
||||
204 -> {205};
|
||||
205 -> {206};
|
||||
206 -> {207};
|
||||
207 -> {208};
|
||||
208 -> {209};
|
||||
209 -> {210};
|
||||
210 -> {211};
|
||||
211 -> {212};
|
||||
212 -> {213};
|
||||
213 -> {214};
|
||||
214 -> {215};
|
||||
215 -> {216};
|
||||
216 -> {218 217};
|
||||
217 -> {228};
|
||||
218 -> {219};
|
||||
219 -> {220};
|
||||
220 -> {221};
|
||||
221 -> {222};
|
||||
222 -> {223};
|
||||
223 -> {224};
|
||||
224 -> {225};
|
||||
225 -> {226};
|
||||
226 -> {227};
|
||||
227 -> {228};
|
||||
228 -> {229};
|
||||
229 -> {230};
|
||||
230 -> {231};
|
||||
231 -> {232};
|
||||
232 -> {233};
|
||||
233 -> {234};
|
||||
234 -> {236 235};
|
||||
235 -> {246};
|
||||
236 -> {237};
|
||||
237 -> {238};
|
||||
238 -> {239};
|
||||
239 -> {240};
|
||||
240 -> {241};
|
||||
241 -> {242};
|
||||
242 -> {243};
|
||||
243 -> {244};
|
||||
244 -> {245};
|
||||
245 -> {246};
|
||||
246 -> {247};
|
||||
247 -> {248};
|
||||
248 -> {249};
|
||||
249 -> {250};
|
||||
250 -> {251};
|
||||
251 -> {252};
|
||||
252 -> {253};
|
||||
253 -> {254};
|
||||
254 -> {256 255};
|
||||
255 -> {266};
|
||||
256 -> {257};
|
||||
257 -> {258};
|
||||
258 -> {259};
|
||||
259 -> {260};
|
||||
260 -> {261};
|
||||
261 -> {262};
|
||||
262 -> {263};
|
||||
263 -> {264};
|
||||
264 -> {265};
|
||||
265 -> {266};
|
||||
266 -> {267};
|
||||
267 -> {268};
|
||||
268 -> {269};
|
||||
269 -> {270};
|
||||
270 -> {271};
|
||||
271 -> {272};
|
||||
272 -> {274 273};
|
||||
273 -> {284};
|
||||
274 -> {275};
|
||||
275 -> {276};
|
||||
276 -> {277};
|
||||
277 -> {278};
|
||||
278 -> {279};
|
||||
279 -> {280};
|
||||
280 -> {281};
|
||||
281 -> {282};
|
||||
282 -> {283};
|
||||
283 -> {284};
|
||||
284 -> {285};
|
||||
285 -> {286};
|
||||
286 -> {287};
|
||||
287 -> {288};
|
||||
288 -> {289};
|
||||
289 -> {290};
|
||||
290 -> {292 291};
|
||||
291 -> {302};
|
||||
292 -> {293};
|
||||
293 -> {294};
|
||||
294 -> {295};
|
||||
295 -> {296};
|
||||
296 -> {297};
|
||||
297 -> {298};
|
||||
298 -> {299};
|
||||
299 -> {300};
|
||||
300 -> {301};
|
||||
301 -> {302};
|
||||
302 -> {303};
|
||||
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
class A {
|
||||
val s: String = ""
|
||||
}
|
||||
|
||||
fun test_0(list: List<A>) {
|
||||
var goodA: A? = null
|
||||
for (a in list) {
|
||||
if (goodA == null) {
|
||||
goodA = a
|
||||
continue
|
||||
}
|
||||
goodA.s
|
||||
}
|
||||
}
|
||||
|
||||
fun test_1(a: A, b: Boolean) {
|
||||
val x: Any
|
||||
if (b) {
|
||||
x = A()
|
||||
} else {
|
||||
x = a
|
||||
}
|
||||
x.s
|
||||
}
|
||||
|
||||
fun test_2(a: Any, b: Boolean) {
|
||||
val x: Any
|
||||
if (b) {
|
||||
//x1
|
||||
x = A()
|
||||
} else {
|
||||
//x2 = a
|
||||
x = a
|
||||
a as A
|
||||
}
|
||||
x.s
|
||||
}
|
||||
|
||||
fun test_3(a: Any, b: Boolean) {
|
||||
val x: Any
|
||||
if (b) {
|
||||
x = A()
|
||||
} else {
|
||||
a as A
|
||||
x = a
|
||||
}
|
||||
x.s
|
||||
}
|
||||
|
||||
fun test_4(a: Any, b: Boolean) {
|
||||
val x: Any
|
||||
if (b) {
|
||||
x = a
|
||||
} else {
|
||||
x = a
|
||||
}
|
||||
x as A
|
||||
x.s
|
||||
a.s
|
||||
}
|
||||
|
||||
fun test_5(a: Any, b: Boolean) {
|
||||
val x: Any
|
||||
if (b) {
|
||||
x = a
|
||||
} else {
|
||||
x = a
|
||||
}
|
||||
a as A
|
||||
x.s
|
||||
a.s
|
||||
}
|
||||
|
||||
fun test_6(a: A) {
|
||||
val x: Any
|
||||
x = a
|
||||
x.s
|
||||
}
|
||||
|
||||
fun test_7() {
|
||||
val z: String? = null
|
||||
var y : String? = z
|
||||
val x: String? = y
|
||||
|
||||
if (x != null) {
|
||||
x.length // OK
|
||||
y.length // OK
|
||||
z.length // OK
|
||||
}
|
||||
if (y != null) {
|
||||
x.length // OK
|
||||
y.length // OK
|
||||
z.length // OK
|
||||
}
|
||||
|
||||
if (z != null) {
|
||||
x.length // OK
|
||||
y.length // OK
|
||||
z.length // OK
|
||||
}
|
||||
|
||||
y = null
|
||||
|
||||
if (x != null) {
|
||||
x.length // OK
|
||||
y.<!INAPPLICABLE_CANDIDATE!>length<!> // Bad
|
||||
z.length // OK
|
||||
}
|
||||
if (y != null) {
|
||||
x.<!INAPPLICABLE_CANDIDATE!>length<!> // Bad
|
||||
y.length // OK
|
||||
z.<!INAPPLICABLE_CANDIDATE!>length<!> // Bad
|
||||
}
|
||||
|
||||
if (z != null) {
|
||||
x.length // OK
|
||||
y.<!INAPPLICABLE_CANDIDATE!>length<!> // Bad
|
||||
z.length // OK
|
||||
}
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
FILE: noBoundSmartcastAfterContinue.kt
|
||||
public final class A : R|kotlin/Any| {
|
||||
public constructor(): R|A| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final val s: R|kotlin/String| = String()
|
||||
public get(): R|kotlin/String|
|
||||
|
||||
}
|
||||
public final fun test_0(list: R|kotlin/collections/List<A>|): R|kotlin/Unit| {
|
||||
lvar goodA: R|A?| = Null(null)
|
||||
lval <range>: R|kotlin/collections/List<A>| = R|<local>/list|
|
||||
lval <iterator>: R|kotlin/collections/Iterator<A>| = R|<local>/<range>|.R|FakeOverride<kotlin/collections/List.iterator: R|kotlin/collections/Iterator<A>|>|()
|
||||
while(R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()) {
|
||||
lval a: R|A| = R|<local>/<iterator>|.R|FakeOverride<kotlin/collections/Iterator.next: R|A|>|()
|
||||
when () {
|
||||
==(R|<local>/goodA|, Null(null)) -> {
|
||||
R|<local>/goodA| = R|<local>/a|
|
||||
continue@@@[R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()]
|
||||
}
|
||||
}
|
||||
|
||||
R|<local>/goodA|.R|/A.s|
|
||||
}
|
||||
|
||||
}
|
||||
public final fun test_1(a: R|A|, b: R|kotlin/Boolean|): R|kotlin/Unit| {
|
||||
lval x: R|kotlin/Any|
|
||||
when () {
|
||||
R|<local>/b| -> {
|
||||
R|<local>/x| = R|/A.A|()
|
||||
}
|
||||
else -> {
|
||||
R|<local>/x| = R|<local>/a|
|
||||
}
|
||||
}
|
||||
|
||||
R|<local>/x|.R|/A.s|
|
||||
}
|
||||
public final fun test_2(a: R|kotlin/Any|, b: R|kotlin/Boolean|): R|kotlin/Unit| {
|
||||
lval x: R|kotlin/Any|
|
||||
when () {
|
||||
R|<local>/b| -> {
|
||||
R|<local>/x| = R|/A.A|()
|
||||
}
|
||||
else -> {
|
||||
R|<local>/x| = R|<local>/a|
|
||||
(R|<local>/a| as R|A|)
|
||||
}
|
||||
}
|
||||
|
||||
R|<local>/x|.R|/A.s|
|
||||
}
|
||||
public final fun test_3(a: R|kotlin/Any|, b: R|kotlin/Boolean|): R|kotlin/Unit| {
|
||||
lval x: R|kotlin/Any|
|
||||
when () {
|
||||
R|<local>/b| -> {
|
||||
R|<local>/x| = R|/A.A|()
|
||||
}
|
||||
else -> {
|
||||
(R|<local>/a| as R|A|)
|
||||
R|<local>/x| = R|<local>/a|
|
||||
}
|
||||
}
|
||||
|
||||
R|<local>/x|.R|/A.s|
|
||||
}
|
||||
public final fun test_4(a: R|kotlin/Any|, b: R|kotlin/Boolean|): R|kotlin/Unit| {
|
||||
lval x: R|kotlin/Any|
|
||||
when () {
|
||||
R|<local>/b| -> {
|
||||
R|<local>/x| = R|<local>/a|
|
||||
}
|
||||
else -> {
|
||||
R|<local>/x| = R|<local>/a|
|
||||
}
|
||||
}
|
||||
|
||||
(R|<local>/x| as R|A|)
|
||||
R|<local>/x|.R|/A.s|
|
||||
R|<local>/a|.R|/A.s|
|
||||
}
|
||||
public final fun test_5(a: R|kotlin/Any|, b: R|kotlin/Boolean|): R|kotlin/Unit| {
|
||||
lval x: R|kotlin/Any|
|
||||
when () {
|
||||
R|<local>/b| -> {
|
||||
R|<local>/x| = R|<local>/a|
|
||||
}
|
||||
else -> {
|
||||
R|<local>/x| = R|<local>/a|
|
||||
}
|
||||
}
|
||||
|
||||
(R|<local>/a| as R|A|)
|
||||
R|<local>/x|.R|/A.s|
|
||||
R|<local>/a|.R|/A.s|
|
||||
}
|
||||
public final fun test_6(a: R|A|): R|kotlin/Unit| {
|
||||
lval x: R|kotlin/Any|
|
||||
R|<local>/x| = R|<local>/a|
|
||||
R|<local>/x|.R|/A.s|
|
||||
}
|
||||
public final fun test_7(): R|kotlin/Unit| {
|
||||
lval z: R|kotlin/String?| = Null(null)
|
||||
lvar y: R|kotlin/String?| = R|<local>/z|
|
||||
lval x: R|kotlin/String?| = R|<local>/y|
|
||||
when () {
|
||||
!=(R|<local>/x|, Null(null)) -> {
|
||||
R|<local>/x|.R|kotlin/String.length|
|
||||
R|<local>/y|.R|kotlin/String.length|
|
||||
R|<local>/z|.R|kotlin/String.length|
|
||||
}
|
||||
}
|
||||
|
||||
when () {
|
||||
!=(R|<local>/y|, Null(null)) -> {
|
||||
R|<local>/x|.R|kotlin/String.length|
|
||||
R|<local>/y|.R|kotlin/String.length|
|
||||
R|<local>/z|.R|kotlin/String.length|
|
||||
}
|
||||
}
|
||||
|
||||
when () {
|
||||
!=(R|<local>/z|, Null(null)) -> {
|
||||
R|<local>/x|.R|kotlin/String.length|
|
||||
R|<local>/y|.R|kotlin/String.length|
|
||||
R|<local>/z|.R|kotlin/String.length|
|
||||
}
|
||||
}
|
||||
|
||||
R|<local>/y| = Null(null)
|
||||
when () {
|
||||
!=(R|<local>/x|, Null(null)) -> {
|
||||
R|<local>/x|.R|kotlin/String.length|
|
||||
R|<local>/y|.<Inapplicable(WRONG_RECEIVER): [kotlin/String.length]>#
|
||||
R|<local>/z|.R|kotlin/String.length|
|
||||
}
|
||||
}
|
||||
|
||||
when () {
|
||||
!=(R|<local>/y|, Null(null)) -> {
|
||||
R|<local>/x|.<Inapplicable(WRONG_RECEIVER): [kotlin/String.length]>#
|
||||
R|<local>/y|.R|kotlin/String.length|
|
||||
R|<local>/z|.<Inapplicable(WRONG_RECEIVER): [kotlin/String.length]>#
|
||||
}
|
||||
}
|
||||
|
||||
when () {
|
||||
!=(R|<local>/z|, Null(null)) -> {
|
||||
R|<local>/x|.R|kotlin/String.length|
|
||||
R|<local>/y|.<Inapplicable(WRONG_RECEIVER): [kotlin/String.length]>#
|
||||
R|<local>/z|.R|kotlin/String.length|
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -499,11 +499,11 @@ digraph nullability_kt {
|
||||
172 [label="Access variable R|/Q.data|"];
|
||||
173 [label="Access variable R|<local>/q|"];
|
||||
174 [label="Access variable R|/Q.data|"];
|
||||
175 [label="Access variable R|/MyData.s|"];
|
||||
175 [label="Access variable <Inapplicable(WRONG_RECEIVER): [/MyData.s]>#"];
|
||||
176 [label="Access variable R|<local>/q|"];
|
||||
177 [label="Access variable R|/Q.data|"];
|
||||
178 [label="Access variable R|/MyData.s|"];
|
||||
179 [label="Function call: R|<local>/q|.R|/Q.data|.R|/MyData.s|.R|kotlin/Int.inc|()"];
|
||||
178 [label="Access variable <Inapplicable(WRONG_RECEIVER): [/MyData.s]>#"];
|
||||
179 [label="Function call: R|<local>/q|.R|/Q.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()"];
|
||||
180 [label="Exit block"];
|
||||
}
|
||||
181 [label="Exit when branch result"];
|
||||
@@ -596,11 +596,11 @@ digraph nullability_kt {
|
||||
216 [label="Access variable R|/Q.data|"];
|
||||
217 [label="Access variable R|<local>/q|"];
|
||||
218 [label="Access variable R|/Q.data|"];
|
||||
219 [label="Access variable R|/MyData.s|"];
|
||||
219 [label="Access variable <Inapplicable(WRONG_RECEIVER): [/MyData.s]>#"];
|
||||
220 [label="Access variable R|<local>/q|"];
|
||||
221 [label="Access variable R|/Q.data|"];
|
||||
222 [label="Access variable R|/MyData.s|"];
|
||||
223 [label="Function call: R|<local>/q|.R|/Q.data|.R|/MyData.s|.R|kotlin/Int.inc|()"];
|
||||
222 [label="Access variable <Inapplicable(WRONG_RECEIVER): [/MyData.s]>#"];
|
||||
223 [label="Function call: R|<local>/q|.R|/Q.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()"];
|
||||
224 [label="Exit function test_6" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
@@ -1288,11 +1288,11 @@ digraph nullability_kt {
|
||||
486 [label="Access variable R|/QImplWithCustomGetter.data|"];
|
||||
487 [label="Access variable R|<local>/q|"];
|
||||
488 [label="Access variable R|/QImplWithCustomGetter.data|"];
|
||||
489 [label="Access variable R|/MyData.s|"];
|
||||
489 [label="Access variable <Inapplicable(WRONG_RECEIVER): [/MyData.s]>#"];
|
||||
490 [label="Access variable R|<local>/q|"];
|
||||
491 [label="Access variable R|/QImplWithCustomGetter.data|"];
|
||||
492 [label="Access variable R|/MyData.s|"];
|
||||
493 [label="Function call: R|<local>/q|.R|/QImplWithCustomGetter.data|.R|/MyData.s|.R|kotlin/Int.inc|()"];
|
||||
492 [label="Access variable <Inapplicable(WRONG_RECEIVER): [/MyData.s]>#"];
|
||||
493 [label="Function call: R|<local>/q|.R|/QImplWithCustomGetter.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()"];
|
||||
494 [label="Exit block"];
|
||||
}
|
||||
495 [label="Exit when branch result"];
|
||||
@@ -1365,11 +1365,11 @@ digraph nullability_kt {
|
||||
518 [label="Access variable R|/QImplMutable.data|"];
|
||||
519 [label="Access variable R|<local>/q|"];
|
||||
520 [label="Access variable R|/QImplMutable.data|"];
|
||||
521 [label="Access variable R|/MyData.s|"];
|
||||
521 [label="Access variable <Inapplicable(WRONG_RECEIVER): [/MyData.s]>#"];
|
||||
522 [label="Access variable R|<local>/q|"];
|
||||
523 [label="Access variable R|/QImplMutable.data|"];
|
||||
524 [label="Access variable R|/MyData.s|"];
|
||||
525 [label="Function call: R|<local>/q|.R|/QImplMutable.data|.R|/MyData.s|.R|kotlin/Int.inc|()"];
|
||||
524 [label="Access variable <Inapplicable(WRONG_RECEIVER): [/MyData.s]>#"];
|
||||
525 [label="Function call: R|<local>/q|.R|/QImplMutable.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()"];
|
||||
526 [label="Exit block"];
|
||||
}
|
||||
527 [label="Exit when branch result"];
|
||||
|
||||
@@ -62,23 +62,21 @@ fun test_4(x: A?) {
|
||||
x.foo()
|
||||
}
|
||||
|
||||
// TODO: Fix this -- see comment in FirDataFlowAnalyzer.getRealVariablesForSafeCallChain()
|
||||
fun test_5(q: Q?) {
|
||||
// `q.data` is a property that has an open getter, so we can NOT smartcast it to non-nullable MyData.
|
||||
if (q?.data?.s?.inc() != null) {
|
||||
q.data // good
|
||||
q.data.s // should be bad
|
||||
q.data.s.inc() // should be bad
|
||||
q.data.<!INAPPLICABLE_CANDIDATE!>s<!> // should be bad
|
||||
q.data.<!INAPPLICABLE_CANDIDATE!>s<!>.<!AMBIGUITY!>inc<!>() // should be bad
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Fix this -- see comment in FirDataFlowAnalyzer.getRealVariablesForSafeCallChain()
|
||||
fun test_6(q: Q?) {
|
||||
// `q.data` is a property that has an open getter, so we can NOT smartcast it to non-nullable MyData.
|
||||
q?.data?.s?.inc() ?: return
|
||||
q.data // good
|
||||
q.data.s // should be bad
|
||||
q.data.s.inc() // should be bad
|
||||
q.data.<!INAPPLICABLE_CANDIDATE!>s<!> // should be bad
|
||||
q.data.<!INAPPLICABLE_CANDIDATE!>s<!>.<!AMBIGUITY!>inc<!>() // should be bad
|
||||
}
|
||||
|
||||
fun test_7(q: Q?) {
|
||||
@@ -139,7 +137,6 @@ fun test_10(a: Int?, b: Int?) {
|
||||
b.<!AMBIGUITY!>inc<!>()
|
||||
}
|
||||
|
||||
// TODO: Fix this -- see comment in FirDataFlowAnalyzer.getRealVariablesForSafeCallChain()
|
||||
fun test_11(q: QImpl?, q2: QImpl) {
|
||||
// `q.data` is a property with the default getter, so we CAN smartcast it to non-nullable MyData.
|
||||
if (q?.data?.s?.inc() != null) {
|
||||
@@ -160,22 +157,20 @@ fun test_11(q: QImpl?, q2: QImpl) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Fix this -- see comment in FirDataFlowAnalyzer.getRealVariablesForSafeCallChain()
|
||||
fun test_12(q: QImplWithCustomGetter?) {
|
||||
// `q.data` is a property that has an open getter, so we can NOT smartcast it to non-nullable MyData.
|
||||
if (q?.data?.s?.inc() != null) {
|
||||
q.data // good
|
||||
q.data.s // should be bad
|
||||
q.data.s.inc() // should be bad
|
||||
q.data.<!INAPPLICABLE_CANDIDATE!>s<!> // should be bad
|
||||
q.data.<!INAPPLICABLE_CANDIDATE!>s<!>.<!AMBIGUITY!>inc<!>() // should be bad
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Fix this -- see comment in FirDataFlowAnalyzer.getRealVariablesForSafeCallChain()
|
||||
fun test_13(q: QImplMutable?) {
|
||||
// `q.data` is a property that is mutable, so we can NOT smartcast it to non-nullable MyData.
|
||||
if (q?.data?.s?.inc() != null) {
|
||||
q.data // good
|
||||
q.data.s // should be bad
|
||||
q.data.s.inc() // should be bad
|
||||
q.data.<!INAPPLICABLE_CANDIDATE!>s<!> // should be bad
|
||||
q.data.<!INAPPLICABLE_CANDIDATE!>s<!>.<!AMBIGUITY!>inc<!>() // should be bad
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,8 +110,8 @@ FILE: nullability.kt
|
||||
when () {
|
||||
!=(R|<local>/q|?.R|/Q.data|?.R|/MyData.s|?.R|kotlin/Int.inc|(), Null(null)) -> {
|
||||
R|<local>/q|.R|/Q.data|
|
||||
R|<local>/q|.R|/Q.data|.R|/MyData.s|
|
||||
R|<local>/q|.R|/Q.data|.R|/MyData.s|.R|kotlin/Int.inc|()
|
||||
R|<local>/q|.R|/Q.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#
|
||||
R|<local>/q|.R|/Q.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,8 +127,8 @@ FILE: nullability.kt
|
||||
}
|
||||
|
||||
R|<local>/q|.R|/Q.data|
|
||||
R|<local>/q|.R|/Q.data|.R|/MyData.s|
|
||||
R|<local>/q|.R|/Q.data|.R|/MyData.s|.R|kotlin/Int.inc|()
|
||||
R|<local>/q|.R|/Q.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#
|
||||
R|<local>/q|.R|/Q.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()
|
||||
}
|
||||
public final fun test_7(q: R|Q?|): R|kotlin/Unit| {
|
||||
when () {
|
||||
@@ -232,8 +232,8 @@ FILE: nullability.kt
|
||||
when () {
|
||||
!=(R|<local>/q|?.R|/QImplWithCustomGetter.data|?.R|/MyData.s|?.R|kotlin/Int.inc|(), Null(null)) -> {
|
||||
R|<local>/q|.R|/QImplWithCustomGetter.data|
|
||||
R|<local>/q|.R|/QImplWithCustomGetter.data|.R|/MyData.s|
|
||||
R|<local>/q|.R|/QImplWithCustomGetter.data|.R|/MyData.s|.R|kotlin/Int.inc|()
|
||||
R|<local>/q|.R|/QImplWithCustomGetter.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#
|
||||
R|<local>/q|.R|/QImplWithCustomGetter.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,8 +242,8 @@ FILE: nullability.kt
|
||||
when () {
|
||||
!=(R|<local>/q|?.R|/QImplMutable.data|?.R|/MyData.s|?.R|kotlin/Int.inc|(), Null(null)) -> {
|
||||
R|<local>/q|.R|/QImplMutable.data|
|
||||
R|<local>/q|.R|/QImplMutable.data|.R|/MyData.s|
|
||||
R|<local>/q|.R|/QImplMutable.data|.R|/MyData.s|.R|kotlin/Int.inc|()
|
||||
R|<local>/q|.R|/QImplMutable.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#
|
||||
R|<local>/q|.R|/QImplMutable.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
digraph overridenOpenVal_kt {
|
||||
graph [splines=ortho nodesep=3]
|
||||
node [shape=box penwidth=2]
|
||||
edge [penwidth=2]
|
||||
|
||||
subgraph cluster_0 {
|
||||
color=red
|
||||
0 [label="Enter function <init>" style="filled" fillcolor=red];
|
||||
1 [label="Exit function <init>" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
0 -> {1};
|
||||
|
||||
subgraph cluster_1 {
|
||||
color=red
|
||||
2 [label="Enter function getter" style="filled" fillcolor=red];
|
||||
3 [label="Exit function getter" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
2 -> {3};
|
||||
|
||||
subgraph cluster_2 {
|
||||
color=red
|
||||
4 [label="Enter property" style="filled" fillcolor=red];
|
||||
5 [label="Access variable R|<local>/x|"];
|
||||
6 [label="Exit property" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
4 -> {5};
|
||||
5 -> {6};
|
||||
|
||||
subgraph cluster_3 {
|
||||
color=red
|
||||
7 [label="Enter function <init>" style="filled" fillcolor=red];
|
||||
8 [label="Access variable R|<local>/x|"];
|
||||
9 [label="Exit function <init>" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
7 -> {8};
|
||||
8 -> {9};
|
||||
|
||||
subgraph cluster_4 {
|
||||
color=red
|
||||
10 [label="Enter function test_1" style="filled" fillcolor=red];
|
||||
subgraph cluster_5 {
|
||||
color=blue
|
||||
11 [label="Enter when"];
|
||||
subgraph cluster_6 {
|
||||
color=blue
|
||||
12 [label="Enter when branch condition "];
|
||||
13 [label="Access variable R|/A.x|"];
|
||||
14 [label="Type operator: x is String"];
|
||||
15 [label="Exit when branch condition"];
|
||||
}
|
||||
16 [label="Synthetic else branch"];
|
||||
17 [label="Enter when branch result"];
|
||||
subgraph cluster_7 {
|
||||
color=blue
|
||||
18 [label="Enter block"];
|
||||
19 [label="Access variable R|/A.x|"];
|
||||
20 [label="Access variable R|kotlin/String.length|"];
|
||||
21 [label="Exit block"];
|
||||
}
|
||||
22 [label="Exit when branch result"];
|
||||
23 [label="Exit when"];
|
||||
}
|
||||
24 [label="Exit function test_1" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
10 -> {11};
|
||||
11 -> {12};
|
||||
12 -> {13};
|
||||
13 -> {14};
|
||||
14 -> {15};
|
||||
15 -> {17 16};
|
||||
16 -> {23};
|
||||
17 -> {18};
|
||||
18 -> {19};
|
||||
19 -> {20};
|
||||
20 -> {21};
|
||||
21 -> {22};
|
||||
22 -> {23};
|
||||
23 -> {24};
|
||||
|
||||
subgraph cluster_8 {
|
||||
color=red
|
||||
25 [label="Enter function test_2" style="filled" fillcolor=red];
|
||||
subgraph cluster_9 {
|
||||
color=blue
|
||||
26 [label="Enter when"];
|
||||
subgraph cluster_10 {
|
||||
color=blue
|
||||
27 [label="Enter when branch condition "];
|
||||
28 [label="Access variable R|<local>/b|"];
|
||||
29 [label="Access variable R|/A.x|"];
|
||||
30 [label="Type operator: b.x is String"];
|
||||
31 [label="Exit when branch condition"];
|
||||
}
|
||||
32 [label="Synthetic else branch"];
|
||||
33 [label="Enter when branch result"];
|
||||
subgraph cluster_11 {
|
||||
color=blue
|
||||
34 [label="Enter block"];
|
||||
35 [label="Access variable R|<local>/b|"];
|
||||
36 [label="Access variable R|/A.x|"];
|
||||
37 [label="Access variable R|kotlin/String.length|"];
|
||||
38 [label="Exit block"];
|
||||
}
|
||||
39 [label="Exit when branch result"];
|
||||
40 [label="Exit when"];
|
||||
}
|
||||
41 [label="Exit function test_2" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
25 -> {26};
|
||||
26 -> {27};
|
||||
27 -> {28};
|
||||
28 -> {29};
|
||||
29 -> {30};
|
||||
30 -> {31};
|
||||
31 -> {33 32};
|
||||
32 -> {40};
|
||||
33 -> {34};
|
||||
34 -> {35};
|
||||
35 -> {36};
|
||||
36 -> {37};
|
||||
37 -> {38};
|
||||
38 -> {39};
|
||||
39 -> {40};
|
||||
40 -> {41};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
open class A(open val x: Any)
|
||||
|
||||
class B(x: Any) : A(x) {
|
||||
fun test_1() {
|
||||
if (x is String) {
|
||||
x.length
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun test_2(b: B) {
|
||||
if (b.x is String) {
|
||||
b.x.length
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
FILE: overridenOpenVal.kt
|
||||
public open class A : R|kotlin/Any| {
|
||||
public constructor(x: R|kotlin/Any|): R|A| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public open val x: R|kotlin/Any| = R|<local>/x|
|
||||
public get(): R|kotlin/Any|
|
||||
|
||||
}
|
||||
public final class B : R|A| {
|
||||
public constructor(x: R|kotlin/Any|): R|B| {
|
||||
super<R|A|>(R|<local>/x|)
|
||||
}
|
||||
|
||||
public final fun test_1(): R|kotlin/Unit| {
|
||||
when () {
|
||||
(this@R|/B|.R|/A.x| is R|kotlin/String|) -> {
|
||||
this@R|/B|.R|/A.x|.R|kotlin/String.length|
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
public final fun test_2(b: R|B|): R|kotlin/Unit| {
|
||||
when () {
|
||||
(R|<local>/b|.R|/A.x| is R|kotlin/String|) -> {
|
||||
R|<local>/b|.R|/A.x|.R|kotlin/String.length|
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -88,7 +88,7 @@ digraph smartcastAfterReassignment_kt {
|
||||
35 [label="Const: Null(null)"];
|
||||
36 [label="Assignmenet: R|<local>/x|"];
|
||||
37 [label="Access variable R|<local>/x|"];
|
||||
38 [label="Access variable <Unresolved name: length>#"];
|
||||
38 [label="Access variable <Inapplicable(WRONG_RECEIVER): [kotlin/String.length]>#"];
|
||||
39 [label="Exit function test_3" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -17,5 +17,5 @@ fun test_3() {
|
||||
x = ""
|
||||
x.length
|
||||
x = null
|
||||
x.<!UNRESOLVED_REFERENCE!>length<!>
|
||||
x.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
}
|
||||
+1
-1
@@ -19,5 +19,5 @@ FILE: smartcastAfterReassignment.kt
|
||||
R|<local>/x| = String()
|
||||
R|<local>/x|.R|kotlin/String.length|
|
||||
R|<local>/x| = Null(null)
|
||||
R|<local>/x|.<Unresolved name: length>#
|
||||
R|<local>/x|.<Inapplicable(WRONG_RECEIVER): [kotlin/String.length]>#
|
||||
}
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
digraph smartcastToNothing_kt {
|
||||
graph [splines=ortho nodesep=3]
|
||||
node [shape=box penwidth=2]
|
||||
edge [penwidth=2]
|
||||
|
||||
subgraph cluster_0 {
|
||||
color=red
|
||||
0 [label="Enter function getNothing" style="filled" fillcolor=red];
|
||||
1 [label="Function call: R|java/lang/Exception.Exception|()"];
|
||||
2 [label="Throw: throw R|java/lang/Exception.Exception|()"];
|
||||
3 [label="Stub" style="filled" fillcolor=gray];
|
||||
4 [label="Jump: ^getNothing throw R|java/lang/Exception.Exception|()" style="filled" fillcolor=gray];
|
||||
5 [label="Stub" style="filled" fillcolor=gray];
|
||||
6 [label="Exit function getNothing" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
0 -> {1};
|
||||
1 -> {2};
|
||||
2 -> {6};
|
||||
2 -> {3} [style=dotted];
|
||||
3 -> {4} [style=dotted];
|
||||
4 -> {6 5} [style=dotted];
|
||||
5 -> {6} [style=dotted];
|
||||
|
||||
subgraph cluster_1 {
|
||||
color=red
|
||||
7 [label="Enter function getNullableNothing" style="filled" fillcolor=red];
|
||||
8 [label="Const: Null(null)"];
|
||||
9 [label="Jump: ^getNullableNothing Null(null)"];
|
||||
10 [label="Stub" style="filled" fillcolor=gray];
|
||||
11 [label="Exit function getNullableNothing" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
7 -> {8};
|
||||
8 -> {9};
|
||||
9 -> {11};
|
||||
9 -> {10} [style=dotted];
|
||||
10 -> {11} [style=dotted];
|
||||
|
||||
subgraph cluster_2 {
|
||||
color=red
|
||||
12 [label="Enter function getter" style="filled" fillcolor=red];
|
||||
13 [label="Const: Int(1)"];
|
||||
14 [label="Jump: ^ Int(1)"];
|
||||
15 [label="Stub" style="filled" fillcolor=gray];
|
||||
16 [label="Exit function getter" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
12 -> {13};
|
||||
13 -> {14};
|
||||
14 -> {16};
|
||||
14 -> {15} [style=dotted];
|
||||
15 -> {16} [style=dotted];
|
||||
|
||||
subgraph cluster_3 {
|
||||
color=red
|
||||
17 [label="Enter property" style="filled" fillcolor=red];
|
||||
18 [label="Exit property" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
17 -> {18};
|
||||
|
||||
subgraph cluster_4 {
|
||||
color=red
|
||||
19 [label="Enter function getter" style="filled" fillcolor=red];
|
||||
20 [label="Const: Int(2)"];
|
||||
21 [label="Jump: ^ Int(2)"];
|
||||
22 [label="Stub" style="filled" fillcolor=gray];
|
||||
23 [label="Exit function getter" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
19 -> {20};
|
||||
20 -> {21};
|
||||
21 -> {23};
|
||||
21 -> {22} [style=dotted];
|
||||
22 -> {23} [style=dotted];
|
||||
|
||||
subgraph cluster_5 {
|
||||
color=red
|
||||
24 [label="Enter property" style="filled" fillcolor=red];
|
||||
25 [label="Exit property" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
24 -> {25};
|
||||
|
||||
subgraph cluster_6 {
|
||||
color=red
|
||||
26 [label="Enter function myListOf" style="filled" fillcolor=red];
|
||||
27 [label="Const: Null(null)"];
|
||||
28 [label="Check not null: Null(null)!!"];
|
||||
29 [label="Jump: ^myListOf Null(null)!!"];
|
||||
30 [label="Stub" style="filled" fillcolor=gray];
|
||||
31 [label="Exit function myListOf" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
26 -> {27};
|
||||
27 -> {28};
|
||||
28 -> {29};
|
||||
29 -> {31};
|
||||
29 -> {30} [style=dotted];
|
||||
30 -> {31} [style=dotted];
|
||||
|
||||
subgraph cluster_7 {
|
||||
color=red
|
||||
32 [label="Enter function <init>" style="filled" fillcolor=red];
|
||||
33 [label="Exit function <init>" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
32 -> {33};
|
||||
|
||||
subgraph cluster_8 {
|
||||
color=red
|
||||
34 [label="Enter function getter" style="filled" fillcolor=red];
|
||||
35 [label="Exit function getter" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
34 -> {35};
|
||||
|
||||
subgraph cluster_9 {
|
||||
color=red
|
||||
36 [label="Enter property" style="filled" fillcolor=red];
|
||||
37 [label="Const: Int(1)"];
|
||||
38 [label="Exit property" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
36 -> {37};
|
||||
37 -> {38};
|
||||
|
||||
subgraph cluster_10 {
|
||||
color=red
|
||||
39 [label="Enter function getter" style="filled" fillcolor=red];
|
||||
40 [label="Exit function getter" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
39 -> {40};
|
||||
|
||||
subgraph cluster_11 {
|
||||
color=red
|
||||
41 [label="Enter property" style="filled" fillcolor=red];
|
||||
42 [label="Const: Boolean(true)"];
|
||||
43 [label="Exit property" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
41 -> {42};
|
||||
42 -> {43};
|
||||
|
||||
subgraph cluster_12 {
|
||||
color=red
|
||||
44 [label="Enter function test_0" style="filled" fillcolor=red];
|
||||
45 [label="Const: Null(null)"];
|
||||
46 [label="Variable declaration: lvar s: R|A?|"];
|
||||
47 [label="Access variable R|<local>/results|"];
|
||||
48 [label="Variable declaration: lval <range>: R|kotlin/collections/List<kotlin/Nothing>|"];
|
||||
49 [label="Access variable R|<local>/<range>|"];
|
||||
50 [label="Function call: R|<local>/<range>|.R|FakeOverride<kotlin/collections/List.iterator: R|kotlin/collections/Iterator<kotlin/Nothing>|>|()"];
|
||||
51 [label="Variable declaration: lval <iterator>: R|kotlin/collections/Iterator<kotlin/Nothing>|"];
|
||||
subgraph cluster_13 {
|
||||
color=blue
|
||||
52 [label="Enter while loop"];
|
||||
subgraph cluster_14 {
|
||||
color=blue
|
||||
53 [label="Enter loop condition"];
|
||||
54 [label="Access variable R|<local>/<iterator>|"];
|
||||
55 [label="Function call: R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()"];
|
||||
56 [label="Exit loop condition"];
|
||||
}
|
||||
subgraph cluster_15 {
|
||||
color=blue
|
||||
57 [label="Enter loop block"];
|
||||
subgraph cluster_16 {
|
||||
color=blue
|
||||
58 [label="Enter block"];
|
||||
59 [label="Access variable R|<local>/<iterator>|"];
|
||||
60 [label="Function call: R|<local>/<iterator>|.R|FakeOverride<kotlin/collections/Iterator.next: R|kotlin/Nothing|>|()"];
|
||||
61 [label="Stub" style="filled" fillcolor=gray];
|
||||
62 [label="Variable declaration: lval result: R|kotlin/Nothing|" style="filled" fillcolor=gray];
|
||||
63 [label="Access variable R|<local>/result|" style="filled" fillcolor=gray];
|
||||
64 [label="Stub" style="filled" fillcolor=gray];
|
||||
65 [label="Assignmenet: R|<local>/s|" style="filled" fillcolor=gray];
|
||||
subgraph cluster_17 {
|
||||
color=blue
|
||||
66 [label="Enter when" style="filled" fillcolor=gray];
|
||||
subgraph cluster_18 {
|
||||
color=blue
|
||||
67 [label="Enter when branch condition " style="filled" fillcolor=gray];
|
||||
68 [label="Access variable R|<local>/result|" style="filled" fillcolor=gray];
|
||||
69 [label="Stub" style="filled" fillcolor=gray];
|
||||
70 [label="Access variable <Unresolved name: b>#" style="filled" fillcolor=gray];
|
||||
71 [label="Exit when branch condition" style="filled" fillcolor=gray];
|
||||
}
|
||||
72 [label="Synthetic else branch" style="filled" fillcolor=gray];
|
||||
73 [label="Enter when branch result" style="filled" fillcolor=gray];
|
||||
subgraph cluster_19 {
|
||||
color=blue
|
||||
74 [label="Enter block" style="filled" fillcolor=gray];
|
||||
75 [label="Jump: break@@@[R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()] " style="filled" fillcolor=gray];
|
||||
76 [label="Stub" style="filled" fillcolor=gray];
|
||||
77 [label="Exit block" style="filled" fillcolor=gray];
|
||||
}
|
||||
78 [label="Exit when branch result" style="filled" fillcolor=gray];
|
||||
79 [label="Exit when" style="filled" fillcolor=gray];
|
||||
}
|
||||
80 [label="Exit block" style="filled" fillcolor=gray];
|
||||
}
|
||||
81 [label="Exit loop block" style="filled" fillcolor=gray];
|
||||
}
|
||||
82 [label="Exit whileloop"];
|
||||
}
|
||||
83 [label="Access variable R|<local>/s|"];
|
||||
84 [label="Enter safe call"];
|
||||
85 [label="Postponed enter to lambda"];
|
||||
subgraph cluster_20 {
|
||||
color=blue
|
||||
86 [label="Enter function anonymousFunction"];
|
||||
87 [label="Access variable R|<local>/it|"];
|
||||
88 [label="Access variable R|/A.a|"];
|
||||
89 [label="Exit function anonymousFunction"];
|
||||
}
|
||||
90 [label="Postponed exit from lambda"];
|
||||
91 [label="Function call: R|<local>/s|?.R|kotlin/let|<R|A|, R|kotlin/Int|>(<L> = let@fun <anonymous>(it: R|A|): R|kotlin/Int| <kind=EXACTLY_ONCE> {
|
||||
^ R|<local>/it|.R|/A.a|
|
||||
}
|
||||
)"];
|
||||
92 [label="Exit safe call"];
|
||||
93 [label="Exit function test_0" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
44 -> {45};
|
||||
45 -> {46};
|
||||
46 -> {47};
|
||||
47 -> {48};
|
||||
48 -> {49};
|
||||
49 -> {50};
|
||||
50 -> {51};
|
||||
51 -> {52};
|
||||
52 -> {53};
|
||||
53 -> {54};
|
||||
54 -> {55};
|
||||
55 -> {56};
|
||||
56 -> {82 57};
|
||||
57 -> {58};
|
||||
58 -> {59};
|
||||
59 -> {60};
|
||||
60 -> {93};
|
||||
60 -> {61} [style=dotted];
|
||||
61 -> {62} [style=dotted];
|
||||
62 -> {63} [style=dotted];
|
||||
63 -> {93 64} [style=dotted];
|
||||
64 -> {65} [style=dotted];
|
||||
65 -> {66} [style=dotted];
|
||||
66 -> {67} [style=dotted];
|
||||
67 -> {68} [style=dotted];
|
||||
68 -> {93 69} [style=dotted];
|
||||
69 -> {70} [style=dotted];
|
||||
70 -> {71} [style=dotted];
|
||||
71 -> {73 72} [style=dotted];
|
||||
72 -> {79} [style=dotted];
|
||||
73 -> {74} [style=dotted];
|
||||
74 -> {75} [style=dotted];
|
||||
75 -> {82 76} [style=dotted];
|
||||
76 -> {77} [style=dotted];
|
||||
77 -> {78} [style=dotted];
|
||||
78 -> {79} [style=dotted];
|
||||
79 -> {80} [style=dotted];
|
||||
80 -> {81} [style=dotted];
|
||||
81 -> {53} [style=dotted];
|
||||
82 -> {83};
|
||||
83 -> {84 92};
|
||||
84 -> {85};
|
||||
85 -> {86};
|
||||
85 -> {90} [color=red];
|
||||
86 -> {87};
|
||||
87 -> {88};
|
||||
88 -> {89};
|
||||
89 -> {90} [color=green];
|
||||
90 -> {91};
|
||||
91 -> {92};
|
||||
92 -> {93} [style=dotted];
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
fun getNothing(): Nothing = throw Exception()
|
||||
fun getNullableNothing(): Nothing? = null
|
||||
|
||||
val String?.q: Int get() = 1
|
||||
val String.qq: Int get() = 2
|
||||
|
||||
fun <T> myListOf(x: T): List<T> = null!!
|
||||
|
||||
class A {
|
||||
val a: Int = 1
|
||||
val b: Boolean = true
|
||||
}
|
||||
|
||||
fun test_0(results: List<Nothing>) {
|
||||
var s: A? = null
|
||||
for (result in results) {
|
||||
s = result
|
||||
if (result.<!UNRESOLVED_REFERENCE!>b<!>) {
|
||||
break
|
||||
}
|
||||
}
|
||||
s?.let { it.a }
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
FILE: smartcastToNothing.kt
|
||||
public final fun getNothing(): R|kotlin/Nothing| {
|
||||
^getNothing throw R|java/lang/Exception.Exception|()
|
||||
}
|
||||
public final fun getNullableNothing(): R|kotlin/Nothing?| {
|
||||
^getNullableNothing Null(null)
|
||||
}
|
||||
public final val R|kotlin/String?|.q: R|kotlin/Int|
|
||||
public get(): R|kotlin/Int| {
|
||||
^ Int(1)
|
||||
}
|
||||
public final val R|kotlin/String|.qq: R|kotlin/Int|
|
||||
public get(): R|kotlin/Int| {
|
||||
^ Int(2)
|
||||
}
|
||||
public final fun <T> myListOf(x: R|T|): R|kotlin/collections/List<T>| {
|
||||
^myListOf Null(null)!!
|
||||
}
|
||||
public final class A : R|kotlin/Any| {
|
||||
public constructor(): R|A| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final val a: R|kotlin/Int| = Int(1)
|
||||
public get(): R|kotlin/Int|
|
||||
|
||||
public final val b: R|kotlin/Boolean| = Boolean(true)
|
||||
public get(): R|kotlin/Boolean|
|
||||
|
||||
}
|
||||
public final fun test_0(results: R|kotlin/collections/List<kotlin/Nothing>|): R|kotlin/Unit| {
|
||||
lvar s: R|A?| = Null(null)
|
||||
lval <range>: R|kotlin/collections/List<kotlin/Nothing>| = R|<local>/results|
|
||||
lval <iterator>: R|kotlin/collections/Iterator<kotlin/Nothing>| = R|<local>/<range>|.R|FakeOverride<kotlin/collections/List.iterator: R|kotlin/collections/Iterator<kotlin/Nothing>|>|()
|
||||
while(R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()) {
|
||||
lval result: R|kotlin/Nothing| = R|<local>/<iterator>|.R|FakeOverride<kotlin/collections/Iterator.next: R|kotlin/Nothing|>|()
|
||||
R|<local>/s| = R|<local>/result|
|
||||
when () {
|
||||
R|<local>/result|.<Unresolved name: b># -> {
|
||||
break@@@[R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
R|<local>/s|?.R|kotlin/let|<R|A|, R|kotlin/Int|>(<L> = let@fun <anonymous>(it: R|A|): R|kotlin/Int| <kind=EXACTLY_ONCE> {
|
||||
^ R|<local>/it|.R|/A.a|
|
||||
}
|
||||
)
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
digraph thisOfExtensionProperty_kt {
|
||||
graph [splines=ortho nodesep=3]
|
||||
node [shape=box penwidth=2]
|
||||
edge [penwidth=2]
|
||||
|
||||
subgraph cluster_0 {
|
||||
color=red
|
||||
0 [label="Enter function getter" style="filled" fillcolor=red];
|
||||
1 [label="Exit function getter" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
0 -> {1};
|
||||
|
||||
subgraph cluster_1 {
|
||||
color=red
|
||||
2 [label="Enter property" style="filled" fillcolor=red];
|
||||
3 [label="Exit property" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
2 -> {3};
|
||||
|
||||
subgraph cluster_2 {
|
||||
color=red
|
||||
4 [label="Enter function getter" style="filled" fillcolor=red];
|
||||
subgraph cluster_3 {
|
||||
color=blue
|
||||
5 [label="Enter &&"];
|
||||
6 [label="Access variable this@R|/check_1|"];
|
||||
7 [label="Type operator: this is B"];
|
||||
8 [label="Exit left part of &&"];
|
||||
9 [label="Enter right part of &&"];
|
||||
10 [label="Access variable R|/B.b|"];
|
||||
11 [label="Exit &&"];
|
||||
}
|
||||
12 [label="Jump: ^ (this@R|/check_1| is R|B|) && this@R|/check_1|.R|/B.b|"];
|
||||
13 [label="Stub" style="filled" fillcolor=gray];
|
||||
14 [label="Exit function getter" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
4 -> {5};
|
||||
5 -> {6};
|
||||
6 -> {7};
|
||||
7 -> {8};
|
||||
8 -> {11 9};
|
||||
9 -> {10};
|
||||
10 -> {11};
|
||||
11 -> {12};
|
||||
12 -> {14};
|
||||
12 -> {13} [style=dotted];
|
||||
13 -> {14} [style=dotted];
|
||||
|
||||
subgraph cluster_4 {
|
||||
color=red
|
||||
15 [label="Enter property" style="filled" fillcolor=red];
|
||||
16 [label="Exit property" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
15 -> {16};
|
||||
|
||||
subgraph cluster_5 {
|
||||
color=red
|
||||
17 [label="Enter function getter" style="filled" fillcolor=red];
|
||||
subgraph cluster_6 {
|
||||
color=blue
|
||||
18 [label="Enter &&"];
|
||||
19 [label="Access variable this@R|/check_2|"];
|
||||
20 [label="Type operator: this is B"];
|
||||
21 [label="Exit left part of &&"];
|
||||
22 [label="Enter right part of &&"];
|
||||
23 [label="Access variable this@R|/check_2|"];
|
||||
24 [label="Access variable R|/B.b|"];
|
||||
25 [label="Exit &&"];
|
||||
}
|
||||
26 [label="Jump: ^ (this@R|/check_2| is R|B|) && this@R|/check_2|.R|/B.b|"];
|
||||
27 [label="Stub" style="filled" fillcolor=gray];
|
||||
28 [label="Exit function getter" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
17 -> {18};
|
||||
18 -> {19};
|
||||
19 -> {20};
|
||||
20 -> {21};
|
||||
21 -> {25 22};
|
||||
22 -> {23};
|
||||
23 -> {24};
|
||||
24 -> {25};
|
||||
25 -> {26};
|
||||
26 -> {28};
|
||||
26 -> {27} [style=dotted];
|
||||
27 -> {28} [style=dotted];
|
||||
|
||||
subgraph cluster_7 {
|
||||
color=red
|
||||
29 [label="Enter property" style="filled" fillcolor=red];
|
||||
30 [label="Exit property" style="filled" fillcolor=red];
|
||||
}
|
||||
|
||||
29 -> {30};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
interface A
|
||||
|
||||
interface B {
|
||||
val b: Boolean
|
||||
}
|
||||
|
||||
val A.check_1: Boolean
|
||||
get() = this is B && b
|
||||
|
||||
val A.check_2: Boolean
|
||||
get() = this is B && this.b
|
||||
// 1713
|
||||
@@ -0,0 +1,16 @@
|
||||
FILE: thisOfExtensionProperty.kt
|
||||
public abstract interface A : R|kotlin/Any| {
|
||||
}
|
||||
public abstract interface B : R|kotlin/Any| {
|
||||
public abstract val b: R|kotlin/Boolean|
|
||||
public get(): R|kotlin/Boolean|
|
||||
|
||||
}
|
||||
public final val R|A|.check_1: R|kotlin/Boolean|
|
||||
public get(): R|kotlin/Boolean| {
|
||||
^ (this@R|/check_1| is R|B|) && this@R|/check_1|.R|/B.b|
|
||||
}
|
||||
public final val R|A|.check_2: R|kotlin/Boolean|
|
||||
public get(): R|kotlin/Boolean| {
|
||||
^ (this@R|/check_2| is R|B|) && this@R|/check_2|.R|/B.b|
|
||||
}
|
||||
-5
@@ -1166,11 +1166,6 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
|
||||
runTest("compiler/fir/resolve/testData/resolve/problems/nestedClassContructor.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noBoundSmartcastAfterContinue.kt")
|
||||
public void testNoBoundSmartcastAfterContinue() throws Exception {
|
||||
runTest("compiler/fir/resolve/testData/resolve/problems/noBoundSmartcastAfterContinue.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("propertyFromJavaPlusAssign.kt")
|
||||
public void testPropertyFromJavaPlusAssign() throws Exception {
|
||||
runTest("compiler/fir/resolve/testData/resolve/problems/propertyFromJavaPlusAssign.kt");
|
||||
|
||||
Generated
+20
@@ -143,6 +143,11 @@ public class FirDiagnosticsWithCfgTestGenerated extends AbstractFirDiagnosticsWi
|
||||
runTest("compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("boundSmartcastsInBranches.kt")
|
||||
public void testBoundSmartcastsInBranches() throws Exception {
|
||||
runTest("compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcastsInBranches.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("casts.kt")
|
||||
public void testCasts() throws Exception {
|
||||
runTest("compiler/fir/resolve/testData/resolve/smartcasts/casts.kt");
|
||||
@@ -203,6 +208,11 @@ public class FirDiagnosticsWithCfgTestGenerated extends AbstractFirDiagnosticsWi
|
||||
runTest("compiler/fir/resolve/testData/resolve/smartcasts/nullability.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("overridenOpenVal.kt")
|
||||
public void testOverridenOpenVal() throws Exception {
|
||||
runTest("compiler/fir/resolve/testData/resolve/smartcasts/overridenOpenVal.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("returns.kt")
|
||||
public void testReturns() throws Exception {
|
||||
runTest("compiler/fir/resolve/testData/resolve/smartcasts/returns.kt");
|
||||
@@ -238,6 +248,16 @@ public class FirDiagnosticsWithCfgTestGenerated extends AbstractFirDiagnosticsWi
|
||||
runTest("compiler/fir/resolve/testData/resolve/smartcasts/smartcastOnLambda.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("smartcastToNothing.kt")
|
||||
public void testSmartcastToNothing() throws Exception {
|
||||
runTest("compiler/fir/resolve/testData/resolve/smartcasts/smartcastToNothing.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("thisOfExtensionProperty.kt")
|
||||
public void testThisOfExtensionProperty() throws Exception {
|
||||
runTest("compiler/fir/resolve/testData/resolve/smartcasts/thisOfExtensionProperty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("when.kt")
|
||||
public void testWhen() throws Exception {
|
||||
runTest("compiler/fir/resolve/testData/resolve/smartcasts/when.kt");
|
||||
|
||||
Generated
-5
@@ -1166,11 +1166,6 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
|
||||
runTest("compiler/fir/resolve/testData/resolve/problems/nestedClassContructor.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noBoundSmartcastAfterContinue.kt")
|
||||
public void testNoBoundSmartcastAfterContinue() throws Exception {
|
||||
runTest("compiler/fir/resolve/testData/resolve/problems/noBoundSmartcastAfterContinue.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("propertyFromJavaPlusAssign.kt")
|
||||
public void testPropertyFromJavaPlusAssign() throws Exception {
|
||||
runTest("compiler/fir/resolve/testData/resolve/problems/propertyFromJavaPlusAssign.kt");
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
open class A {
|
||||
open fun foo(): Any = "A"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
interface A<T, U> {
|
||||
fun foo(t: T, u: U) = "A"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
var result = ""
|
||||
|
||||
interface D1 {
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
interface D1 {
|
||||
fun foo(): Any
|
||||
}
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
open class A(val value: String) {
|
||||
fun component1() = value
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
interface A<T> {
|
||||
fun foo(t: T, u: Int) = "A"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
var result = ""
|
||||
|
||||
interface Base
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
open class A<T> {
|
||||
open fun foo(t: T) = "A"
|
||||
}
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
open class A<T, U, V> {
|
||||
open fun foo(t: T, u: U, v: V) = "A"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
open class A<T> {
|
||||
open fun foo(t: T) = "A"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
open class A<T> {
|
||||
open fun <U> foo(t: T, u: U) = "A"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
open class A<T : Number>(val t: T) {
|
||||
open fun foo(): T = t
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
interface A<T> {
|
||||
fun foo(t: T) = "A"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
open class A<T : Number> {
|
||||
open fun foo(t: T) = "A"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
abstract class A<T> {
|
||||
abstract fun foo(t: T): String
|
||||
}
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
open class A<T : U, U> {
|
||||
open fun foo(t: T, u: U) = "A"
|
||||
}
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
open class A<T> {
|
||||
open fun <U> foo(t: T, u: U) = "A"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
open class A<T> {
|
||||
open fun foo(t: T) = "A"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
open class A<T : Number> {
|
||||
open fun foo(t: T) = "A"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
interface A {
|
||||
fun foo(): Any = "A"
|
||||
}
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
interface A<T> {
|
||||
fun foo(t: T, u: Int) = "A"
|
||||
}
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
interface A<T> {
|
||||
fun foo(t: T, u: Int) = "A"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
interface A<T> {
|
||||
fun foo(t: T) = "A"
|
||||
}
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// TARGET_BACKEND: JVM
|
||||
// FILE: Test.java
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@ class A() {
|
||||
fun f(): Unit {
|
||||
var x: Int? = 1
|
||||
x = null
|
||||
x + 1
|
||||
x plus 1
|
||||
x <!INAPPLICABLE_CANDIDATE!>+<!> 1
|
||||
x <!INAPPLICABLE_CANDIDATE!>plus<!> 1
|
||||
x < 1
|
||||
x += 1
|
||||
<!UNRESOLVED_REFERENCE!>x += 1<!>
|
||||
|
||||
x == 1
|
||||
x != 1
|
||||
@@ -22,8 +22,8 @@ fun f(): Unit {
|
||||
x === 1
|
||||
x !== 1
|
||||
|
||||
x..2
|
||||
x in 1..2
|
||||
x<!INAPPLICABLE_CANDIDATE!>..<!>2
|
||||
x <!INAPPLICABLE_CANDIDATE!>in<!> 1..2
|
||||
|
||||
val y : Boolean? = true
|
||||
false || y
|
||||
|
||||
@@ -11,7 +11,7 @@ fun foo() {
|
||||
while (y != null) {
|
||||
bar(y)
|
||||
}
|
||||
bar(y)
|
||||
<!INAPPLICABLE_CANDIDATE!>bar<!>(y)
|
||||
|
||||
val z: Int? = null
|
||||
while (z == null) {
|
||||
|
||||
+1
-1
@@ -22,5 +22,5 @@ fun whileWithNoCondition(x: Int?) {
|
||||
while (<!SYNTAX!><!>) {
|
||||
x!!
|
||||
}
|
||||
checkSubtype<Int>(x)
|
||||
<!INAPPLICABLE_CANDIDATE!>checkSubtype<!><Int>(x)
|
||||
}
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@ class A<out T, in E> {
|
||||
fun foo(x: A<String, Any?>, cs: CharSequence, ls: List<CharSequence>) {
|
||||
val y: A<CharSequence, String> = x
|
||||
|
||||
y.foo(cs)
|
||||
val s: String = y.foo(cs, ls)
|
||||
y.<!INAPPLICABLE_CANDIDATE!>foo<!>(cs)
|
||||
val s: String = y.<!INAPPLICABLE_CANDIDATE!>foo<!>(cs, ls)
|
||||
|
||||
val ls2: List<String> = y.bar()
|
||||
}
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ class CtxImpl : Ctx {
|
||||
open class Test(open val ctx: Ctx) {
|
||||
fun test() {
|
||||
when (ctx) {
|
||||
is CtxImpl -> ctx.doJob(2)
|
||||
is CtxImpl -> ctx.<!UNRESOLVED_REFERENCE!>doJob<!>(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -9,6 +9,6 @@ var a: A? = null
|
||||
|
||||
fun smartCastInterference(b: B) {
|
||||
if (a != null) {
|
||||
a.foo(b)
|
||||
a.<!INAPPLICABLE_CANDIDATE!>foo<!>(b)
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
fun foo(): String {
|
||||
var s: String?
|
||||
s = null
|
||||
s?.<!UNRESOLVED_REFERENCE!>length<!>
|
||||
s.<!UNRESOLVED_REFERENCE!>length<!>
|
||||
s?.length
|
||||
s.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
if (s == null) return s!!
|
||||
var t: String? = "y"
|
||||
if (t == null) t = "x"
|
||||
|
||||
@@ -41,7 +41,7 @@ fun f(a: SomeClass?) {
|
||||
if (aa as? SomeSubClass != null) {
|
||||
aa = null
|
||||
// 'aa' cannot be cast to SomeSubClass
|
||||
aa.<!UNRESOLVED_REFERENCE!>hashCode<!>()
|
||||
aa.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
|
||||
aa.<!UNRESOLVED_REFERENCE!>foo<!>
|
||||
(aa as? SomeSubClass).<!INAPPLICABLE_CANDIDATE!>foo<!>
|
||||
(aa as SomeSubClass).foo
|
||||
@@ -50,7 +50,7 @@ fun f(a: SomeClass?) {
|
||||
aa = null
|
||||
if (b != null) {
|
||||
// 'aa' cannot be cast to SomeSubClass
|
||||
aa.<!UNRESOLVED_REFERENCE!>hashCode<!>()
|
||||
aa.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
|
||||
aa.<!UNRESOLVED_REFERENCE!>foo<!>
|
||||
(aa as? SomeSubClass).<!INAPPLICABLE_CANDIDATE!>foo<!>
|
||||
(aa as SomeSubClass).foo
|
||||
|
||||
+4
-4
@@ -28,11 +28,11 @@ fun test() {
|
||||
x.foo().checkType { _<CharSequence?>() }
|
||||
|
||||
if (x is B && x is C) {
|
||||
x.foo().checkType { <!INAPPLICABLE_CANDIDATE!>_<!><CharSequence?>() }
|
||||
x.foo().checkType { _<CharSequence?>() }
|
||||
x.baz("")
|
||||
x.baz(1).checkType { <!INAPPLICABLE_CANDIDATE!>_<!><Unit>() }
|
||||
x.baz(1, 2)
|
||||
x.baz(1).checkType { _<Unit>() }
|
||||
x.<!INAPPLICABLE_CANDIDATE!>baz<!>(1, 2)
|
||||
|
||||
x.foobar().checkType { _<String>() }
|
||||
x.<!UNRESOLVED_REFERENCE!>foobar<!>().<!INAPPLICABLE_CANDIDATE!>checkType<!> { <!UNRESOLVED_REFERENCE!>_<!><String>() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,5 +6,5 @@ public fun foo(p: String?): Int {
|
||||
if (x()) break
|
||||
}
|
||||
// p is nullable because it's possible loop body is not executed at all
|
||||
return p.length
|
||||
return p.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
}
|
||||
@@ -7,5 +7,5 @@ public fun foo(p: String?): Int {
|
||||
if (x()) break
|
||||
}
|
||||
// Smart cast should not work in this case, see KT-6284
|
||||
return p.length
|
||||
return p.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
}
|
||||
Vendored
+2
-2
@@ -8,7 +8,7 @@ fun foo() {
|
||||
|
||||
}
|
||||
// TODO: this testdata fixates undesired behavior (it should be an unsafe call)
|
||||
x.<!UNRESOLVED_REFERENCE!>length<!> // 'x' is unsoundly smartcasted here
|
||||
x.length // 'x' is unsoundly smartcasted here
|
||||
}
|
||||
|
||||
fun bar() {
|
||||
@@ -19,5 +19,5 @@ fun bar() {
|
||||
|
||||
}
|
||||
// TODO: this testdata fixates undesired behavior (it should be an unsafe call)
|
||||
x.<!UNRESOLVED_REFERENCE!>size<!> // 'x' is unsoundly smartcasted here
|
||||
x.size // 'x' is unsoundly smartcasted here
|
||||
}
|
||||
compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakBefore.fir.kt
Vendored
+2
-2
@@ -7,7 +7,7 @@ fun foo() {
|
||||
break
|
||||
|
||||
}
|
||||
x.<!UNRESOLVED_REFERENCE!>length<!> // 'x' is unsoundly smartcasted here
|
||||
x.length // 'x' is unsoundly smartcasted here
|
||||
}
|
||||
|
||||
fun bar() {
|
||||
@@ -17,5 +17,5 @@ fun bar() {
|
||||
break
|
||||
|
||||
}
|
||||
x.<!UNRESOLVED_REFERENCE!>size<!> // 'x' is unsoundly smartcasted here
|
||||
x.size // 'x' is unsoundly smartcasted here
|
||||
}
|
||||
+1
-1
@@ -9,5 +9,5 @@ fun foo(arg: Int?) {
|
||||
}
|
||||
if (x != null) x = 42
|
||||
// Unsafe because of lambda
|
||||
x.<!UNRESOLVED_REFERENCE!>hashCode<!>()
|
||||
x.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@ public class X {
|
||||
public fun fn(): Int {
|
||||
if (y != null)
|
||||
// With non-default getter smartcast is not possible
|
||||
return y.length
|
||||
return y.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
else
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -7,5 +7,5 @@ infix fun Int.bar(i: Int) = i
|
||||
fun test() {
|
||||
val p = A()
|
||||
// For open value properties, smart casts should not work
|
||||
if (p.foo is Int) p.foo bar 11
|
||||
if (p.foo is Int) p.foo <!INAPPLICABLE_CANDIDATE!>bar<!> 11
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@ public class X {
|
||||
public fun fn(): Int {
|
||||
if (x != null)
|
||||
// Smartcast is not possible for variable properties
|
||||
return x.length
|
||||
return x.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
else if (y != null)
|
||||
// Even if they are private
|
||||
return y.length
|
||||
return y.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
else
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -15,6 +15,6 @@ fun bar(): Int {
|
||||
return when(ss) {
|
||||
"abc" -> ss
|
||||
else -> "xyz"
|
||||
}.length
|
||||
}.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -8,7 +8,7 @@ fun test1() {
|
||||
if (newC != null) {
|
||||
c = newC
|
||||
}
|
||||
<!INAPPLICABLE_CANDIDATE!>foo<!>(c)
|
||||
foo(c)
|
||||
}
|
||||
|
||||
fun test2() {
|
||||
@@ -19,7 +19,7 @@ fun test2() {
|
||||
if (newC is String) {
|
||||
c = newC
|
||||
}
|
||||
<!INAPPLICABLE_CANDIDATE!>foo<!>(c)
|
||||
foo(c)
|
||||
}
|
||||
|
||||
fun test3() {
|
||||
@@ -30,6 +30,6 @@ fun test3() {
|
||||
if (newC == null) return
|
||||
c = newC
|
||||
|
||||
<!INAPPLICABLE_CANDIDATE!>foo<!>(c)
|
||||
foo(c)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -6,5 +6,5 @@ fun foo(x: Int, f: () -> Unit, y: Int) {}
|
||||
fun bar() {
|
||||
var x: Int?
|
||||
x = 4
|
||||
foo(x, { x = null; x.<!UNRESOLVED_REFERENCE!>hashCode<!>() }, x)
|
||||
foo(x, { x = null; x.<!INAPPLICABLE_CANDIDATE!>hashCode<!>() }, x)
|
||||
}
|
||||
+1
-1
@@ -13,5 +13,5 @@ fun list(start: String) {
|
||||
e = e.next()
|
||||
}
|
||||
// e can never be null but we do not know it
|
||||
e.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
|
||||
e.hashCode()
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@ fun foo() {
|
||||
v = "abc"
|
||||
v.length
|
||||
v = null
|
||||
v.<!UNRESOLVED_REFERENCE!>length<!>
|
||||
v.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
v = "abc"
|
||||
v.length
|
||||
}
|
||||
+2
-2
@@ -6,7 +6,7 @@ fun foo() {
|
||||
val y = x
|
||||
x = null
|
||||
if (y != null) {
|
||||
x.<!UNRESOLVED_REFERENCE!>hashCode<!>()
|
||||
x.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ fun bar(s: String?) {
|
||||
val hashCode = ss?.hashCode()
|
||||
ss = null
|
||||
if (hashCode != null) {
|
||||
ss.<!UNRESOLVED_REFERENCE!>hashCode<!>()
|
||||
ss.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -44,7 +44,7 @@ fun gaz(s: String?) {
|
||||
x = null
|
||||
}
|
||||
run {
|
||||
x.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
|
||||
x.hashCode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -11,11 +11,11 @@ fun list(start: SomeObject) {
|
||||
// In theory smart cast is possible here
|
||||
// But in practice we have a loop with changing e
|
||||
// ?: should we "or" entrance type info with condition type info?
|
||||
if (<!UNRESOLVED_REFERENCE!>!<!>e.<!INAPPLICABLE_CANDIDATE!>doSomething<!>())
|
||||
if (!e.doSomething())
|
||||
break
|
||||
// Smart cast here is still not possible
|
||||
e = e.<!INAPPLICABLE_CANDIDATE!>next<!>()
|
||||
e = e.next()
|
||||
} while (e != null)
|
||||
// e can be null because of next()
|
||||
e.<!INAPPLICABLE_CANDIDATE!>doSomething<!>()
|
||||
e.doSomething()
|
||||
}
|
||||
+1
-1
@@ -8,5 +8,5 @@ public fun foo(pp: String?): Int {
|
||||
p = null
|
||||
} while (!x())
|
||||
// Smart cast is NOT possible here
|
||||
return p.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
return p.length
|
||||
}
|
||||
@@ -9,8 +9,8 @@ fun list(start: SomeObject): SomeObject {
|
||||
var e: SomeObject? = start
|
||||
for (i in 0..42) {
|
||||
// Unsafe calls because of nullable e at the beginning
|
||||
e.<!INAPPLICABLE_CANDIDATE!>doSomething<!>()
|
||||
e = e.<!INAPPLICABLE_CANDIDATE!>next<!>()
|
||||
e.doSomething()
|
||||
e = e.next()
|
||||
}
|
||||
// Smart cast is not possible here due to next()
|
||||
return e
|
||||
|
||||
+1
-1
@@ -16,5 +16,5 @@ fun list(start: SomeObject) {
|
||||
e = e.next()
|
||||
}
|
||||
// e can be null because of next()
|
||||
e.<!INAPPLICABLE_CANDIDATE!>doSomething<!>()
|
||||
e.doSomething()
|
||||
}
|
||||
+1
-1
@@ -76,5 +76,5 @@ fun test6() {
|
||||
finally {
|
||||
a = null
|
||||
}
|
||||
a.<!UNRESOLVED_REFERENCE!>hashCode<!>() // a is null here
|
||||
a.<!INAPPLICABLE_CANDIDATE!>hashCode<!>() // a is null here
|
||||
}
|
||||
|
||||
+1
-1
@@ -4,5 +4,5 @@ fun foo() {
|
||||
// It is possible in principle to provide smart cast here
|
||||
v.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
v = null
|
||||
v.<!UNRESOLVED_REFERENCE!>length<!>
|
||||
v.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
}
|
||||
+1
-1
@@ -6,5 +6,5 @@ fun foo() {
|
||||
try {
|
||||
s = null
|
||||
} catch (ex: Exception) {}
|
||||
s.<!UNRESOLVED_REFERENCE!>hashCode<!>()
|
||||
s.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
|
||||
}
|
||||
+1
-1
@@ -12,5 +12,5 @@ fun foo() {
|
||||
finally {
|
||||
bar()
|
||||
}
|
||||
s.<!UNRESOLVED_REFERENCE!>hashCode<!>()
|
||||
s.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
|
||||
}
|
||||
+1
-1
@@ -6,5 +6,5 @@ fun foo() {
|
||||
try {
|
||||
s = null
|
||||
} catch (ex: Exception) {}
|
||||
s.<!UNRESOLVED_REFERENCE!>hashCode<!>()
|
||||
s.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
|
||||
}
|
||||
+1
-1
@@ -2,5 +2,5 @@
|
||||
fun foo(): Int {
|
||||
var i: Int? = 42
|
||||
i = null
|
||||
return i + 1
|
||||
return i <!INAPPLICABLE_CANDIDATE!>+<!> 1
|
||||
}
|
||||
@@ -2,5 +2,5 @@
|
||||
fun foo(): Int {
|
||||
var s: String? = "abc"
|
||||
s = null
|
||||
return s.<!UNRESOLVED_REFERENCE!>length<!>
|
||||
return s.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
}
|
||||
@@ -9,5 +9,5 @@ public fun foo(pp: String?): Int {
|
||||
}
|
||||
// Smart cast is NOT possible here
|
||||
// (we could provide it but p = null makes it much harder)
|
||||
return p.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
return p.length
|
||||
}
|
||||
+1
-1
@@ -9,5 +9,5 @@ public fun foo(pp: String?): Int {
|
||||
}
|
||||
// Smart cast is NOT possible here
|
||||
// (we could provide it but p = null makes it much harder)
|
||||
return p.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
return p.length
|
||||
}
|
||||
+1
-1
@@ -9,5 +9,5 @@ public fun foo(pp: String?): Int {
|
||||
}
|
||||
// Smart cast is NOT possible here
|
||||
// (we could provide it but p = null makes it much harder)
|
||||
return p.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
return p.length
|
||||
}
|
||||
+1
-1
@@ -13,5 +13,5 @@ fun list(start: SomeObject) {
|
||||
e = e.next()
|
||||
}
|
||||
// e can be null because of next()
|
||||
e.<!INAPPLICABLE_CANDIDATE!>doSomething<!>()
|
||||
e.doSomething()
|
||||
}
|
||||
+2
-2
@@ -27,11 +27,11 @@ fun testUnsafeCaptureVarInInitializer() {
|
||||
|
||||
val s = when (val y = run { x = null; 32 }) {
|
||||
0 -> {
|
||||
x.<!UNRESOLVED_REFERENCE!>inc<!>() // NB smart cast should be impossible
|
||||
x.<!INAPPLICABLE_CANDIDATE!>inc<!>() // NB smart cast should be impossible
|
||||
"0"
|
||||
}
|
||||
else -> "!= 0"
|
||||
}
|
||||
|
||||
x.<!UNRESOLVED_REFERENCE!>inc<!>() // NB smart cast should be impossible
|
||||
x.<!INAPPLICABLE_CANDIDATE!>inc<!>() // NB smart cast should be impossible
|
||||
}
|
||||
+1
-1
@@ -3,5 +3,5 @@
|
||||
fun foo(y: String) {
|
||||
var x: String? = null
|
||||
y.let { x = it }
|
||||
x.<!INAPPLICABLE_CANDIDATE!>length<!> // Smart cast is not possible
|
||||
x.length // Smart cast is not possible
|
||||
}
|
||||
|
||||
+1
-1
@@ -4,6 +4,6 @@ fun foo(y: String?) {
|
||||
var x: String? = null
|
||||
if (x != null) {
|
||||
y?.let { x = it }
|
||||
x.<!INAPPLICABLE_CANDIDATE!>length<!> // not-null or not-null
|
||||
x.length // not-null or not-null
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -15,7 +15,7 @@ fun test1(s: String?) {
|
||||
catch (e: Exception) {
|
||||
requireNotNull(s)
|
||||
}
|
||||
t2.<!UNRESOLVED_REFERENCE!>not<!>()
|
||||
t2.<!INAPPLICABLE_CANDIDATE!>not<!>()
|
||||
s.length
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ fun test3() {
|
||||
s = null
|
||||
return
|
||||
}
|
||||
s.<!UNRESOLVED_REFERENCE!>length<!>
|
||||
s.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
}
|
||||
|
||||
fun test4() {
|
||||
@@ -61,7 +61,7 @@ fun test4() {
|
||||
catch (e: ExcB) {
|
||||
|
||||
}
|
||||
s.<!UNRESOLVED_REFERENCE!>length<!>
|
||||
s.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
}
|
||||
|
||||
fun test5(s: String?) {
|
||||
|
||||
+3
-3
@@ -16,7 +16,7 @@ fun test1(s: String?) {
|
||||
catch (e: Exception) {
|
||||
requireNotNull(s)
|
||||
}
|
||||
t2.<!UNRESOLVED_REFERENCE!>not<!>()
|
||||
t2.<!INAPPLICABLE_CANDIDATE!>not<!>()
|
||||
s.length
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ fun test3() {
|
||||
s = null
|
||||
return
|
||||
}
|
||||
s.<!UNRESOLVED_REFERENCE!>length<!>
|
||||
s.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
}
|
||||
|
||||
fun test4() {
|
||||
@@ -62,7 +62,7 @@ fun test4() {
|
||||
catch (e: ExcB) {
|
||||
|
||||
}
|
||||
s.<!UNRESOLVED_REFERENCE!>length<!>
|
||||
s.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
}
|
||||
|
||||
fun test5(s: String?) {
|
||||
|
||||
+12
-12
@@ -13,15 +13,15 @@ fun test1() {
|
||||
try {
|
||||
x = null
|
||||
} catch (e: Exception) {
|
||||
x.<!UNRESOLVED_REFERENCE!>length<!> // smartcast shouldn't be allowed (OOME could happen after `x = null`)
|
||||
x.<!INAPPLICABLE_CANDIDATE!>length<!> // smartcast shouldn't be allowed (OOME could happen after `x = null`)
|
||||
throw e
|
||||
}
|
||||
finally {
|
||||
// smartcast shouldn't be allowed, `x = null` could've happened
|
||||
x.<!UNRESOLVED_REFERENCE!>length<!>
|
||||
x.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
}
|
||||
// smartcast shouldn't be allowed, `x = null` could've happened
|
||||
x.<!UNRESOLVED_REFERENCE!>length<!>
|
||||
x.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
}
|
||||
|
||||
// With old DFA of try/catch info about unsound smartcasts after try
|
||||
@@ -33,12 +33,12 @@ fun test2() {
|
||||
try {
|
||||
x = null
|
||||
} catch (e: Exception) {
|
||||
x.<!UNRESOLVED_REFERENCE!>length<!>
|
||||
x.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
}
|
||||
finally {
|
||||
x.<!UNRESOLVED_REFERENCE!>length<!>
|
||||
x.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
}
|
||||
x.<!UNRESOLVED_REFERENCE!>length<!>
|
||||
x.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
}
|
||||
|
||||
fun test3() {
|
||||
@@ -49,7 +49,7 @@ fun test3() {
|
||||
} catch (e: Exception) {
|
||||
t2 = null
|
||||
}
|
||||
t2.<!UNRESOLVED_REFERENCE!>not<!>() // wrong smartcast, NPE
|
||||
t2.<!INAPPLICABLE_CANDIDATE!>not<!>() // wrong smartcast, NPE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ fun test4() {
|
||||
try {
|
||||
t2 = null
|
||||
} finally { }
|
||||
t2.<!UNRESOLVED_REFERENCE!>not<!>() // wrong smartcast, NPE
|
||||
t2.<!INAPPLICABLE_CANDIDATE!>not<!>() // wrong smartcast, NPE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,10 +80,10 @@ fun test5() {
|
||||
}
|
||||
finally {
|
||||
s1.length
|
||||
s2.<!UNRESOLVED_REFERENCE!>length<!>
|
||||
s2.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
}
|
||||
s1.length
|
||||
s2.<!UNRESOLVED_REFERENCE!>length<!>
|
||||
s2.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
}
|
||||
|
||||
fun test6(s1: String?, s2: String?) {
|
||||
@@ -97,10 +97,10 @@ fun test6(s1: String?, s2: String?) {
|
||||
return
|
||||
}
|
||||
finally {
|
||||
s.<!UNRESOLVED_REFERENCE!>length<!>
|
||||
s.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
requireNotNull(s2)
|
||||
}
|
||||
s.<!UNRESOLVED_REFERENCE!>length<!>
|
||||
s.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
s1.length
|
||||
s2.length
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user