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.CheckArgumentTypesMode;
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext; import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache; 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.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults; import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults;
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsUtil; import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsUtil;
@@ -95,13 +96,15 @@ public class CallExpressionResolver {
} }
@Nullable @Nullable
public ResolvedCall<FunctionDescriptor> getResolvedCallForFunction( private ResolvedCall<FunctionDescriptor> getResolvedCallForFunction(
@NotNull Call call, @NotNull Call call,
@NotNull ResolutionContext context, @NotNull CheckArgumentTypesMode checkArguments, @NotNull ResolutionContext context, @NotNull CheckArgumentTypesMode checkArguments,
@NotNull boolean[] result @NotNull boolean[] result,
@NotNull DataFlowInfo initialDataFlowInfoForArguments
) { ) {
OverloadResolutionResults<FunctionDescriptor> results = callResolver.resolveFunctionCall( OverloadResolutionResults<FunctionDescriptor> results = callResolver.resolveFunctionCall(
BasicCallResolutionContext.create(context, call, checkArguments)); BasicCallResolutionContext.create(context, call, checkArguments,
new DataFlowInfoForArgumentsImpl(initialDataFlowInfoForArguments, call)));
if (!results.isNothing()) { if (!results.isNothing()) {
result[0] = true; result[0] = true;
return OverloadResolutionResultsUtil.getResultingCall(results, context.contextDependency); return OverloadResolutionResultsUtil.getResultingCall(results, context.contextDependency);
@@ -145,6 +148,15 @@ public class CallExpressionResolver {
public KotlinTypeInfo getSimpleNameExpressionTypeInfo( public KotlinTypeInfo getSimpleNameExpressionTypeInfo(
@NotNull KtSimpleNameExpression nameExpression, @Nullable Receiver receiver, @NotNull KtSimpleNameExpression nameExpression, @Nullable Receiver receiver,
@Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context @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]; boolean[] result = new boolean[1];
@@ -155,7 +167,7 @@ public class CallExpressionResolver {
if (result[0]) { if (result[0]) {
temporaryForVariable.commit(); temporaryForVariable.commit();
return TypeInfoFactoryKt.createTypeInfo(type, context); return TypeInfoFactoryKt.createTypeInfo(type, initialDataFlowInfoForArguments);
} }
Call call = CallMaker.makeCall(nameExpression, receiver, callOperationNode, nameExpression, Collections.<ValueArgument>emptyList()); 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); context, "trace to resolve as function", nameExpression);
ResolutionContext newContext = context.replaceTraceAndCache(temporaryForFunction); ResolutionContext newContext = context.replaceTraceAndCache(temporaryForFunction);
ResolvedCall<FunctionDescriptor> resolvedCall = getResolvedCallForFunction( ResolvedCall<FunctionDescriptor> resolvedCall = getResolvedCallForFunction(
call, newContext, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS, result); call, newContext, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS, result, initialDataFlowInfoForArguments);
if (result[0]) { if (result[0]) {
FunctionDescriptor functionDescriptor = resolvedCall != null ? resolvedCall.getResultingDescriptor() : null; FunctionDescriptor functionDescriptor = resolvedCall != null ? resolvedCall.getResultingDescriptor() : null;
if (!(functionDescriptor instanceof ConstructorDescriptor)) { if (!(functionDescriptor instanceof ConstructorDescriptor)) {
@@ -193,7 +205,8 @@ public class CallExpressionResolver {
@NotNull KtCallExpression callExpression, @Nullable ReceiverValue receiver, @NotNull KtCallExpression callExpression, @Nullable ReceiverValue receiver,
@Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context @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) { if (context.contextDependency == INDEPENDENT) {
dataFlowAnalyzer.checkType(typeInfo.getType(), callExpression, context); dataFlowAnalyzer.checkType(typeInfo.getType(), callExpression, context);
} }
@@ -207,7 +220,8 @@ public class CallExpressionResolver {
@NotNull @NotNull
private KotlinTypeInfo getCallExpressionTypeInfoWithoutFinalTypeCheck( private KotlinTypeInfo getCallExpressionTypeInfoWithoutFinalTypeCheck(
@NotNull KtCallExpression callExpression, @Nullable Receiver receiver, @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]; boolean[] result = new boolean[1];
Call call = CallMaker.makeCall(receiver, callOperationNode, callExpression); Call call = CallMaker.makeCall(receiver, callOperationNode, callExpression);
@@ -217,7 +231,9 @@ public class CallExpressionResolver {
ResolvedCall<FunctionDescriptor> resolvedCall = getResolvedCallForFunction( ResolvedCall<FunctionDescriptor> resolvedCall = getResolvedCallForFunction(
call, call,
context.replaceTraceAndCache(temporaryForFunction), context.replaceTraceAndCache(temporaryForFunction),
CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS, result); CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS,
result,
initialDataFlowInfoForArguments);
if (result[0]) { if (result[0]) {
FunctionDescriptor functionDescriptor = resolvedCall != null ? resolvedCall.getResultingDescriptor() : null; FunctionDescriptor functionDescriptor = resolvedCall != null ? resolvedCall.getResultingDescriptor() : null;
temporaryForFunction.commit(); temporaryForFunction.commit();
@@ -311,14 +327,16 @@ public class CallExpressionResolver {
@NotNull Receiver receiver, @NotNull Receiver receiver,
@Nullable ASTNode callOperationNode, @Nullable ASTNode callOperationNode,
@Nullable KtExpression selectorExpression, @Nullable KtExpression selectorExpression,
@NotNull ExpressionTypingContext context @NotNull ExpressionTypingContext context,
@NotNull DataFlowInfo initialDataFlowInfoForArguments
) { ) {
if (selectorExpression instanceof KtCallExpression) { if (selectorExpression instanceof KtCallExpression) {
return getCallExpressionTypeInfoWithoutFinalTypeCheck((KtCallExpression) selectorExpression, receiver, return getCallExpressionTypeInfoWithoutFinalTypeCheck((KtCallExpression) selectorExpression, receiver,
callOperationNode, context); callOperationNode, context, initialDataFlowInfoForArguments);
} }
else if (selectorExpression instanceof KtSimpleNameExpression) { else if (selectorExpression instanceof KtSimpleNameExpression) {
return getSimpleNameExpressionTypeInfo((KtSimpleNameExpression) selectorExpression, receiver, callOperationNode, context); return getSimpleNameExpressionTypeInfo(
(KtSimpleNameExpression) selectorExpression, receiver, callOperationNode, context, initialDataFlowInfoForArguments);
} }
else if (selectorExpression != null) { else if (selectorExpression != null) {
expressionTypingServices.getTypeInfo(selectorExpression, context); expressionTypingServices.getTypeInfo(selectorExpression, context);
@@ -422,15 +440,15 @@ public class CallExpressionResolver {
contextForSelector = contextForSelector.replaceDataFlowInfo(receiverDataFlowInfo); contextForSelector = contextForSelector.replaceDataFlowInfo(receiverDataFlowInfo);
} }
DataFlowInfo initialDataFlowInfoForArguments = contextForSelector.dataFlowInfo;
if (receiver instanceof ReceiverValue) { if (receiver instanceof ReceiverValue) {
DataFlowValue receiverDataFlowValue = DataFlowValueFactory.createDataFlowValue((ReceiverValue) receiver, context); DataFlowValue receiverDataFlowValue = DataFlowValueFactory.createDataFlowValue((ReceiverValue) receiver, context);
// Additional "receiver != null" information // Additional "receiver != null" information
// Should be applied if we consider a safe call // Should be applied if we consider a safe call
if (element.getSafe()) { if (element.getSafe()) {
DataFlowInfo dataFlowInfo = contextForSelector.dataFlowInfo; if (initialDataFlowInfoForArguments.getPredictableNullability(receiverDataFlowValue).canBeNull()) {
if (dataFlowInfo.getPredictableNullability(receiverDataFlowValue).canBeNull()) { initialDataFlowInfoForArguments = initialDataFlowInfoForArguments.disequate(
contextForSelector = contextForSelector.replaceDataFlowInfo( receiverDataFlowValue, DataFlowValue.nullValue(builtIns));
dataFlowInfo.disequate(receiverDataFlowValue, DataFlowValue.nullValue(builtIns)));
} }
else { else {
reportUnnecessarySafeCall(trace, receiverType, element.getNode(), receiver); reportUnnecessarySafeCall(trace, receiverType, element.getNode(), receiver);
@@ -439,8 +457,8 @@ public class CallExpressionResolver {
} }
KtExpression selectorExpression = element.getSelector(); KtExpression selectorExpression = element.getSelector();
KotlinTypeInfo selectorReturnTypeInfo = KotlinTypeInfo selectorReturnTypeInfo = getSelectorReturnTypeInfo(
getSelectorReturnTypeInfo(receiver, element.getNode(), selectorExpression, contextForSelector); receiver, element.getNode(), selectorExpression, contextForSelector, initialDataFlowInfoForArguments);
KotlinType selectorReturnType = selectorReturnTypeInfo.getType(); KotlinType selectorReturnType = selectorReturnTypeInfo.getType();
if (qualifierReceiver != null) { if (qualifierReceiver != null) {
@@ -9,5 +9,5 @@ fun <T> T.testThis(): String? {
if (this != null) { if (this != null) {
return this<!UNNECESSARY_SAFE_CALL!>?.<!>toString() 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); 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") @TestMetadata("twoArgs.kt")
public void testTwoArgs() throws Exception { public void testTwoArgs() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/twoArgs.kt"); 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?) { 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() a<info>?.</info>foo()