KT-10646, KT-10647:
Move IMPLICIT_CAST_TO_UNIT_OR_ANY to ControlFlowInformationProvider (where checks for 'if' and 'when' used as expressions are performed).
This commit is contained in:
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.cfg;
|
package org.jetbrains.kotlin.cfg;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
@@ -701,10 +702,17 @@ public class ControlFlowInformationProvider {
|
|||||||
for (KtElement element : instruction.getOwner().getValueElements(value)) {
|
for (KtElement element : instruction.getOwner().getValueElements(value)) {
|
||||||
if (!(element instanceof KtIfExpression)) continue;
|
if (!(element instanceof KtIfExpression)) continue;
|
||||||
KtIfExpression ifExpression = (KtIfExpression) element;
|
KtIfExpression ifExpression = (KtIfExpression) element;
|
||||||
if (ifExpression.getThen() != null && ifExpression.getElse() != null) continue;
|
|
||||||
|
|
||||||
if (BindingContextUtilsKt.isUsedAsExpression(ifExpression, trace.getBindingContext())) {
|
if (BindingContextUtilsKt.isUsedAsExpression(ifExpression, trace.getBindingContext())) {
|
||||||
trace.report(INVALID_IF_AS_EXPRESSION.on(ifExpression));
|
KtExpression thenExpression = ifExpression.getThen();
|
||||||
|
KtExpression elseExpression = ifExpression.getElse();
|
||||||
|
|
||||||
|
if (thenExpression == null || elseExpression == null) {
|
||||||
|
trace.report(INVALID_IF_AS_EXPRESSION.on(ifExpression));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
checkImplicitCastOnConditionalExpression(ifExpression, ImmutableList.of(thenExpression, elseExpression));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -712,6 +720,31 @@ public class ControlFlowInformationProvider {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkImplicitCastOnConditionalExpression(
|
||||||
|
@NotNull KtExpression expression,
|
||||||
|
@NotNull Collection<KtExpression> branchExpressions
|
||||||
|
) {
|
||||||
|
KotlinType expectedExpressionType = trace.get(EXPECTED_EXPRESSION_TYPE, expression);
|
||||||
|
if (expectedExpressionType != null) return;
|
||||||
|
|
||||||
|
KotlinType expressionType = trace.getType(expression);
|
||||||
|
if (expressionType == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (KotlinBuiltIns.isAnyOrNullableAny(expressionType)) {
|
||||||
|
for (KtExpression branchExpression : branchExpressions) {
|
||||||
|
KotlinType branchType = trace.getType(branchExpression);
|
||||||
|
if (branchType == null || KotlinBuiltIns.isAnyOrNullableAny(branchType)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trace.report(IMPLICIT_CAST_TO_UNIT_OR_ANY.on(expression, expressionType));
|
||||||
|
}
|
||||||
|
else if (KotlinBuiltIns.isUnit(expressionType)) {
|
||||||
|
trace.report(IMPLICIT_CAST_TO_UNIT_OR_ANY.on(expression, expressionType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void markWhenWithoutElse() {
|
public void markWhenWithoutElse() {
|
||||||
final Map<Instruction, Edges<InitControlFlowInfo>> initializers = pseudocodeVariablesData.getVariableInitializers();
|
final Map<Instruction, Edges<InitControlFlowInfo>> initializers = pseudocodeVariablesData.getVariableInitializers();
|
||||||
PseudocodeTraverserKt.traverse(
|
PseudocodeTraverserKt.traverse(
|
||||||
@@ -741,6 +774,18 @@ public class ControlFlowInformationProvider {
|
|||||||
for (KtElement element : instruction.getOwner().getValueElements(value)) {
|
for (KtElement element : instruction.getOwner().getValueElements(value)) {
|
||||||
if (!(element instanceof KtWhenExpression)) continue;
|
if (!(element instanceof KtWhenExpression)) continue;
|
||||||
KtWhenExpression whenExpression = (KtWhenExpression) element;
|
KtWhenExpression whenExpression = (KtWhenExpression) element;
|
||||||
|
|
||||||
|
if (BindingContextUtilsKt.isUsedAsExpression(whenExpression, trace.getBindingContext())) {
|
||||||
|
List<KtExpression> branchExpressions = new ArrayList<KtExpression>(whenExpression.getEntries().size() + 1);
|
||||||
|
for (KtWhenEntry whenEntry : whenExpression.getEntries()) {
|
||||||
|
branchExpressions.add(whenEntry.getExpression());
|
||||||
|
}
|
||||||
|
if (whenExpression.getElseExpression() != null) {
|
||||||
|
branchExpressions.add(whenExpression.getElseExpression());
|
||||||
|
}
|
||||||
|
checkImplicitCastOnConditionalExpression(whenExpression, branchExpressions);
|
||||||
|
}
|
||||||
|
|
||||||
if (whenExpression.getElseExpression() != null) continue;
|
if (whenExpression.getElseExpression() != null) continue;
|
||||||
|
|
||||||
BindingContext context = trace.getBindingContext();
|
BindingContext context = trace.getBindingContext();
|
||||||
@@ -761,6 +806,8 @@ public class ControlFlowInformationProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
+7
-17
@@ -91,6 +91,8 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public KotlinTypeInfo visitIfExpression(KtIfExpression ifExpression, ExpressionTypingContext contextWithExpectedType, boolean isStatement) {
|
public KotlinTypeInfo visitIfExpression(KtIfExpression ifExpression, ExpressionTypingContext contextWithExpectedType, boolean isStatement) {
|
||||||
|
components.dataFlowAnalyzer.recordExpectedType(contextWithExpectedType.trace, ifExpression, contextWithExpectedType.expectedType);
|
||||||
|
|
||||||
ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE);
|
ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE);
|
||||||
KtExpression condition = ifExpression.getCondition();
|
KtExpression condition = ifExpression.getCondition();
|
||||||
DataFlowInfo conditionDataFlowInfo = checkCondition(context.scope, condition, context);
|
DataFlowInfo conditionDataFlowInfo = checkCondition(context.scope, condition, context);
|
||||||
@@ -112,12 +114,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
? result.replaceJumpOutPossible(true).replaceJumpFlowInfo(conditionDataFlowInfo)
|
? result.replaceJumpOutPossible(true).replaceJumpFlowInfo(conditionDataFlowInfo)
|
||||||
: result;
|
: result;
|
||||||
}
|
}
|
||||||
return TypeInfoFactoryKt.createTypeInfo(components.dataFlowAnalyzer.checkImplicitCast(
|
return TypeInfoFactoryKt.createTypeInfo(components.builtIns.getUnitType(), thenInfo.or(elseInfo));
|
||||||
components.builtIns.getUnitType(), ifExpression,
|
|
||||||
contextWithExpectedType, isStatement
|
|
||||||
),
|
|
||||||
thenInfo.or(elseInfo)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (thenBranch == null) {
|
if (thenBranch == null) {
|
||||||
return getTypeInfoWhenOnlyOneBranchIsPresent(
|
return getTypeInfoWhenOnlyOneBranchIsPresent(
|
||||||
@@ -173,9 +170,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If break or continue was possible, take condition check info as the jump info
|
// If break or continue was possible, take condition check info as the jump info
|
||||||
return TypeInfoFactoryKt
|
return TypeInfoFactoryKt.createTypeInfo(resultType, resultDataFlowInfo, loopBreakContinuePossible, conditionDataFlowInfo);
|
||||||
.createTypeInfo(components.dataFlowAnalyzer.checkImplicitCast(resultType, ifExpression, contextWithExpectedType, isStatement),
|
|
||||||
resultDataFlowInfo, loopBreakContinuePossible, conditionDataFlowInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -199,15 +194,10 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
} else {
|
} else {
|
||||||
dataFlowInfo = typeInfo.getDataFlowInfo().or(otherInfo);
|
dataFlowInfo = typeInfo.getDataFlowInfo().or(otherInfo);
|
||||||
}
|
}
|
||||||
return components.dataFlowAnalyzer.checkImplicitCast(
|
return components.dataFlowAnalyzer.checkType(
|
||||||
components.dataFlowAnalyzer.checkType(
|
typeInfo.replaceType(components.builtIns.getUnitType()),
|
||||||
typeInfo.replaceType(components.builtIns.getUnitType()),
|
|
||||||
ifExpression,
|
|
||||||
context
|
|
||||||
),
|
|
||||||
ifExpression,
|
ifExpression,
|
||||||
context,
|
context
|
||||||
isStatement
|
|
||||||
).replaceDataFlowInfo(dataFlowInfo);
|
).replaceDataFlowInfo(dataFlowInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -271,29 +271,6 @@ public class DataFlowAnalyzer {
|
|||||||
return builtIns.getUnitType();
|
return builtIns.getUnitType();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public KotlinType checkImplicitCast(@Nullable KotlinType expressionType, @NotNull KtExpression expression, @NotNull ResolutionContext context, boolean isStatement) {
|
|
||||||
boolean isIfExpression = expression instanceof KtIfExpression;
|
|
||||||
if (expressionType != null
|
|
||||||
&& (context.expectedType == NO_EXPECTED_TYPE || isIfExpression)
|
|
||||||
&& context.contextDependency == INDEPENDENT && !isStatement
|
|
||||||
&& (KotlinBuiltIns.isUnit(expressionType) || KotlinBuiltIns.isAnyOrNullableAny(expressionType))
|
|
||||||
&& !DynamicTypesKt.isDynamic(expressionType)) {
|
|
||||||
if (isIfExpression && KotlinBuiltIns.isUnit(expressionType) || isIfExpression && context.expectedType != NO_EXPECTED_TYPE) {
|
|
||||||
return expressionType;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
context.trace.report(IMPLICIT_CAST_TO_UNIT_OR_ANY.on(expression, expressionType));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return expressionType;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public KotlinTypeInfo checkImplicitCast(@NotNull KotlinTypeInfo typeInfo, @NotNull KtExpression expression, @NotNull ResolutionContext context, boolean isStatement) {
|
|
||||||
return typeInfo.replaceType(checkImplicitCast(typeInfo.getType(), expression, context, isStatement));
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public KotlinTypeInfo illegalStatementType(@NotNull KtExpression expression, @NotNull ExpressionTypingContext context, @NotNull ExpressionTypingInternals facade) {
|
public KotlinTypeInfo illegalStatementType(@NotNull KtExpression expression, @NotNull ExpressionTypingContext context, @NotNull ExpressionTypingInternals facade) {
|
||||||
facade.checkStatementType(
|
facade.checkStatementType(
|
||||||
|
|||||||
+2
-5
@@ -162,12 +162,9 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
if (isExhaustive && expression.getElseExpression() == null && KotlinBuiltIns.isNothing(resultType)) {
|
if (isExhaustive && expression.getElseExpression() == null && KotlinBuiltIns.isNothing(resultType)) {
|
||||||
context.trace.record(BindingContext.IMPLICIT_EXHAUSTIVE_WHEN, expression);
|
context.trace.record(BindingContext.IMPLICIT_EXHAUSTIVE_WHEN, expression);
|
||||||
}
|
}
|
||||||
|
resultType = components.dataFlowAnalyzer.checkType(resultType, expression, contextWithExpectedType);
|
||||||
}
|
}
|
||||||
return TypeInfoFactoryKt.createTypeInfo(expressionTypes.isEmpty() ? null : components.dataFlowAnalyzer.checkType(
|
return TypeInfoFactoryKt.createTypeInfo(resultType,
|
||||||
components.dataFlowAnalyzer.checkImplicitCast(
|
|
||||||
resultType, expression,
|
|
||||||
contextWithExpectedType, isStatement),
|
|
||||||
expression, contextWithExpectedType),
|
|
||||||
commonDataFlowInfo,
|
commonDataFlowInfo,
|
||||||
loopBreakContinuePossible,
|
loopBreakContinuePossible,
|
||||||
contextWithExpectedType.dataFlowInfo);
|
contextWithExpectedType.dataFlowInfo);
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
val test1 = { <!IMPLICIT_CAST_TO_UNIT_OR_ANY!>if (true) 1 else ""<!> }
|
||||||
|
|
||||||
|
val test2 = { { <!IMPLICIT_CAST_TO_UNIT_OR_ANY!>if (true) 1 else ""<!> } }
|
||||||
|
|
||||||
|
val test3: (Boolean) -> Any = { if (it) 1 else "" }
|
||||||
|
|
||||||
|
val test4: (Boolean) -> Any? = { if (it) 1 else "" }
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public val test1: () -> kotlin.Any
|
||||||
|
public val test2: () -> () -> kotlin.Any
|
||||||
|
public val test3: (kotlin.Boolean) -> kotlin.Any
|
||||||
|
public val test4: (kotlin.Boolean) -> kotlin.Any?
|
||||||
+2
-2
@@ -17,12 +17,12 @@ fun example() {
|
|||||||
}();
|
}();
|
||||||
|
|
||||||
{
|
{
|
||||||
if (true) {} else false
|
<!IMPLICIT_CAST_TO_UNIT_OR_ANY!>if (true) {} else false<!>
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
if (true) true else {}
|
<!IMPLICIT_CAST_TO_UNIT_OR_ANY!>if (true) true else {}<!>
|
||||||
}()
|
}()
|
||||||
|
|
||||||
fun t(): Boolean {
|
fun t(): Boolean {
|
||||||
|
|||||||
+5
-5
@@ -18,13 +18,13 @@ val w = <!EXPRESSION_EXPECTED!>while (true) {}<!>
|
|||||||
|
|
||||||
fun foo() {
|
fun foo() {
|
||||||
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>z<!> = 2
|
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>z<!> = 2
|
||||||
val r = { // type fun(): Int is inferred
|
val r = { // type fun(): Any is inferred
|
||||||
if (true) {
|
<!IMPLICIT_CAST_TO_UNIT_OR_ANY!>if (true) {
|
||||||
2
|
2
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
z = 34
|
z = 34
|
||||||
}
|
}<!>
|
||||||
}
|
}
|
||||||
val <!UNUSED_VARIABLE!>f<!>: ()-> Int = <!TYPE_MISMATCH!>r<!>
|
val <!UNUSED_VARIABLE!>f<!>: ()-> Int = <!TYPE_MISMATCH!>r<!>
|
||||||
val <!UNUSED_VARIABLE!>g<!>: ()-> Any = r
|
val <!UNUSED_VARIABLE!>g<!>: ()-> Any = r
|
||||||
@@ -73,11 +73,11 @@ fun testCoercionToUnit() {
|
|||||||
|
|
||||||
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>x<!> = 43
|
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>x<!> = 43
|
||||||
val checkType = {
|
val checkType = {
|
||||||
if (true) {
|
<!IMPLICIT_CAST_TO_UNIT_OR_ANY!>if (true) {
|
||||||
x = 4
|
x = 4
|
||||||
} else {
|
} else {
|
||||||
45
|
45
|
||||||
}
|
}<!>
|
||||||
}
|
}
|
||||||
val <!UNUSED_VARIABLE!>f<!> : () -> String = <!TYPE_MISMATCH!>checkType<!>
|
val <!UNUSED_VARIABLE!>f<!> : () -> String = <!TYPE_MISMATCH!>checkType<!>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
val test1 = { <!IMPLICIT_CAST_TO_UNIT_OR_ANY!>when (true) { true -> 1; else -> "" }<!> }
|
||||||
|
|
||||||
|
val test2 = { { <!IMPLICIT_CAST_TO_UNIT_OR_ANY!>when (true) { true -> 1; else -> "" }<!> } }
|
||||||
|
|
||||||
|
val test3: (Boolean) -> Any = { when (true) { true -> 1; else -> "" } }
|
||||||
|
|
||||||
|
val test4: (Boolean) -> Any? = { when (true) { true -> 1; else -> "" } }
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public val test1: () -> kotlin.Any
|
||||||
|
public val test2: () -> () -> kotlin.Any
|
||||||
|
public val test3: (kotlin.Boolean) -> kotlin.Any
|
||||||
|
public val test4: (kotlin.Boolean) -> kotlin.Any?
|
||||||
@@ -3318,6 +3318,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ifInResultOfLambda.kt")
|
||||||
|
public void testIfInResultOfLambda() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/ifInResultOfLambda.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("ifWhenWithoutElse.kt")
|
@TestMetadata("ifWhenWithoutElse.kt")
|
||||||
public void testIfWhenWithoutElse() throws Exception {
|
public void testIfWhenWithoutElse() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/ifWhenWithoutElse.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/ifWhenWithoutElse.kt");
|
||||||
@@ -3402,6 +3408,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("whenInResultOfLambda.kt")
|
||||||
|
public void testWhenInResultOfLambda() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/whenInResultOfLambda.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("when.kt234.kt973.kt")
|
@TestMetadata("when.kt234.kt973.kt")
|
||||||
public void testWhen_kt234_kt973() throws Exception {
|
public void testWhen_kt234_kt973() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/when.kt234.kt973.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/when.kt234.kt973.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user