diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java index 8a9c533109a..0d42706a377 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java @@ -16,6 +16,7 @@ package org.jetbrains.jet.lang.types.expressions; +import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.intellij.psi.PsiElement; import com.intellij.psi.tree.IElementType; @@ -35,6 +36,7 @@ import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory; import org.jetbrains.jet.lang.resolve.calls.autocasts.Nullability; import org.jetbrains.jet.lang.resolve.calls.context.CheckValueArgumentsMode; import org.jetbrains.jet.lang.resolve.calls.context.ExpressionPosition; +import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallWithTrace; import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall; import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults; @@ -1031,25 +1033,22 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { JetExpression left = expression.getLeft(); JetExpression right = expression.getRight(); - JetTypeInfo leftTypeInfo = getTypeInfoOrNullType(left, context, facade); + if (left == null || right == null) { + getTypeInfoOrNullType(left, context, facade); + return JetTypeInfo.create(null, context.dataFlowInfo); + } + + Call call = createCallForSpecialConstruction(expression, Lists.newArrayList(left, right)); + ResolvedCall resolvedCall = resolveSpecialConstructionAsCall( + call, "Elvis", Lists.newArrayList("left", "right"), Lists.newArrayList(true, false), contextWithExpectedType, null); + JetTypeInfo leftTypeInfo = BindingContextUtils.getRecordedTypeInfo(left, context.trace.getBindingContext()); + assert leftTypeInfo != null : "Left expression was not processed: " + expression; JetType leftType = leftTypeInfo.getType(); - DataFlowInfo dataFlowInfo = leftTypeInfo.getDataFlowInfo(); - - if (left == null || leftType == null) return JetTypeInfo.create(null, dataFlowInfo); - - if (isKnownToBeNotNull(left, leftType, context)) { + if (leftType != null && isKnownToBeNotNull(left, leftType, context)) { context.trace.report(USELESS_ELVIS.on(left, leftType)); } - - ExpressionTypingContext newContext = contextWithExpectedType.replaceDataFlowInfo(dataFlowInfo).replaceScope(context.scope); - JetType rightType = right == null ? null : facade.getTypeInfo(right, newContext).getType(); - - if (rightType != null) { - DataFlowUtils.checkType(TypeUtils.makeNullableAsSpecified(leftType, rightType.isNullable()), left, contextWithExpectedType); - return JetTypeInfo.create(TypeUtils.makeNullableAsSpecified( - CommonSupertypes.commonSupertype(Arrays.asList(leftType, rightType)), rightType.isNullable()), dataFlowInfo); - } - return JetTypeInfo.create(null, dataFlowInfo); + return JetTypeInfo.create(resolvedCall.getResultingDescriptor().getReturnType(), + resolvedCall.getDataFlowInfoForArguments().getResultInfo()); } @NotNull diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingUtils.java index 6b397ef001d..a0aca7746cc 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingUtils.java @@ -250,6 +250,11 @@ public class ControlStructureTypingUtils { this.trace = trace; this.expectedType = expectedType; } + + CheckTypeContext makeTypeNullable() { + if (TypeUtils.noExpectedType(expectedType)) return this; + return new CheckTypeContext(trace, TypeUtils.makeNullable(expectedType)); + } } final JetVisitor checkTypeVisitor = new JetVisitor() { @@ -280,13 +285,22 @@ public class ControlStructureTypingUtils { @Override public Void visitPostfixExpression(JetPostfixExpression expression, CheckTypeContext c) { if (expression.getOperationReference().getReferencedNameElementType() == JetTokens.EXCLEXCL) { - checkExpressionType(expression.getBaseExpression(), - new CheckTypeContext(c.trace, TypeUtils.makeNullable(c.expectedType))); + checkExpressionType(expression.getBaseExpression(), c.makeTypeNullable()); return null; } return super.visitPostfixExpression(expression, c); } + @Override + public Void visitBinaryExpression(JetBinaryExpression expression, CheckTypeContext c) { + if (expression.getOperationReference().getReferencedNameElementType() == JetTokens.ELVIS) { + checkExpressionType(expression.getLeft(), c.makeTypeNullable()); + checkExpressionType(expression.getRight(), c); + return null; + } + return super.visitBinaryExpression(expression, c); + } + @Override public Void visitExpression(JetExpression expression, CheckTypeContext c) { JetTypeInfo typeInfo = BindingContextUtils.getRecordedTypeInfo(expression, c.trace.getBindingContext()); diff --git a/compiler/testData/diagnostics/tests/resolve/specialConstructions/elvisAsCall.kt b/compiler/testData/diagnostics/tests/resolve/specialConstructions/elvisAsCall.kt new file mode 100644 index 00000000000..81c06080111 --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/specialConstructions/elvisAsCall.kt @@ -0,0 +1,34 @@ +package a + +trait A +fun doList(l: List) = l +fun doInt(i: Int) = i +fun getList(): List? = null +fun strangeList(f: (T) -> Unit): List = throw Exception("$f") +fun emptyListOfA(): List = throw Exception() + +//------------------------------- + +fun testElvis(a: Int?, b: Int?) { + if (a != null) { + doInt(b ?: a) + } + doList(getList() ?: emptyListOfA()) //should be an error + doList(getList() ?: strangeList { doInt(it) }) //lambda was not analyzed +} + + +fun testDataFlowInfo1(a: Int?, b: Int?) { + val c: Int = a ?: b!! + doInt(c) + b + 1 +} + +fun testDataFlowInfo2(a: Int?, b: Int?) { + doInt(a ?: b!!) + b + 1 +} + +fun testTypeMismatch(a: String?, b: Any) { + doInt(a ?: b) +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index 96b77d2ba8e..03d85a16b9e 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -4793,6 +4793,11 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/resolve/specialConstructions"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("elvisAsCall.kt") + public void testElvisAsCall() throws Exception { + doTest("compiler/testData/diagnostics/tests/resolve/specialConstructions/elvisAsCall.kt"); + } + @TestMetadata("exclExclAsCall.kt") public void testExclExclAsCall() throws Exception { doTest("compiler/testData/diagnostics/tests/resolve/specialConstructions/exclExclAsCall.kt");