through completion phase result type of call should be updated

with respect to being selector in safe call expression
This commit is contained in:
Svetlana Isakova
2013-08-02 18:18:48 +04:00
parent 4621fe6dfa
commit 7b04755a59
4 changed files with 74 additions and 9 deletions
@@ -28,9 +28,9 @@ import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.JetTypeInfo;
import org.jetbrains.jet.lang.types.TypeUtils;
import org.jetbrains.jet.util.slicedmap.ReadOnlySlice;
import org.jetbrains.jet.util.slicedmap.Slices;
@@ -274,14 +274,20 @@ public class BindingContextUtils {
trace.report(AMBIGUOUS_LABEL.on(targetLabel));
}
public static void updateRecordedType(
@Nullable
public static JetType updateRecordedType(
@Nullable JetType type,
@NotNull JetExpression expression,
@NotNull BindingTrace trace
@NotNull BindingTrace trace,
boolean shouldBeMadeNullable
) {
if (type == null) return;
if (type == null) return null;
if (shouldBeMadeNullable) {
type = TypeUtils.makeNullable(type);
}
trace.record(BindingContext.EXPRESSION_TYPE, expression, type);
trace.record(BindingContext.PROCESSED, expression);
return type;
}
@Nullable
@@ -48,6 +48,7 @@ import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
import org.jetbrains.jet.lang.types.expressions.DataFlowUtils;
import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.lexer.JetTokens;
import javax.inject.Inject;
import java.util.ArrayList;
@@ -367,8 +368,9 @@ public class CandidateResolver {
type = completeNestedCallsInference(contextForArgument);
checkValueArgumentTypes(contextForArgument);
}
BindingContextUtils.updateRecordedType(type, expression, context.trace);
DataFlowUtils.checkType(type, expression, contextForArgument);
JetType result = BindingContextUtils.updateRecordedType(
type, expression, context.trace, isFairSafeCallExpression(expression, context.trace));
DataFlowUtils.checkType(result, expression, contextForArgument);
}
@NotNull
@@ -388,6 +390,19 @@ public class CandidateResolver {
return expression.accept(selectorExpressionFinder, null);
}
private boolean isFairSafeCallExpression(@NotNull JetExpression expression, @NotNull BindingTrace trace) {
// We are interested in type of the last call:
// 'a.b?.foo()' is safe call, but 'a?.b.foo()' is not.
// Since receiver is 'a.b' and selector is 'foo()',
// we can only check if an expression is safe call.
if (!(expression instanceof JetSafeQualifiedExpression)) return false;
JetSafeQualifiedExpression safeQualifiedExpression = (JetSafeQualifiedExpression) expression;
//If a receiver type is not null, then this safe expression is useless, and we don't need to make the result type nullable.
JetType type = trace.get(BindingContext.EXPRESSION_TYPE, safeQualifiedExpression.getReceiverExpression());
return type != null && type.isNullable();
}
@Nullable
private <D extends CallableDescriptor> JetType updateResultArgumentTypeIfNotDenotable(
@NotNull CallCandidateResolutionContext<D> context,
@@ -398,7 +413,7 @@ public class CandidateResolver {
if (type.getConstructor() instanceof NumberValueTypeConstructor) {
NumberValueTypeConstructor constructor = (NumberValueTypeConstructor) type.getConstructor();
type = TypeUtils.getPrimitiveNumberType(constructor, context.expectedType);
BindingContextUtils.updateRecordedType(type, expression, context.trace);
BindingContextUtils.updateRecordedType(type, expression, context.trace, false);
}
}
return type;
@@ -537,8 +552,8 @@ public class CandidateResolver {
constraintSystem.addSubtypeConstraint(receiverType, receiverParameter.getType(), ConstraintPosition.RECEIVER_POSITION);
}
ConstraintSystem
constraintSystemWithRightTypeParameters = constraintSystem.replaceTypeVariables(new Function<TypeParameterDescriptor, TypeParameterDescriptor>() {
ConstraintSystem constraintSystemWithRightTypeParameters = constraintSystem.replaceTypeVariables(
new Function<TypeParameterDescriptor, TypeParameterDescriptor>() {
@Override
public TypeParameterDescriptor apply(@Nullable TypeParameterDescriptor typeParameterDescriptor) {
assert typeParameterDescriptor != null;
@@ -0,0 +1,39 @@
package a
trait A {
val b: B
val nb: B?
}
trait B {
fun foo(): Int
}
fun test(u: A?, x: A?, y: A?, z: A?, w: A, v: A?) {
u?.b?.foo()!! // was UNNECESSARY_SAFE_CALL everywhere, because result type (of 'foo()') wasn't made nullable
u!!.b<!UNNECESSARY_SAFE_CALL!>?.<!>foo()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
x?.b!!.foo()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
x!!.b<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.foo()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
y?.nb?.foo()!!
y!!.nb?.foo()!!
z?.nb!!.foo()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
z!!.nb!!.foo()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
w.b<!UNNECESSARY_SAFE_CALL!>?.<!>foo()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
w.b<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.foo()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
w.nb?.foo()!!
w.nb!!.foo()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
v!!.b.foo()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
}
fun B?.bar(): Int = 1
fun B?.baz(): Int? = 1
fun doInt(i: Int) = i
fun test(a: A?) {
doInt(a?.b.bar()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>)
doInt(a?.b.baz()!!)
}
@@ -2866,6 +2866,11 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
doTest("compiler/testData/diagnostics/tests/inference/nestedCalls/kt3461checkTypes.kt");
}
@TestMetadata("makeNullableIfSafeCall.kt")
public void testMakeNullableIfSafeCall() throws Exception {
doTest("compiler/testData/diagnostics/tests/inference/nestedCalls/makeNullableIfSafeCall.kt");
}
@TestMetadata("nontrivialCallExpression.kt")
public void testNontrivialCallExpression() throws Exception {
doTest("compiler/testData/diagnostics/tests/inference/nestedCalls/nontrivialCallExpression.kt");