initialDataFlowInfoForArguments introduced in CallExpressionResolver methods #KT-10175 Fixed

This commit is contained in:
Mikhail Glukhikh
2016-01-11 14:42:40 +03:00
parent 16d97ab72c
commit 0f80df7b2e
6 changed files with 61 additions and 19 deletions
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext;
import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode;
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache;
import org.jetbrains.kotlin.resolve.calls.model.DataFlowInfoForArgumentsImpl;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults;
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsUtil;
@@ -95,13 +96,15 @@ public class CallExpressionResolver {
}
@Nullable
public ResolvedCall<FunctionDescriptor> getResolvedCallForFunction(
private ResolvedCall<FunctionDescriptor> getResolvedCallForFunction(
@NotNull Call call,
@NotNull ResolutionContext context, @NotNull CheckArgumentTypesMode checkArguments,
@NotNull boolean[] result
@NotNull boolean[] result,
@NotNull DataFlowInfo initialDataFlowInfoForArguments
) {
OverloadResolutionResults<FunctionDescriptor> results = callResolver.resolveFunctionCall(
BasicCallResolutionContext.create(context, call, checkArguments));
BasicCallResolutionContext.create(context, call, checkArguments,
new DataFlowInfoForArgumentsImpl(initialDataFlowInfoForArguments, call)));
if (!results.isNothing()) {
result[0] = true;
return OverloadResolutionResultsUtil.getResultingCall(results, context.contextDependency);
@@ -145,6 +148,15 @@ public class CallExpressionResolver {
public KotlinTypeInfo getSimpleNameExpressionTypeInfo(
@NotNull KtSimpleNameExpression nameExpression, @Nullable Receiver receiver,
@Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context
) {
return getSimpleNameExpressionTypeInfo(nameExpression, receiver, callOperationNode, context, context.dataFlowInfo);
}
@NotNull
private KotlinTypeInfo getSimpleNameExpressionTypeInfo(
@NotNull KtSimpleNameExpression nameExpression, @Nullable Receiver receiver,
@Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context,
@NotNull DataFlowInfo initialDataFlowInfoForArguments
) {
boolean[] result = new boolean[1];
@@ -155,7 +167,7 @@ public class CallExpressionResolver {
if (result[0]) {
temporaryForVariable.commit();
return TypeInfoFactoryKt.createTypeInfo(type, context);
return TypeInfoFactoryKt.createTypeInfo(type, initialDataFlowInfoForArguments);
}
Call call = CallMaker.makeCall(nameExpression, receiver, callOperationNode, nameExpression, Collections.<ValueArgument>emptyList());
@@ -163,7 +175,7 @@ public class CallExpressionResolver {
context, "trace to resolve as function", nameExpression);
ResolutionContext newContext = context.replaceTraceAndCache(temporaryForFunction);
ResolvedCall<FunctionDescriptor> resolvedCall = getResolvedCallForFunction(
call, newContext, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS, result);
call, newContext, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS, result, initialDataFlowInfoForArguments);
if (result[0]) {
FunctionDescriptor functionDescriptor = resolvedCall != null ? resolvedCall.getResultingDescriptor() : null;
if (!(functionDescriptor instanceof ConstructorDescriptor)) {
@@ -193,7 +205,8 @@ public class CallExpressionResolver {
@NotNull KtCallExpression callExpression, @Nullable ReceiverValue receiver,
@Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context
) {
KotlinTypeInfo typeInfo = getCallExpressionTypeInfoWithoutFinalTypeCheck(callExpression, receiver, callOperationNode, context);
KotlinTypeInfo typeInfo = getCallExpressionTypeInfoWithoutFinalTypeCheck(
callExpression, receiver, callOperationNode, context, context.dataFlowInfo);
if (context.contextDependency == INDEPENDENT) {
dataFlowAnalyzer.checkType(typeInfo.getType(), callExpression, context);
}
@@ -207,7 +220,8 @@ public class CallExpressionResolver {
@NotNull
private KotlinTypeInfo getCallExpressionTypeInfoWithoutFinalTypeCheck(
@NotNull KtCallExpression callExpression, @Nullable Receiver receiver,
@Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context
@Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context,
@NotNull DataFlowInfo initialDataFlowInfoForArguments
) {
boolean[] result = new boolean[1];
Call call = CallMaker.makeCall(receiver, callOperationNode, callExpression);
@@ -217,7 +231,9 @@ public class CallExpressionResolver {
ResolvedCall<FunctionDescriptor> resolvedCall = getResolvedCallForFunction(
call,
context.replaceTraceAndCache(temporaryForFunction),
CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS, result);
CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS,
result,
initialDataFlowInfoForArguments);
if (result[0]) {
FunctionDescriptor functionDescriptor = resolvedCall != null ? resolvedCall.getResultingDescriptor() : null;
temporaryForFunction.commit();
@@ -311,14 +327,16 @@ public class CallExpressionResolver {
@NotNull Receiver receiver,
@Nullable ASTNode callOperationNode,
@Nullable KtExpression selectorExpression,
@NotNull ExpressionTypingContext context
@NotNull ExpressionTypingContext context,
@NotNull DataFlowInfo initialDataFlowInfoForArguments
) {
if (selectorExpression instanceof KtCallExpression) {
return getCallExpressionTypeInfoWithoutFinalTypeCheck((KtCallExpression) selectorExpression, receiver,
callOperationNode, context);
callOperationNode, context, initialDataFlowInfoForArguments);
}
else if (selectorExpression instanceof KtSimpleNameExpression) {
return getSimpleNameExpressionTypeInfo((KtSimpleNameExpression) selectorExpression, receiver, callOperationNode, context);
return getSimpleNameExpressionTypeInfo(
(KtSimpleNameExpression) selectorExpression, receiver, callOperationNode, context, initialDataFlowInfoForArguments);
}
else if (selectorExpression != null) {
expressionTypingServices.getTypeInfo(selectorExpression, context);
@@ -422,15 +440,15 @@ public class CallExpressionResolver {
contextForSelector = contextForSelector.replaceDataFlowInfo(receiverDataFlowInfo);
}
DataFlowInfo initialDataFlowInfoForArguments = contextForSelector.dataFlowInfo;
if (receiver instanceof ReceiverValue) {
DataFlowValue receiverDataFlowValue = DataFlowValueFactory.createDataFlowValue((ReceiverValue) receiver, context);
// Additional "receiver != null" information
// Should be applied if we consider a safe call
if (element.getSafe()) {
DataFlowInfo dataFlowInfo = contextForSelector.dataFlowInfo;
if (dataFlowInfo.getPredictableNullability(receiverDataFlowValue).canBeNull()) {
contextForSelector = contextForSelector.replaceDataFlowInfo(
dataFlowInfo.disequate(receiverDataFlowValue, DataFlowValue.nullValue(builtIns)));
if (initialDataFlowInfoForArguments.getPredictableNullability(receiverDataFlowValue).canBeNull()) {
initialDataFlowInfoForArguments = initialDataFlowInfoForArguments.disequate(
receiverDataFlowValue, DataFlowValue.nullValue(builtIns));
}
else {
reportUnnecessarySafeCall(trace, receiverType, element.getNode(), receiver);
@@ -439,8 +457,8 @@ public class CallExpressionResolver {
}
KtExpression selectorExpression = element.getSelector();
KotlinTypeInfo selectorReturnTypeInfo =
getSelectorReturnTypeInfo(receiver, element.getNode(), selectorExpression, contextForSelector);
KotlinTypeInfo selectorReturnTypeInfo = getSelectorReturnTypeInfo(
receiver, element.getNode(), selectorExpression, contextForSelector, initialDataFlowInfoForArguments);
KotlinType selectorReturnType = selectorReturnTypeInfo.getType();
if (qualifierReceiver != null) {
@@ -9,5 +9,5 @@ fun <T> T.testThis(): String? {
if (this != null) {
return this<!UNNECESSARY_SAFE_CALL!>?.<!>toString()
}
return this?.toString()
return <!DEBUG_INFO_CONSTANT!>this<!>?.toString()
}
@@ -0,0 +1,6 @@
fun Any?.foo(my: My) = my === this
class My(val x: Any)
// my is nullable in brackets because Any?.foo has nullable receiver
fun foo(my: My?) = my?.x.foo(<!TYPE_MISMATCH!>my<!>)
@@ -0,0 +1,12 @@
package
public fun foo(/*0*/ my: My?): kotlin.Boolean
public fun kotlin.Any?.foo(/*0*/ my: My): kotlin.Boolean
public final class My {
public constructor My(/*0*/ x: kotlin.Any)
public final val x: kotlin.Any
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -16652,6 +16652,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("simpleNullableReceiver.kt")
public void testSimpleNullableReceiver() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/simpleNullableReceiver.kt");
doTest(fileName);
}
@TestMetadata("twoArgs.kt")
public void testTwoArgs() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/twoArgs.kt");
+1 -1
View File
@@ -20,7 +20,7 @@ fun test(a: A?) {
}
if (a is B? && a is C?) {
<info descr="Smart cast to B">a</info><info>?.</info>bar()
<info descr="Smart cast to B?">a</info><info>?.</info>bar()
}
a<info>?.</info>foo()