Don't generate UNNECESSARY_SAFE_CALL on variables of generic type <T: Any?>
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+43
-1
@@ -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
|
||||
|
||||
+13
@@ -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,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
|
||||
|
||||
Reference in New Issue
Block a user