Don't generate UNNECESSARY_SAFE_CALL on variables of generic type <T: Any?>

This commit is contained in:
Andrey Breslav
2012-10-25 12:00:27 +04:00
parent 3e38870ecc
commit b5385788a3
6 changed files with 70 additions and 6 deletions
@@ -17,7 +17,6 @@
package org.jetbrains.jet.lang.resolve.calls;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -31,6 +30,8 @@ import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.*;
import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastServiceImpl;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValue;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory;
import org.jetbrains.jet.lang.resolve.calls.inference.*;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
@@ -891,11 +892,13 @@ public class CallResolver {
ReceiverDescriptor receiverParameter, ReceiverDescriptor receiverArgument,
boolean isExplicitReceiver, boolean implicitInvokeCheck) {
BindingContext bindingContext = context.candidateCall.getTrace().getBindingContext();
ResolutionStatus result = SUCCESS;
if (receiverParameter.exists() && receiverArgument.exists()) {
boolean safeAccess = isExplicitReceiver && !implicitInvokeCheck && candidateCall.isSafeCall();
JetType receiverArgumentType = receiverArgument.getType();
AutoCastServiceImpl autoCastService = new AutoCastServiceImpl(context.dataFlowInfo, context.candidateCall.getTrace().getBindingContext());
AutoCastServiceImpl autoCastService = new AutoCastServiceImpl(context.dataFlowInfo, bindingContext);
if (!safeAccess && !receiverParameter.getType().isNullable() && !autoCastService.isNotNull(receiverArgument)) {
context.tracing.unsafeCall(context.candidateCall.getTrace(), receiverArgumentType, implicitInvokeCheck);
@@ -912,7 +915,8 @@ public class CallResolver {
result = OTHER_ERROR;
}
}
if (safeAccess && !receiverArgumentType.isNullable()) {
DataFlowValue receiverValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(receiverArgument, bindingContext);
if (safeAccess && !context.dataFlowInfo.getNullability(receiverValue).canBeNull()) {
context.tracing.unnecessarySafeCall(context.candidateCall.getTrace(), receiverArgumentType);
}
}
@@ -23,7 +23,7 @@ import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.JetModuleUtil;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiverDescriptor;
import org.jetbrains.jet.lang.resolve.scopes.receivers.*;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeUtils;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
@@ -61,6 +61,48 @@ public class DataFlowValueFactory {
return new DataFlowValue(variableDescriptor, type, isStableVariable(variableDescriptor), getImmanentNullability(type));
}
@NotNull
public DataFlowValue createDataFlowValue(@NotNull ReceiverDescriptor receiverDescriptor, @NotNull BindingContext bindingContext) {
return receiverDescriptor.accept(new ReceiverDescriptorVisitor<DataFlowValue, BindingContext>() {
@Override
public DataFlowValue visitNoReceiver(ReceiverDescriptor noReceiver, BindingContext data) {
throw new IllegalArgumentException("No DataFlowValue exists for ReceiverDescriptor.NO_RECEIVER");
}
@Override
public DataFlowValue visitExtensionReceiver(ExtensionReceiver receiver, BindingContext data) {
return createDataFlowValue(receiver);
}
@Override
public DataFlowValue visitExpressionReceiver(ExpressionReceiver receiver, BindingContext bindingContext) {
return createDataFlowValue(receiver.getExpression(), receiver.getType(), bindingContext);
}
@Override
public DataFlowValue visitClassReceiver(ClassReceiver receiver, BindingContext data) {
return createDataFlowValue(receiver);
}
@Override
public DataFlowValue visitTransientReceiver(TransientReceiver receiver, BindingContext data) {
return createTransientDataFlowValue(receiver);
}
@Override
public DataFlowValue visitScriptReceiver(ScriptReceiver receiver, BindingContext data) {
return createTransientDataFlowValue(receiver);
}
@NotNull
private DataFlowValue createTransientDataFlowValue(ReceiverDescriptor receiver) {
JetType type = receiver.getType();
boolean nullable = type.isNullable() || TypeUtils.hasNullableSuperType(type);
return new DataFlowValue(receiver, type, nullable, Nullability.NOT_NULL);
}
}, bindingContext);
}
private Nullability getImmanentNullability(JetType type) {
return type.isNullable() || TypeUtils.hasNullableSuperType(type) ? Nullability.UNKNOWN : Nullability.NOT_NULL;
}
@@ -4,7 +4,7 @@ package outer
fun Int?.optint() : Unit {}
val Int?.optval : Unit = Unit.VALUE
fun <T, E> T.foo(<!UNUSED_PARAMETER!>x<!> : E, y : A) : T {
fun <T: Any, E> T.foo(<!UNUSED_PARAMETER!>x<!> : E, y : A) : T {
y.plus(1)
y plus 1
y + 1.0
@@ -0,0 +1,13 @@
fun <T> test(t: T): String {
if (t != null) {
return t<!UNNECESSARY_SAFE_CALL!>?.<!>toString()
}
return t?.toString()
}
fun <T> T.testThis(): String {
if (this != null) {
return this<!UNNECESSARY_SAFE_CALL!>?.<!>toString()
}
return this?.toString()
}
@@ -2319,6 +2319,11 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
doTest("compiler/testData/diagnostics/tests/nullableTypes/redundantNullableInSupertype.kt");
}
@TestMetadata("safeCallOnTypeWithNullableUpperBound.kt")
public void testSafeCallOnTypeWithNullableUpperBound() throws Exception {
doTest("compiler/testData/diagnostics/tests/nullableTypes/safeCallOnTypeWithNullableUpperBound.kt");
}
}
@TestMetadata("compiler/testData/diagnostics/tests/objects")
+1 -1
View File
@@ -1,7 +1,7 @@
fun Int?.optint() : Unit {}
val Int?.optval : Unit = Unit.VALUE
fun <T, E> T.foo(<warning>x</warning> : E, y : A) : T {
fun <T: Any, E> T.foo(<warning>x</warning> : E, y : A) : T {
y.plus(1)
y plus 1
y + 1.0