resolve elvis operator as call

This commit is contained in:
Svetlana Isakova
2013-08-02 15:19:28 +04:00
parent 624eaa5aa8
commit 4621fe6dfa
4 changed files with 70 additions and 18 deletions
@@ -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<FunctionDescriptor> 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
@@ -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<Void, CheckTypeContext> checkTypeVisitor = new JetVisitor<Void, CheckTypeContext>() {
@@ -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());
@@ -0,0 +1,34 @@
package a
trait A
fun doList(l: List<Int>) = l
fun doInt(i: Int) = i
fun getList(): List<Int>? = null
fun <T> strangeList(f: (T) -> Unit): List<T> = throw Exception("$f")
fun <T: A> emptyListOfA(): List<T> = throw Exception()
//-------------------------------
fun testElvis(a: Int?, b: Int?) {
if (a != null) {
doInt(b ?: a)
}
doList(getList() ?: <!TYPE_INFERENCE_UPPER_BOUND_VIOLATED!>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(<!TYPE_MISMATCH!>a<!> ?: <!TYPE_MISMATCH!>b<!>)
}
@@ -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");