initialDataFlowInfoForArguments introduced in CallExpressionResolver methods #KT-10175 Fixed
This commit is contained in:
+35
-17
@@ -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) {
|
||||||
|
|||||||
+1
-1
@@ -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()
|
||||||
}
|
}
|
||||||
+6
@@ -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<!>)
|
||||||
+12
@@ -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
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user