Elvis / if / when now infer error type in case of ErrorType + Nothing #KT-6665 Fixed
(cherry picked from commit 9a50a0b)
This commit is contained in:
committed by
Mikhail Glukhikh
parent
49fb9ff424
commit
5ccbf47531
+4
-2
@@ -1175,7 +1175,10 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
DataFlowInfo dataFlowInfo = resolvedCall.getDataFlowInfoForArguments().getInfo(call.getValueArguments().get(1));
|
||||
|
||||
KotlinType type = resolvedCall.getResultingDescriptor().getReturnType();
|
||||
if (type == null || rightType == null) return TypeInfoFactoryKt.noTypeInfo(dataFlowInfo);
|
||||
if (type == null ||
|
||||
rightType == null ||
|
||||
leftType == null && KotlinBuiltIns.isNothing(rightType)) return TypeInfoFactoryKt.noTypeInfo(dataFlowInfo);
|
||||
|
||||
if (leftType != null) {
|
||||
DataFlowValue leftValue = createDataFlowValue(left, leftType, context);
|
||||
DataFlowInfo rightDataFlowInfo = resolvedCall.getDataFlowInfoForArguments().getResultInfo();
|
||||
@@ -1197,7 +1200,6 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Sometimes return type for special call for elvis operator might be nullable,
|
||||
// but result is not nullable if the right type is not nullable
|
||||
if (!TypeUtils.isNullableType(rightType) && TypeUtils.isNullableType(type)) {
|
||||
|
||||
+4
@@ -179,6 +179,10 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
||||
else {
|
||||
resultDataFlowInfo = thenDataFlowInfo.or(elseDataFlowInfo);
|
||||
}
|
||||
if (thenType == null && jumpInElse ||
|
||||
elseType == null && jumpInThen) {
|
||||
return TypeInfoFactoryKt.noTypeInfo(resultDataFlowInfo);
|
||||
}
|
||||
}
|
||||
// If break or continue was possible, take condition check info as the jump info
|
||||
return TypeInfoFactoryKt.createTypeInfo(
|
||||
|
||||
+20
-10
@@ -36,6 +36,7 @@ import org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils.*
|
||||
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.createTypeInfo
|
||||
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.noTypeInfo
|
||||
import java.util.*
|
||||
|
||||
class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTypingInternals) : ExpressionTypingVisitor(facade) {
|
||||
@@ -87,29 +88,29 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
||||
val whenReturnType = inferTypeForWhenExpression(expression, contextWithExpectedType, contextAfterSubject, dataFlowInfoForEntries)
|
||||
val whenResultValue = whenReturnType?.let { DataFlowValueFactory.createDataFlowValue(expression, it, contextAfterSubject) }
|
||||
|
||||
val (outputDataFlowInfo, jumpOutPossible) =
|
||||
joinWhenExpressionBranches(expression, contextAfterSubject, jumpOutPossibleInSubject, whenResultValue)
|
||||
val branchesTypeInfo =
|
||||
joinWhenExpressionBranches(expression, contextAfterSubject, whenReturnType, jumpOutPossibleInSubject, whenResultValue)
|
||||
|
||||
val isExhaustive = WhenChecker.isWhenExhaustive(expression, trace)
|
||||
|
||||
val branchesDataFlowInfo = branchesTypeInfo.dataFlowInfo
|
||||
val resultDataFlowInfo = if (expression.elseExpression == null && !isExhaustive) {
|
||||
// Without else expression in non-exhaustive when, we *must* take initial data flow info into account,
|
||||
// because data flow can bypass all when branches in this case
|
||||
outputDataFlowInfo.or(contextAfterSubject.dataFlowInfo)
|
||||
branchesDataFlowInfo.or(contextAfterSubject.dataFlowInfo)
|
||||
}
|
||||
else {
|
||||
outputDataFlowInfo
|
||||
branchesDataFlowInfo
|
||||
}
|
||||
|
||||
if (whenReturnType != null && isExhaustive && expression.elseExpression == null && KotlinBuiltIns.isNothing(whenReturnType)) {
|
||||
trace.record(BindingContext.IMPLICIT_EXHAUSTIVE_WHEN, expression)
|
||||
}
|
||||
|
||||
val resultType = whenReturnType?.let {
|
||||
components.dataFlowAnalyzer.checkType(it, expression, contextWithExpectedType)
|
||||
}
|
||||
val branchesType = branchesTypeInfo.type ?: return noTypeInfo(resultDataFlowInfo)
|
||||
val resultType = components.dataFlowAnalyzer.checkType(branchesType, expression, contextWithExpectedType)
|
||||
|
||||
return createTypeInfo(resultType, resultDataFlowInfo, jumpOutPossible, contextWithExpectedType.dataFlowInfo)
|
||||
return createTypeInfo(resultType, resultDataFlowInfo, branchesTypeInfo.jumpOutPossible, contextWithExpectedType.dataFlowInfo)
|
||||
}
|
||||
|
||||
private fun inferTypeForWhenExpression(
|
||||
@@ -172,19 +173,24 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
||||
private fun joinWhenExpressionBranches(
|
||||
expression: KtWhenExpression,
|
||||
contextAfterSubject: ExpressionTypingContext,
|
||||
resultType: KotlinType?,
|
||||
jumpOutPossibleInSubject: Boolean,
|
||||
whenResultValue: DataFlowValue?
|
||||
): Pair<DataFlowInfo, Boolean> {
|
||||
): KotlinTypeInfo {
|
||||
val bindingContext = contextAfterSubject.trace.bindingContext
|
||||
|
||||
var currentDataFlowInfo: DataFlowInfo? = null
|
||||
var jumpOutPossible = jumpOutPossibleInSubject
|
||||
var errorTypeExistInBranch = false
|
||||
for (whenEntry in expression.entries) {
|
||||
val entryExpression = whenEntry.expression ?: continue
|
||||
|
||||
val entryTypeInfo = BindingContextUtils.getRecordedTypeInfo(entryExpression, bindingContext) ?:
|
||||
continue
|
||||
val entryType = entryTypeInfo.type
|
||||
if (entryType == null) {
|
||||
errorTypeExistInBranch = true
|
||||
}
|
||||
|
||||
val entryDataFlowInfo =
|
||||
if (whenResultValue != null && entryType != null) {
|
||||
@@ -206,7 +212,11 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
||||
jumpOutPossible = jumpOutPossible or entryTypeInfo.jumpOutPossible
|
||||
}
|
||||
|
||||
return Pair(currentDataFlowInfo ?: contextAfterSubject.dataFlowInfo, jumpOutPossible)
|
||||
val resultDataFlowInfo = currentDataFlowInfo ?: contextAfterSubject.dataFlowInfo
|
||||
return if (resultType == null || errorTypeExistInBranch && KotlinBuiltIns.isNothing(resultType))
|
||||
noTypeInfo(resultDataFlowInfo)
|
||||
else
|
||||
createTypeInfo(resultType, resultDataFlowInfo, jumpOutPossible, resultDataFlowInfo)
|
||||
}
|
||||
|
||||
private fun checkSmartCastsInSubjectIfRequired(
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ else {
|
||||
{ true } <!USELESS_ELVIS_ON_LAMBDA_EXPRESSION!>?:<!> null!!
|
||||
}
|
||||
|
||||
val <!IMPLICIT_NOTHING_PROPERTY_TYPE!>ww<!> = if (true) {
|
||||
val ww = if (true) {
|
||||
<!TYPE_MISMATCH!>{ true }<!> <!USELESS_ELVIS_ON_LAMBDA_EXPRESSION!>?:<!> null!!
|
||||
}
|
||||
else if (true) {
|
||||
|
||||
+1
-1
@@ -7,6 +7,6 @@ public val bbbb: (() -> kotlin.Boolean)?
|
||||
public val n: kotlin.Nothing
|
||||
public val v: () -> kotlin.Boolean
|
||||
public val w: () -> kotlin.Boolean
|
||||
public val ww: kotlin.Nothing
|
||||
public val ww: ???
|
||||
public fun f(/*0*/ x: kotlin.Long?): kotlin.Long
|
||||
public fun l(): (() -> kotlin.Boolean)?
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
// See KT-6665: unresolved reference (v.bar) should not produce "unreachable code" after it
|
||||
fun foo(): Int {
|
||||
val v = 1
|
||||
val <!UNUSED_VARIABLE!>c<!> = v.<!UNRESOLVED_REFERENCE!>bar<!> ?: return 0
|
||||
return 42
|
||||
}
|
||||
|
||||
fun foo2(): Int {
|
||||
val v = 1
|
||||
val c = if (true) v.<!UNRESOLVED_REFERENCE!>bar<!> else return 3
|
||||
val <!UNUSED_VARIABLE!>b<!> = <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>c<!>
|
||||
return 42
|
||||
}
|
||||
|
||||
fun foo3(): Int {
|
||||
val v = 1
|
||||
val c = when {
|
||||
true -> v.<!UNRESOLVED_REFERENCE!>bar<!>
|
||||
else -> return 3
|
||||
}
|
||||
val <!UNUSED_VARIABLE!>b<!> = <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>c<!>
|
||||
return 42
|
||||
}
|
||||
|
||||
// Type + ErrorType should give Type, unless Type is Nothing
|
||||
|
||||
fun bar(): Int {
|
||||
val v = 1
|
||||
val c = v.<!UNRESOLVED_REFERENCE!>bar<!> ?: 42
|
||||
return c
|
||||
}
|
||||
|
||||
fun bar2(): Int {
|
||||
val v = 1
|
||||
val c = if (true) v.<!UNRESOLVED_REFERENCE!>bar<!> else 3
|
||||
val b = c
|
||||
return b
|
||||
}
|
||||
|
||||
fun bar3(): Int {
|
||||
val v = 1
|
||||
val c = when {
|
||||
true -> v.<!UNRESOLVED_REFERENCE!>bar<!>
|
||||
else -> 3
|
||||
}
|
||||
val b = c
|
||||
return b
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package
|
||||
|
||||
public fun bar(): kotlin.Int
|
||||
public fun bar2(): kotlin.Int
|
||||
public fun bar3(): kotlin.Int
|
||||
public fun foo(): kotlin.Int
|
||||
public fun foo2(): kotlin.Int
|
||||
public fun foo3(): kotlin.Int
|
||||
@@ -3453,6 +3453,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("unresolvedReference.kt")
|
||||
public void testUnresolvedReference() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/unresolvedReference.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("useUninitializedInLambda.kt")
|
||||
public void testUseUninitializedInLambda() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/useUninitializedInLambda.kt");
|
||||
|
||||
Reference in New Issue
Block a user