Smart casts inside property chains like a?.b?.c.foo(a, b) are now handled correctly #KT-7290 Fixed
This commit is contained in:
+18
-4
@@ -21,7 +21,9 @@ import com.intellij.psi.PsiElement;
|
|||||||
import com.intellij.psi.util.PsiTreeUtil;
|
import com.intellij.psi.util.PsiTreeUtil;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens;
|
||||||
import org.jetbrains.kotlin.psi.*;
|
import org.jetbrains.kotlin.psi.*;
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||||
import org.jetbrains.kotlin.resolve.BindingTrace;
|
import org.jetbrains.kotlin.resolve.BindingTrace;
|
||||||
@@ -36,6 +38,8 @@ 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;
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue;
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory;
|
||||||
import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
|
import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
|
||||||
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject;
|
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject;
|
||||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
|
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
|
||||||
@@ -66,17 +70,20 @@ public class CallExpressionResolver {
|
|||||||
private final ConstantExpressionEvaluator constantExpressionEvaluator;
|
private final ConstantExpressionEvaluator constantExpressionEvaluator;
|
||||||
private final SymbolUsageValidator symbolUsageValidator;
|
private final SymbolUsageValidator symbolUsageValidator;
|
||||||
private final DataFlowAnalyzer dataFlowAnalyzer;
|
private final DataFlowAnalyzer dataFlowAnalyzer;
|
||||||
|
@NotNull private final KotlinBuiltIns builtIns;
|
||||||
|
|
||||||
public CallExpressionResolver(
|
public CallExpressionResolver(
|
||||||
@NotNull CallResolver callResolver,
|
@NotNull CallResolver callResolver,
|
||||||
@NotNull ConstantExpressionEvaluator constantExpressionEvaluator,
|
@NotNull ConstantExpressionEvaluator constantExpressionEvaluator,
|
||||||
@NotNull SymbolUsageValidator symbolUsageValidator,
|
@NotNull SymbolUsageValidator symbolUsageValidator,
|
||||||
@NotNull DataFlowAnalyzer dataFlowAnalyzer
|
@NotNull DataFlowAnalyzer dataFlowAnalyzer,
|
||||||
|
@NotNull KotlinBuiltIns builtIns
|
||||||
) {
|
) {
|
||||||
this.callResolver = callResolver;
|
this.callResolver = callResolver;
|
||||||
this.constantExpressionEvaluator = constantExpressionEvaluator;
|
this.constantExpressionEvaluator = constantExpressionEvaluator;
|
||||||
this.symbolUsageValidator = symbolUsageValidator;
|
this.symbolUsageValidator = symbolUsageValidator;
|
||||||
this.dataFlowAnalyzer = dataFlowAnalyzer;
|
this.dataFlowAnalyzer = dataFlowAnalyzer;
|
||||||
|
this.builtIns = builtIns;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExpressionTypingServices expressionTypingServices;
|
private ExpressionTypingServices expressionTypingServices;
|
||||||
@@ -152,9 +159,16 @@ public class CallExpressionResolver {
|
|||||||
context, "trace to resolve as variable", nameExpression);
|
context, "trace to resolve as variable", nameExpression);
|
||||||
KotlinType type =
|
KotlinType type =
|
||||||
getVariableType(nameExpression, receiver, callOperationNode, context.replaceTraceAndCache(temporaryForVariable), result);
|
getVariableType(nameExpression, receiver, callOperationNode, context.replaceTraceAndCache(temporaryForVariable), result);
|
||||||
// TODO: for a safe call, it's necessary to set receiver != null here, as inside ArgumentTypeResolver.analyzeArgumentsAndRecordTypes
|
// NB: we have duplicating code in ArgumentTypeResolver.
|
||||||
// Unfortunately it provokes problems with x?.y!!.foo() with the following x!!.bar():
|
// It would be better to do it in getSelectorTypeInfo, but it breaks call expression analysis
|
||||||
// x != null proceeds to successive statements
|
// (all safe calls become unnecessary after it)
|
||||||
|
// QualifierReceiver is a thing like Collections. which has no type or value
|
||||||
|
if (receiver.exists() && !(receiver instanceof QualifierReceiver)) {
|
||||||
|
DataFlowValue receiverDataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver, context);
|
||||||
|
if (callOperationNode != null && callOperationNode.getElementType() == KtTokens.SAFE_ACCESS) {
|
||||||
|
context = context.replaceDataFlowInfo(context.dataFlowInfo.disequate(receiverDataFlowValue, DataFlowValue.nullValue(builtIns)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (result[0]) {
|
if (result[0]) {
|
||||||
temporaryForVariable.commit();
|
temporaryForVariable.commit();
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// See KT-7290
|
||||||
|
class MyClass(val x: String?)
|
||||||
|
fun foo(y: MyClass?): Int {
|
||||||
|
// x here is smartcast but y is not
|
||||||
|
val z = y?.x?.subSequence(0, <!DEBUG_INFO_SMARTCAST!><!DEBUG_INFO_SMARTCAST!>y<!>.x<!>.length)
|
||||||
|
// !! is necessary here
|
||||||
|
y!!.x
|
||||||
|
return z?.length ?: -1
|
||||||
|
}
|
||||||
|
fun bar(y: MyClass?) {
|
||||||
|
y?.x!!.length
|
||||||
|
// !! is necessary here
|
||||||
|
y!!.x
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun bar(/*0*/ y: MyClass?): kotlin.Unit
|
||||||
|
public fun foo(/*0*/ y: MyClass?): kotlin.Int
|
||||||
|
|
||||||
|
public final class MyClass {
|
||||||
|
public constructor MyClass(/*0*/ x: kotlin.String?)
|
||||||
|
public final val x: kotlin.String?
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -15194,6 +15194,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("propertyChain.kt")
|
||||||
|
public void testPropertyChain() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/propertyChain.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("receiver.kt")
|
@TestMetadata("receiver.kt")
|
||||||
public void testReceiver() throws Exception {
|
public void testReceiver() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/receiver.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/receiver.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user