through completion phase result type of call should be updated
with respect to being selector in safe call expression
This commit is contained in:
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user