Implementation and tests for KT-5840 and newly created KT-7204. Now a safe call provides not-null receiver state *inside* argument list. It works also for ?. chains. #KT-5840 Fixed.

On the other hand, argument states do not propagate to successor statements for a safe call. #KT-7204 Fixed. A few additional comments.
This commit is contained in:
Mikhail Glukhikh
2015-03-24 17:03:01 +03:00
parent 79739b7090
commit d92ccad35d
53 changed files with 566 additions and 63 deletions
@@ -28,14 +28,20 @@ import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext;
import org.jetbrains.kotlin.resolve.calls.context.CheckValueArgumentsMode;
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
import org.jetbrains.kotlin.resolve.calls.model.MutableDataFlowInfoForArguments;
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.constants.IntegerValueTypeConstructor;
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.kotlin.resolve.scopes.JetScope;
import org.jetbrains.kotlin.resolve.scopes.receivers.QualifierReceiver;
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.kotlin.types.JetType;
import org.jetbrains.kotlin.types.JetTypeInfo;
import org.jetbrains.kotlin.types.TypeUtils;
import org.jetbrains.kotlin.types.checker.JetTypeChecker;
import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilPackage;
import javax.inject.Inject;
import java.util.Collections;
@@ -251,17 +257,36 @@ public class ArgumentTypeResolver {
return defaultValue;
}
/**
* Visits function call arguments and determines data flow information changes
*/
public void analyzeArgumentsAndRecordTypes(
@NotNull CallResolutionContext<?> context
) {
MutableDataFlowInfoForArguments infoForArguments = context.dataFlowInfoForArguments;
infoForArguments.setInitialDataFlowInfo(context.dataFlowInfo);
Call call = context.call;
ReceiverValue receiver = call.getExplicitReceiver();
DataFlowInfo initialDataFlowInfo = context.dataFlowInfo;
// QualifierReceiver is a thing like Collections. which has no type or value
if (receiver.exists() && !(receiver instanceof QualifierReceiver)) {
DataFlowValue receiverDataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver, context);
// Additional "receiver != null" information for KT-5840
// Should be applied if we consider a safe call
// For an unsafe call, we should not do it,
// otherwise not-null will propagate to successive statements
// Sample: x?.foo(x.bar()) // Inside foo call, x is not-nullable
if (PsiUtilPackage.isSafeCall(call)) {
initialDataFlowInfo = initialDataFlowInfo.disequate(receiverDataFlowValue, DataFlowValue.NULL);
}
}
infoForArguments.setInitialDataFlowInfo(initialDataFlowInfo);
for (ValueArgument argument : context.call.getValueArguments()) {
for (ValueArgument argument : call.getValueArguments()) {
JetExpression expression = argument.getArgumentExpression();
if (expression == null) continue;
CallResolutionContext<?> newContext = context.replaceDataFlowInfo(infoForArguments.getInfo(argument));
// Here we go inside arguments and determine additional data flow information for them
JetTypeInfo typeInfoForCall = getArgumentTypeInfo(expression, newContext, SHAPE_FUNCTION_ARGUMENTS);
infoForArguments.updateInfo(argument, typeInfoForCall.getDataFlowInfo());
}
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults;
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsUtil;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
@@ -86,7 +87,8 @@ public class CallExpressionResolver {
}
@Nullable
private JetType getVariableType(@NotNull JetSimpleNameExpression nameExpression, @NotNull ReceiverValue receiver,
private JetType getVariableType(
@NotNull JetSimpleNameExpression nameExpression, @NotNull ReceiverValue receiver,
@Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context, @NotNull boolean[] result
) {
TemporaryTraceAndCache temporaryForVariable = TemporaryTraceAndCache.create(
@@ -124,17 +126,24 @@ public class CallExpressionResolver {
}
@NotNull
public JetTypeInfo getSimpleNameExpressionTypeInfo(@NotNull JetSimpleNameExpression nameExpression, @NotNull ReceiverValue receiver,
public JetTypeInfo getSimpleNameExpressionTypeInfo(
@NotNull JetSimpleNameExpression nameExpression, @NotNull ReceiverValue receiver,
@Nullable ASTNode callOperationNode, @NotNull ExpressionTypingContext context
) {
boolean[] result = new boolean[1];
TemporaryTraceAndCache temporaryForVariable = TemporaryTraceAndCache.create(
context, "trace to resolve as variable", nameExpression);
JetType type = getVariableType(nameExpression, receiver, callOperationNode, context.replaceTraceAndCache(temporaryForVariable), result);
JetType type =
getVariableType(nameExpression, receiver, callOperationNode, context.replaceTraceAndCache(temporaryForVariable), result);
DataFlowInfo dataFlowInfo = context.dataFlowInfo;
// TODO: for a safe call, it's necessary to set receiver != null here, as inside ArgumentTypeResolver.analyzeArgumentsAndRecordTypes
// Unfortunately it provokes problems with x?.y!!.foo() with the following x!!.bar():
// x != null proceeds to successive statements
if (result[0]) {
temporaryForVariable.commit();
return JetTypeInfo.create(type, context.dataFlowInfo);
return JetTypeInfo.create(type, dataFlowInfo);
}
Call call = CallMaker.makeCall(nameExpression, receiver, callOperationNode, nameExpression, Collections.<ValueArgument>emptyList());
@@ -149,11 +158,11 @@ public class CallExpressionResolver {
boolean hasValueParameters = functionDescriptor == null || functionDescriptor.getValueParameters().size() > 0;
context.trace.report(FUNCTION_CALL_EXPECTED.on(nameExpression, nameExpression, hasValueParameters));
type = functionDescriptor != null ? functionDescriptor.getReturnType() : null;
return JetTypeInfo.create(type, context.dataFlowInfo);
return JetTypeInfo.create(type, dataFlowInfo);
}
temporaryForVariable.commit();
return JetTypeInfo.create(null, context.dataFlowInfo);
return JetTypeInfo.create(null, dataFlowInfo);
}
@NotNull
@@ -168,6 +177,10 @@ public class CallExpressionResolver {
return typeInfo;
}
/**
* Visits a call expression and its arguments.
* Determines the result type and data flow information after the call.
*/
@NotNull
public JetTypeInfo getCallExpressionTypeInfoWithoutFinalTypeCheck(
@NotNull JetCallExpression callExpression, @NotNull ReceiverValue receiver,
@@ -179,7 +192,9 @@ public class CallExpressionResolver {
TemporaryTraceAndCache temporaryForFunction = TemporaryTraceAndCache.create(
context, "trace to resolve as function call", callExpression);
ResolvedCall<FunctionDescriptor> resolvedCall = getResolvedCallForFunction(
call, callExpression, context.replaceTraceAndCache(temporaryForFunction),
call, callExpression,
// It's possible start of a call so we should reset safe call chain
context.replaceTraceAndCache(temporaryForFunction).replaceInsideCallChain(false),
CheckValueArgumentsMode.ENABLED, result);
if (result[0]) {
FunctionDescriptor functionDescriptor = resolvedCall != null ? resolvedCall.getResultingDescriptor() : null;
@@ -192,7 +207,8 @@ public class CallExpressionResolver {
if (functionDescriptor == null) {
return JetTypeInfo.create(null, context.dataFlowInfo);
}
if (functionDescriptor instanceof ConstructorDescriptor && DescriptorUtils.isAnnotationClass(functionDescriptor.getContainingDeclaration())) {
if (functionDescriptor instanceof ConstructorDescriptor &&
DescriptorUtils.isAnnotationClass(functionDescriptor.getContainingDeclaration())) {
if (!canInstantiateAnnotationClass(callExpression)) {
context.trace.report(ANNOTATION_CLASS_CONSTRUCTOR_CALL.on(callExpression));
}
@@ -256,6 +272,36 @@ public class CallExpressionResolver {
return JetTypeInfo.create(null, context.dataFlowInfo);
}
/**
* Extended variant of JetTypeInfo stores additional information
* about data flow info from the left-more receiver, e.g. x != null for
* foo(x!!)?.bar(y!!)?.baz()
*/
private static class JetTypeInfoInsideSafeCall extends JetTypeInfo {
private final DataFlowInfo safeCallChainInfo;
private JetTypeInfoInsideSafeCall(@Nullable JetType type, @NotNull DataFlowInfo dataFlowInfo, @Nullable DataFlowInfo safeCallChainInfo) {
super(type, dataFlowInfo);
this.safeCallChainInfo = safeCallChainInfo;
}
/**
* Returns safe call chain information which is taken from the left-most receiver of a chain
* foo(x!!)?.bar(y!!)?.gav() ==> x != null is safe call chain information
*/
@Nullable
public DataFlowInfo getSafeCallChainInfo() {
return safeCallChainInfo;
}
}
/**
* Visits a qualified expression like x.y or x?.z controlling data flow information changes.
*
* @return qualified expression type together with data flow information
*/
@NotNull
public JetTypeInfo getQualifiedExpressionTypeInfo(
@NotNull JetQualifiedExpression expression, @NotNull ExpressionTypingContext context
@@ -263,16 +309,23 @@ public class CallExpressionResolver {
// TODO : functions as values
JetExpression selectorExpression = expression.getSelectorExpression();
JetExpression receiverExpression = expression.getReceiverExpression();
ResolutionContext contextForReceiver = context.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT);
boolean safeCall = (expression.getOperationSign() == JetTokens.SAFE_ACCESS);
ResolutionContext contextForReceiver = context.replaceExpectedType(NO_EXPECTED_TYPE).
replaceContextDependency(INDEPENDENT).
replaceInsideCallChain(true); // Enter call chain
// Visit receiver (x in x.y or x?.z) here. Recursion is possible.
JetTypeInfo receiverTypeInfo = expressionTypingServices.getTypeInfo(receiverExpression, contextForReceiver);
JetType receiverType = receiverTypeInfo.getType();
QualifierReceiver qualifierReceiver = (QualifierReceiver) context.trace.get(BindingContext.QUALIFIER, receiverExpression);
if (receiverType == null) receiverType = ErrorUtils.createErrorType("Type for " + expression.getText());
context = context.replaceDataFlowInfo(receiverTypeInfo.getDataFlowInfo());
ReceiverValue receiver = qualifierReceiver == null ? new ExpressionReceiver(receiverExpression, receiverType) : qualifierReceiver;
DataFlowInfo receiverDataFlowInfo = receiverTypeInfo.getDataFlowInfo();
// Receiver changes should be always applied, at least for argument analysis
context = context.replaceDataFlowInfo(receiverDataFlowInfo);
// Visit selector (y in x.y) here. Recursion is also possible.
JetTypeInfo selectorReturnTypeInfo = getSelectorReturnTypeInfo(
receiver, expression.getOperationTokenNode(), selectorExpression, context);
JetType selectorReturnType = selectorReturnTypeInfo.getType();
@@ -281,14 +334,13 @@ public class CallExpressionResolver {
checkNestedClassAccess(expression, context);
//TODO move further
if (expression.getOperationSign() == JetTokens.SAFE_ACCESS) {
if (safeCall) {
if (selectorReturnType != null && !KotlinBuiltIns.isUnit(selectorReturnType)) {
if (TypeUtils.isNullableType(receiverType)) {
selectorReturnType = TypeUtils.makeNullable(selectorReturnType);
}
}
}
// TODO : this is suspicious: remove this code?
if (selectorReturnType != null) {
context.trace.record(BindingContext.EXPRESSION_TYPE, selectorExpression, selectorReturnType);
@@ -299,7 +351,48 @@ public class CallExpressionResolver {
return BasicExpressionTypingVisitor.createCompileTimeConstantTypeInfo(value, expression, context);
}
JetTypeInfo typeInfo = JetTypeInfo.create(selectorReturnType, selectorReturnTypeInfo.getDataFlowInfo());
JetTypeInfo typeInfo;
DataFlowInfo safeCallChainInfo;
if (receiverTypeInfo instanceof JetTypeInfoInsideSafeCall) {
safeCallChainInfo = ((JetTypeInfoInsideSafeCall) receiverTypeInfo).getSafeCallChainInfo();
}
else {
safeCallChainInfo = null;
}
if (safeCall) {
if (safeCallChainInfo == null) safeCallChainInfo = receiverDataFlowInfo;
if (context.insideCallChain) {
// If we are inside safe call chain, we SHOULD take arguments into account, for example
// x?.foo(y!!)?.bar(x.field)?.gav(y.field) (like smartCasts\safecalls\longChain)
// Also, we should provide further safe call chain data flow information or
// if we are in the most left safe call then just take it from receiver
typeInfo = new JetTypeInfoInsideSafeCall(selectorReturnType, selectorReturnTypeInfo.getDataFlowInfo(), safeCallChainInfo);
}
else {
// Here we should not take selector data flow info into account because it's only one branch, see KT-7204
// x?.foo(y!!) // y becomes not-nullable during argument analysis
// y.bar() // ERROR: y is nullable at this point
// So we should just take safe call chain data flow information, e.g. foo(x!!)?.bar()?.gav()
// If it's null, we must take receiver normal info, it's a chain with length of one like foo(x!!)?.bar() and
// safe call chain information does not yet exist
typeInfo = JetTypeInfo.create(selectorReturnType, safeCallChainInfo);
}
}
else {
// It's not a safe call, so we can take selector data flow information
// Safe call chain information also should be provided because it's can be a part of safe call chain
if (context.insideCallChain || safeCallChainInfo == null) {
// Not a safe call inside call chain with safe calls OR
// call chain without safe calls at all
typeInfo = new JetTypeInfoInsideSafeCall(selectorReturnType, selectorReturnTypeInfo.getDataFlowInfo(),
selectorReturnTypeInfo.getDataFlowInfo());
}
else {
// Exiting call chain with safe calls -- take data flow info from the lest-most receiver
// foo(x!!)?.bar().gav()
typeInfo = JetTypeInfo.create(selectorReturnType, safeCallChainInfo);
}
}
if (context.contextDependency == INDEPENDENT) {
DataFlowUtils.checkType(typeInfo, expression, context);
}
@@ -336,7 +429,8 @@ public class CallExpressionResolver {
if (receiverQualifier == null && expressionQualifier != null) {
assert expressionQualifier.getClassifier() instanceof ClassDescriptor :
"Only class can (package cannot) be accessed by instance reference: " + expressionQualifier;
context.trace.report(NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE.on(selectorExpression, (ClassDescriptor)expressionQualifier.getClassifier()));
context.trace.report(NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE
.on(selectorExpression, (ClassDescriptor) expressionQualifier.getClassifier()));
}
}
}
@@ -43,10 +43,12 @@ public class BasicCallResolutionContext extends CallResolutionContext<BasicCallR
@NotNull AdditionalTypeChecker additionalTypeChecker,
@NotNull StatementFilter statementFilter,
boolean isAnnotationContext,
boolean collectAllCandidates
boolean collectAllCandidates,
boolean insideSafeCallChain
) {
super(trace, scope, call, expectedType, dataFlowInfo, contextDependency, checkArguments, resolutionResultsCache,
dataFlowInfoForArguments, callChecker, additionalTypeChecker, statementFilter, isAnnotationContext, collectAllCandidates);
dataFlowInfoForArguments, callChecker, additionalTypeChecker, statementFilter, isAnnotationContext,
collectAllCandidates, insideSafeCallChain);
}
@NotNull
@@ -64,7 +66,7 @@ public class BasicCallResolutionContext extends CallResolutionContext<BasicCallR
) {
return new BasicCallResolutionContext(trace, scope, call, expectedType, dataFlowInfo, contextDependency, checkArguments,
new ResolutionResultsCacheImpl(), null,
callChecker, additionalTypeChecker, StatementFilter.NONE, isAnnotationContext, false);
callChecker, additionalTypeChecker, StatementFilter.NONE, isAnnotationContext, false, false);
}
@NotNull
@@ -75,7 +77,7 @@ public class BasicCallResolutionContext extends CallResolutionContext<BasicCallR
return new BasicCallResolutionContext(
context.trace, context.scope, call, context.expectedType, context.dataFlowInfo, context.contextDependency, checkArguments,
context.resolutionResultsCache, dataFlowInfoForArguments, context.callChecker, context.additionalTypeChecker, context.statementFilter,
context.isAnnotationContext, context.collectAllCandidates);
context.isAnnotationContext, context.collectAllCandidates, context.insideCallChain);
}
@NotNull
@@ -94,17 +96,20 @@ public class BasicCallResolutionContext extends CallResolutionContext<BasicCallR
@NotNull ContextDependency contextDependency,
@NotNull ResolutionResultsCache resolutionResultsCache,
@NotNull StatementFilter statementFilter,
boolean collectAllCandidates
boolean collectAllCandidates,
boolean insideSafeCallChain
) {
return new BasicCallResolutionContext(
trace, scope, call, expectedType, dataFlowInfo, contextDependency, checkArguments, resolutionResultsCache,
dataFlowInfoForArguments, callChecker, additionalTypeChecker, statementFilter, isAnnotationContext, collectAllCandidates);
dataFlowInfoForArguments, callChecker, additionalTypeChecker, statementFilter, isAnnotationContext,
collectAllCandidates, insideSafeCallChain);
}
@NotNull
public BasicCallResolutionContext replaceCall(@NotNull Call newCall) {
return new BasicCallResolutionContext(
trace, scope, newCall, expectedType, dataFlowInfo, contextDependency, checkArguments, resolutionResultsCache,
dataFlowInfoForArguments, callChecker, additionalTypeChecker, statementFilter, isAnnotationContext, collectAllCandidates);
dataFlowInfoForArguments, callChecker, additionalTypeChecker, statementFilter, isAnnotationContext,
collectAllCandidates, insideCallChain);
}
}
@@ -57,10 +57,12 @@ public final class CallCandidateResolutionContext<D extends CallableDescriptor>
@NotNull StatementFilter statementFilter,
@NotNull ReceiverValue explicitExtensionReceiverForInvoke,
boolean isAnnotationContext,
boolean collectAllCandidates
boolean collectAllCandidates,
boolean insideSafeCallChain
) {
super(trace, scope, call, expectedType, dataFlowInfo, contextDependency, checkArguments, resolutionResultsCache,
dataFlowInfoForArguments, callChecker, additionalTypeChecker, statementFilter, isAnnotationContext, collectAllCandidates);
dataFlowInfoForArguments, callChecker, additionalTypeChecker, statementFilter, isAnnotationContext,
collectAllCandidates, insideSafeCallChain);
this.candidateCall = candidateCall;
this.tracing = tracing;
this.explicitExtensionReceiverForInvoke = explicitExtensionReceiverForInvoke;
@@ -76,7 +78,7 @@ public final class CallCandidateResolutionContext<D extends CallableDescriptor>
context.dataFlowInfo, context.contextDependency, context.checkArguments,
context.resolutionResultsCache, context.dataFlowInfoForArguments,
context.callChecker, context.additionalTypeChecker, context.statementFilter, explicitExtensionReceiverForInvoke,
context.isAnnotationContext, context.collectAllCandidates);
context.isAnnotationContext, context.collectAllCandidates, context.insideCallChain);
}
public static <D extends CallableDescriptor> CallCandidateResolutionContext<D> create(
@@ -100,7 +102,7 @@ public final class CallCandidateResolutionContext<D extends CallableDescriptor>
candidateCall, tracing, context.trace, context.scope, context.call, context.expectedType,
context.dataFlowInfo, context.contextDependency, context.checkArguments, context.resolutionResultsCache,
context.dataFlowInfoForArguments, context.callChecker, context.additionalTypeChecker, context.statementFilter,
ReceiverValue.NO_RECEIVER, context.isAnnotationContext, context.collectAllCandidates);
ReceiverValue.NO_RECEIVER, context.isAnnotationContext, context.collectAllCandidates, context.insideCallChain);
}
@Override
@@ -112,11 +114,12 @@ public final class CallCandidateResolutionContext<D extends CallableDescriptor>
@NotNull ContextDependency contextDependency,
@NotNull ResolutionResultsCache resolutionResultsCache,
@NotNull StatementFilter statementFilter,
boolean collectAllCandidates
boolean collectAllCandidates,
boolean insideSafeCallChain
) {
return new CallCandidateResolutionContext<D>(
candidateCall, tracing, trace, scope, call, expectedType, dataFlowInfo, contextDependency, checkArguments,
resolutionResultsCache, dataFlowInfoForArguments, callChecker, additionalTypeChecker, statementFilter,
explicitExtensionReceiverForInvoke, isAnnotationContext, collectAllCandidates);
explicitExtensionReceiverForInvoke, isAnnotationContext, collectAllCandidates, insideSafeCallChain);
}
}
@@ -52,10 +52,11 @@ public abstract class CallResolutionContext<Context extends CallResolutionContex
@NotNull AdditionalTypeChecker additionalTypeChecker,
@NotNull StatementFilter statementFilter,
boolean isAnnotationContext,
boolean collectAllCandidates
boolean collectAllCandidates,
boolean insideSafeCallChain
) {
super(trace, scope, expectedType, dataFlowInfo, contextDependency, resolutionResultsCache, callChecker, additionalTypeChecker,
statementFilter, isAnnotationContext, collectAllCandidates);
statementFilter, isAnnotationContext, collectAllCandidates, insideSafeCallChain);
this.call = call;
this.checkArguments = checkArguments;
if (dataFlowInfoForArguments != null) {
@@ -27,6 +27,12 @@ import org.jetbrains.kotlin.resolve.scopes.JetScope;
import org.jetbrains.kotlin.types.JetType;
import org.jetbrains.kotlin.types.TypeUtils;
/**
* This class together with its descendants is intended to transfer data flow analysis information
* in top-down direction, from AST parents to children.
*
* NB: all descendants must be immutable!
*/
public abstract class ResolutionContext<Context extends ResolutionContext<Context>> {
@NotNull
public final BindingTrace trace;
@@ -51,6 +57,9 @@ public abstract class ResolutionContext<Context extends ResolutionContext<Contex
public final boolean collectAllCandidates;
// True if we are inside call chain like x?.foo()!!.bar()?.gav()
public final boolean insideCallChain;
protected ResolutionContext(
@NotNull BindingTrace trace,
@NotNull JetScope scope,
@@ -62,7 +71,8 @@ public abstract class ResolutionContext<Context extends ResolutionContext<Contex
@NotNull AdditionalTypeChecker additionalTypeChecker,
@NotNull StatementFilter statementFilter,
boolean isAnnotationContext,
boolean collectAllCandidates
boolean collectAllCandidates,
boolean insideCallChain
) {
this.trace = trace;
this.scope = scope;
@@ -75,6 +85,7 @@ public abstract class ResolutionContext<Context extends ResolutionContext<Contex
this.additionalTypeChecker = additionalTypeChecker;
this.isAnnotationContext = isAnnotationContext;
this.collectAllCandidates = collectAllCandidates;
this.insideCallChain = insideCallChain;
}
protected abstract Context create(
@@ -85,7 +96,8 @@ public abstract class ResolutionContext<Context extends ResolutionContext<Contex
@NotNull ContextDependency contextDependency,
@NotNull ResolutionResultsCache resolutionResultsCache,
@NotNull StatementFilter statementFilter,
boolean collectAllCandidates
boolean collectAllCandidates,
boolean insideSafeCallChain
);
@NotNull
@@ -98,14 +110,14 @@ public abstract class ResolutionContext<Context extends ResolutionContext<Contex
public Context replaceBindingTrace(@NotNull BindingTrace trace) {
if (this.trace == trace) return self();
return create(trace, scope, dataFlowInfo, expectedType, contextDependency, resolutionResultsCache, statementFilter,
collectAllCandidates);
collectAllCandidates, insideCallChain);
}
@NotNull
public Context replaceDataFlowInfo(@NotNull DataFlowInfo newDataFlowInfo) {
if (newDataFlowInfo == dataFlowInfo) return self();
return create(trace, scope, newDataFlowInfo, expectedType, contextDependency, resolutionResultsCache, statementFilter,
collectAllCandidates);
collectAllCandidates, insideCallChain);
}
@NotNull
@@ -113,28 +125,28 @@ public abstract class ResolutionContext<Context extends ResolutionContext<Contex
if (newExpectedType == null) return replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE);
if (expectedType == newExpectedType) return self();
return create(trace, scope, dataFlowInfo, newExpectedType, contextDependency, resolutionResultsCache, statementFilter,
collectAllCandidates);
collectAllCandidates, insideCallChain);
}
@NotNull
public Context replaceScope(@NotNull JetScope newScope) {
if (newScope == scope) return self();
return create(trace, newScope, dataFlowInfo, expectedType, contextDependency, resolutionResultsCache, statementFilter,
collectAllCandidates);
collectAllCandidates, insideCallChain);
}
@NotNull
public Context replaceContextDependency(@NotNull ContextDependency newContextDependency) {
if (newContextDependency == contextDependency) return self();
return create(trace, scope, dataFlowInfo, expectedType, newContextDependency, resolutionResultsCache, statementFilter,
collectAllCandidates);
collectAllCandidates, insideCallChain);
}
@NotNull
public Context replaceResolutionResultsCache(@NotNull ResolutionResultsCache newResolutionResultsCache) {
if (newResolutionResultsCache == resolutionResultsCache) return self();
return create(trace, scope, dataFlowInfo, expectedType, contextDependency, newResolutionResultsCache, statementFilter,
collectAllCandidates);
collectAllCandidates, insideCallChain);
}
@NotNull
@@ -145,12 +157,19 @@ public abstract class ResolutionContext<Context extends ResolutionContext<Contex
@NotNull
public Context replaceCollectAllCandidates(boolean newCollectAllCandidates) {
return create(trace, scope, dataFlowInfo, expectedType, contextDependency, resolutionResultsCache, statementFilter,
newCollectAllCandidates);
newCollectAllCandidates, insideCallChain);
}
@NotNull
public Context replacestatementFilter(@NotNull StatementFilter statementFilter) {
public Context replaceStatementFilter(@NotNull StatementFilter statementFilter) {
return create(trace, scope, dataFlowInfo, expectedType, contextDependency, resolutionResultsCache, statementFilter,
collectAllCandidates);
collectAllCandidates, insideCallChain);
}
@NotNull
public Context replaceInsideCallChain(boolean insideSafeCallChain) {
return create(trace, scope, dataFlowInfo, expectedType, contextDependency, resolutionResultsCache, statementFilter,
collectAllCandidates, insideSafeCallChain);
}
}
@@ -37,10 +37,11 @@ public class SimpleResolutionContext extends ResolutionContext<SimpleResolutionC
@NotNull AdditionalTypeChecker additionalTypeChecker,
@NotNull StatementFilter statementFilter,
boolean isAnnotationContext,
boolean collectAllCandidates
boolean collectAllCandidates,
boolean insideSafeCallChain
) {
super(trace, scope, expectedType, dataFlowInfo, contextDependency, resolutionResultsCache, callChecker, additionalTypeChecker,
statementFilter, isAnnotationContext, collectAllCandidates);
statementFilter, isAnnotationContext, collectAllCandidates, insideSafeCallChain);
}
public SimpleResolutionContext(
@@ -54,7 +55,7 @@ public class SimpleResolutionContext extends ResolutionContext<SimpleResolutionC
@NotNull StatementFilter statementFilter
) {
this(trace, scope, expectedType, dataFlowInfo, contextDependency, new ResolutionResultsCacheImpl(),
callChecker, additionalTypeChecker, statementFilter, false, false);
callChecker, additionalTypeChecker, statementFilter, false, false, false);
}
@Override
@@ -66,10 +67,11 @@ public class SimpleResolutionContext extends ResolutionContext<SimpleResolutionC
@NotNull ContextDependency contextDependency,
@NotNull ResolutionResultsCache resolutionResultsCache,
@NotNull StatementFilter statementFilter,
boolean collectAllCandidates
boolean collectAllCandidates,
boolean insideSafeCallChain
) {
return new SimpleResolutionContext(
trace, scope, expectedType, dataFlowInfo, contextDependency, resolutionResultsCache, callChecker, additionalTypeChecker,
statementFilter, isAnnotationContext, collectAllCandidates);
statementFilter, isAnnotationContext, collectAllCandidates, insideSafeCallChain);
}
}
@@ -61,10 +61,11 @@ public class ResolutionTask<D extends CallableDescriptor, F extends D> extends C
@NotNull StatementFilter statementFilter,
@NotNull Collection<MutableResolvedCall<F>> resolvedCalls,
boolean isAnnotationContext,
boolean collectAllCandidates
boolean collectAllCandidates,
boolean insideSafeCallChain
) {
super(trace, scope, call, expectedType, dataFlowInfo, contextDependency, checkArguments, resolutionResultsCache,
dataFlowInfoForArguments, callChecker, additionalTypeChecker, statementFilter, isAnnotationContext, collectAllCandidates);
dataFlowInfoForArguments, callChecker, additionalTypeChecker, statementFilter, isAnnotationContext, collectAllCandidates, insideSafeCallChain);
this.lazyCandidates = lazyCandidates;
this.resolvedCalls = resolvedCalls;
this.tracing = tracing;
@@ -80,7 +81,7 @@ public class ResolutionTask<D extends CallableDescriptor, F extends D> extends C
context.expectedType, context.dataFlowInfo, context.contextDependency, context.checkArguments,
context.resolutionResultsCache, context.dataFlowInfoForArguments,
context.callChecker, context.additionalTypeChecker, context.statementFilter, Lists.<MutableResolvedCall<F>>newArrayList(),
context.isAnnotationContext, context.collectAllCandidates);
context.isAnnotationContext, context.collectAllCandidates, context.insideCallChain);
}
public ResolutionTask(
@@ -119,12 +120,13 @@ public class ResolutionTask<D extends CallableDescriptor, F extends D> extends C
@NotNull ContextDependency contextDependency,
@NotNull ResolutionResultsCache resolutionResultsCache,
@NotNull StatementFilter statementFilter,
boolean collectAllCandidates
boolean collectAllCandidates,
boolean insideSafeCallChain
) {
return new ResolutionTask<D, F>(
lazyCandidates, tracing, trace, scope, call, expectedType, dataFlowInfo, contextDependency, checkArguments,
resolutionResultsCache, dataFlowInfoForArguments, callChecker, additionalTypeChecker, statementFilter, resolvedCalls,
isAnnotationContext, collectAllCandidates);
isAnnotationContext, collectAllCandidates, insideSafeCallChain);
}
public ResolutionTask<D, F> replaceContext(@NotNull BasicCallResolutionContext newContext) {
@@ -135,7 +137,7 @@ public class ResolutionTask<D extends CallableDescriptor, F extends D> extends C
return new ResolutionTask<D, F>(
lazyCandidates, tracing, trace, scope, newCall, expectedType, dataFlowInfo, contextDependency, checkArguments,
resolutionResultsCache, dataFlowInfoForArguments, callChecker, additionalTypeChecker, statementFilter, resolvedCalls,
isAnnotationContext, collectAllCandidates);
isAnnotationContext, collectAllCandidates, insideCallChain);
}
public interface DescriptorCheckStrategy {
@@ -20,16 +20,23 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
/**
* This class is intended to transfer data flow analysis information in bottom-up direction,
* from AST children to parents. It stores a type of expression under analysis,
* current information about types and nullabilities.
*
* NB: it must be immutable together with all its descendants!
*/
public class JetTypeInfo {
@NotNull
public static JetTypeInfo create(@Nullable JetType type, @NotNull DataFlowInfo dataFlowInfo) {
return new JetTypeInfo(type, dataFlowInfo);
}
private final JetType type;
private final DataFlowInfo dataFlowInfo;
private JetTypeInfo(@Nullable JetType type, @NotNull DataFlowInfo dataFlowInfo) {
protected JetTypeInfo(@Nullable JetType type, @NotNull DataFlowInfo dataFlowInfo) {
this.type = type;
this.dataFlowInfo = dataFlowInfo;
}
@@ -876,6 +876,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
JetSimpleNameExpression operationSign = expression.getOperationReference();
assert operationSign.getReferencedNameElementType() == JetTokens.EXCLEXCL;
// TODO: something must be done for not to lose safe call chain information here
// See also CallExpressionResolver.getSimpleNameExpressionTypeInfo, .getQualifiedExpressionTypeInfo
Call call = createCallForSpecialConstruction(expression, expression.getOperationReference(), Collections.singletonList(baseExpression));
components.controlStructureTypingUtils.resolveSpecialConstructionAsCall(
call, "ExclExcl", Collections.singletonList("baseExpr"), Collections.singletonList(true), context, null);
@@ -66,7 +66,7 @@ public class ExpressionTypingContext extends ResolutionContext<ExpressionTypingC
context.trace, context.scope, context.dataFlowInfo, context.expectedType,
context.contextDependency, context.resolutionResultsCache, context.callChecker, context.additionalTypeChecker,
context.statementFilter,
context.isAnnotationContext, context.collectAllCandidates
context.isAnnotationContext, context.collectAllCandidates, context.insideCallChain
);
}
@@ -85,7 +85,7 @@ public class ExpressionTypingContext extends ResolutionContext<ExpressionTypingC
) {
return new ExpressionTypingContext(
trace, scope, dataFlowInfo, expectedType, contextDependency, resolutionResultsCache, callChecker, additionalTypeChecker,
statementFilter, isAnnotationContext, false);
statementFilter, isAnnotationContext, false, false);
}
private CompileTimeConstantChecker compileTimeConstantChecker;
@@ -101,10 +101,11 @@ public class ExpressionTypingContext extends ResolutionContext<ExpressionTypingC
@NotNull AdditionalTypeChecker additionalTypeChecker,
@NotNull StatementFilter statementFilter,
boolean isAnnotationContext,
boolean collectAllCandidates
boolean collectAllCandidates,
boolean insideSafeCallChain
) {
super(trace, scope, expectedType, dataFlowInfo, contextDependency, resolutionResultsCache, callChecker, additionalTypeChecker,
statementFilter, isAnnotationContext, collectAllCandidates);
statementFilter, isAnnotationContext, collectAllCandidates, insideSafeCallChain);
}
@Override
@@ -116,11 +117,12 @@ public class ExpressionTypingContext extends ResolutionContext<ExpressionTypingC
@NotNull ContextDependency contextDependency,
@NotNull ResolutionResultsCache resolutionResultsCache,
@NotNull StatementFilter statementFilter,
boolean collectAllCandidates
boolean collectAllCandidates,
boolean insideSafeCallChain
) {
return new ExpressionTypingContext(trace, scope, dataFlowInfo,
expectedType, contextDependency, resolutionResultsCache, callChecker, additionalTypeChecker,
statementFilter, isAnnotationContext, collectAllCandidates);
statementFilter, isAnnotationContext, collectAllCandidates, insideSafeCallChain);
}
///////////// LAZY ACCESSORS
@@ -266,7 +266,7 @@ public class ExpressionTypingServices {
}
else {
r = getBlockReturnedTypeWithWritableScope(scope, block, coercionStrategyForLastExpression,
context.replacestatementFilter(getStatementFilter()));
context.replaceStatementFilter(getStatementFilter()));
}
scope.changeLockLevel(WritableScope.LockLevel.READING);
@@ -303,6 +303,10 @@ public class ExpressionTypingServices {
}
}
/**
* Visits block statements propagating data flow information from the first to the last.
* Determines block returned type and data flow information at the end of the block.
*/
/*package*/ JetTypeInfo getBlockReturnedTypeWithWritableScope(
@NotNull WritableScope scope,
@NotNull List<? extends JetElement> block,
@@ -0,0 +1,6 @@
fun calc(x: List<String>?, y: Int?): Int {
x?.get(y!! - 1)
// y!! above should not provide smart cast here
val yy: Int = <!TYPE_MISMATCH!>y<!>
return yy + (x?.size() ?: 0)
}
@@ -0,0 +1,3 @@
package
internal fun calc(/*0*/ x: kotlin.List<kotlin.String>?, /*1*/ y: kotlin.Int?): kotlin.Int
@@ -0,0 +1,6 @@
fun calc(x: List<String>?): Int {
// After KT-5840 fix !! assertion should become unnecessary here
x?.get(x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.size() - 1)
// x?. or x!! above should not provide smart cast here
return x<!UNSAFE_CALL!>.<!>size()
}
@@ -0,0 +1,3 @@
package
internal fun calc(/*0*/ x: kotlin.List<kotlin.String>?): kotlin.Int
@@ -0,0 +1,6 @@
fun calc(x: List<String>?): Int {
// x should be non-null in arguments list, despite of a chain
x?.subList(0, 1)?.get(<!DEBUG_INFO_SMARTCAST!>x<!>.size())
// But not here!
return x!!.size()
}
@@ -0,0 +1,3 @@
package
internal fun calc(/*0*/ x: kotlin.List<kotlin.String>?): kotlin.Int
@@ -0,0 +1,6 @@
fun calc(x: List<String>?, y: List<Int>?) {
// x and y should be non-null in arguments list, despite of a chains
x?.subList(y?.subList(1, 2)?.get(<!DEBUG_INFO_SMARTCAST!>y<!>.size()) ?: 0,
y?.get(0) ?: 1) // But safe call is NECESSARY here for y
?.get(<!DEBUG_INFO_SMARTCAST!>x<!>.size())
}
@@ -0,0 +1,3 @@
package
internal fun calc(/*0*/ x: kotlin.List<kotlin.String>?, /*1*/ y: kotlin.List<kotlin.Int>?): kotlin.Unit
@@ -0,0 +1,6 @@
fun calc(x: List<String>?): Int {
// x should be non-null in arguments list, despite of a chain
x?.subList(0, 1)<!UNSAFE_CALL!>.<!>get(<!DEBUG_INFO_SMARTCAST!>x<!>.size())
// But not here!
return x!!.size()
}
@@ -0,0 +1,3 @@
package
internal fun calc(/*0*/ x: kotlin.List<kotlin.String>?): kotlin.Int
@@ -0,0 +1,6 @@
fun calc(x: List<String>?): Int {
// x should be non-null in arguments list, including inner call
x?.get(<!DEBUG_INFO_SMARTCAST!>x<!>.get(<!DEBUG_INFO_SMARTCAST!>x<!>.size() - 1).length())
// but not also here!
return x<!UNSAFE_CALL!>.<!>size()
}
@@ -0,0 +1,3 @@
package
internal fun calc(/*0*/ x: kotlin.List<kotlin.String>?): kotlin.Int
@@ -0,0 +1,6 @@
fun String.foo(arg: Int) = this[arg]
fun calc(x: String?) {
// x should be non-null in arguments list
x?.foo(<!DEBUG_INFO_SMARTCAST!>x<!>.length() - 1)
}
@@ -0,0 +1,4 @@
package
internal fun calc(/*0*/ x: kotlin.String?): kotlin.Unit
internal fun kotlin.String.foo(/*0*/ arg: kotlin.Int): kotlin.Char
@@ -0,0 +1,7 @@
fun foo(y: Int) = y
fun calc(x: List<String>?): Int {
foo(x!!.size())
// Here we should have smart cast because of x!!, despite of KT-7204 fixed
return <!DEBUG_INFO_SMARTCAST!>x<!>.size()
}
@@ -0,0 +1,4 @@
package
internal fun calc(/*0*/ x: kotlin.List<kotlin.String>?): kotlin.Int
internal fun foo(/*0*/ y: kotlin.Int): kotlin.Int
@@ -0,0 +1,4 @@
fun calc(x: List<String>?, y: Int?) {
// Smart cast should work here despite of KT-7204 fixed
x?.subList(0, y!!)?.get(<!DEBUG_INFO_SMARTCAST!>y<!>)
}
@@ -0,0 +1,3 @@
package
internal fun calc(/*0*/ x: kotlin.List<kotlin.String>?, /*1*/ y: kotlin.Int?): kotlin.Unit
@@ -0,0 +1,7 @@
fun String.foo(y: Int) = y
fun calc(x: List<String>?): Int {
"abc".foo(x!!.size())
// Here we should have smart cast because of x!!, despite of KT-7204 fixed
return <!DEBUG_INFO_SMARTCAST!>x<!>.size()
}
@@ -0,0 +1,4 @@
package
internal fun calc(/*0*/ x: kotlin.List<kotlin.String>?): kotlin.Int
internal fun kotlin.String.foo(/*0*/ y: kotlin.Int): kotlin.Int
@@ -0,0 +1,5 @@
fun calc(x: List<String>?, y: Int?): Int {
x?.subList(y!! - 1, <!DEBUG_INFO_SMARTCAST!>y<!>)
// y!! above should not provide smart cast here
return <!TYPE_MISMATCH!>y<!>
}
@@ -0,0 +1,3 @@
package
internal fun calc(/*0*/ x: kotlin.List<kotlin.String>?, /*1*/ y: kotlin.Int?): kotlin.Int
@@ -0,0 +1,8 @@
fun foo(x: String): String? = x
fun calc(x: String?, y: String?): Int {
// Smart cast because of y!! in receiver
x?.subSequence(y!!.subSequence(0, 1).length(), <!DEBUG_INFO_SMARTCAST!>y<!>.length())
// No smart cast possible
return y<!UNSAFE_CALL!>.<!>length()
}
@@ -0,0 +1,4 @@
package
internal fun calc(/*0*/ x: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Int
internal fun foo(/*0*/ x: kotlin.String): kotlin.String?
@@ -0,0 +1,8 @@
fun foo(y: Int): Int {
return y + 1
}
fun calc(x: List<String>?): Int {
// x should be non-null in arguments list
return foo(x?.get(<!DEBUG_INFO_SMARTCAST!>x<!>.size() - 1)!!.length())
}
@@ -0,0 +1,4 @@
package
internal fun calc(/*0*/ x: kotlin.List<kotlin.String>?): kotlin.Int
internal fun foo(/*0*/ y: kotlin.Int): kotlin.Int
@@ -0,0 +1,6 @@
fun calc(x: List<String>?) {
// x should be non-null in arguments list, despite of a chain
x?.subList(0, <!DEBUG_INFO_SMARTCAST!>x<!>.size())?.
subList(0, <!DEBUG_INFO_SMARTCAST!>x<!>.size())?.
get(<!DEBUG_INFO_SMARTCAST!>x<!>.size())
}
@@ -0,0 +1,3 @@
package
internal fun calc(/*0*/ x: kotlin.List<kotlin.String>?): kotlin.Unit
@@ -0,0 +1,6 @@
data class MyClass(val x: String?)
fun foo(y: MyClass): Int {
val z = y.x?.subSequence(0, <!DEBUG_INFO_SMARTCAST!>y.x<!>.length())
return z?.length() ?: -1
}
@@ -0,0 +1,13 @@
package
internal fun foo(/*0*/ y: MyClass): kotlin.Int
kotlin.data() internal final class MyClass {
public constructor MyClass(/*0*/ x: kotlin.String?)
internal final val x: kotlin.String?
internal final /*synthesized*/ fun component1(): kotlin.String?
public final /*synthesized*/ fun copy(/*0*/ x: kotlin.String? = ...): MyClass
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
}
@@ -0,0 +1,8 @@
fun foo(x: String): String? = x
fun calc(x: String?): Int {
// Smart cast because of x!! in receiver
foo(x!!)?.subSequence(0, <!DEBUG_INFO_SMARTCAST!>x<!>.length())
// Smart cast because of x!! in receiver
return <!DEBUG_INFO_SMARTCAST!>x<!>.length()
}
@@ -0,0 +1,4 @@
package
internal fun calc(/*0*/ x: kotlin.String?): kotlin.Int
internal fun foo(/*0*/ x: kotlin.String): kotlin.String?
@@ -0,0 +1,8 @@
fun foo(x: String): String? = x
fun calc(x: String?): Int {
// Smart cast because of x!! in receiver
foo(x!!)?.subSequence(0, <!DEBUG_INFO_SMARTCAST!>x<!>.length())?.length()
// Smart cast because of x!! in receiver
return <!DEBUG_INFO_SMARTCAST!>x<!>.length()
}
@@ -0,0 +1,4 @@
package
internal fun calc(/*0*/ x: kotlin.String?): kotlin.Int
internal fun foo(/*0*/ x: kotlin.String): kotlin.String?
@@ -0,0 +1,8 @@
fun foo(x: String): String? = x
fun calc(x: String?, y: Int?): Int {
// Smart cast because of x!! in receiver
foo(x!!)?.subSequence(y!!, <!DEBUG_INFO_SMARTCAST!>x<!>.length())?.length()
// No smart cast possible
return <!TYPE_MISMATCH!>y<!>
}
@@ -0,0 +1,4 @@
package
internal fun calc(/*0*/ x: kotlin.String?, /*1*/ y: kotlin.Int?): kotlin.Int
internal fun foo(/*0*/ x: kotlin.String): kotlin.String?
@@ -0,0 +1,6 @@
fun calc(x: List<String>?): Int {
// x should be non-null in arguments list
x?.get(<!DEBUG_INFO_SMARTCAST!>x<!>.size() - 1)
// but not also here!
return x<!UNSAFE_CALL!>.<!>size()
}
@@ -0,0 +1,3 @@
package
internal fun calc(/*0*/ x: kotlin.List<kotlin.String>?): kotlin.Int
@@ -0,0 +1,6 @@
fun calc(x: List<String>?): Int {
// x should be non-null in arguments list
x?.subList(<!DEBUG_INFO_SMARTCAST!>x<!>.size() - 1, <!DEBUG_INFO_SMARTCAST!>x<!>.size())
// but not also here!
return x<!UNSAFE_CALL!>.<!>size()
}
@@ -0,0 +1,3 @@
package
internal fun calc(/*0*/ x: kotlin.List<kotlin.String>?): kotlin.Int
@@ -11024,6 +11024,7 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
@TestDataPath("$PROJECT_ROOT")
@InnerTestClasses({
SmartCasts.Inference.class,
SmartCasts.Safecalls.class,
SmartCasts.Varnotnull.class,
})
@RunWith(JUnit3RunnerWithInners.class)
@@ -11185,6 +11186,135 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
}
}
@TestMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Safecalls extends AbstractJetDiagnosticsTest {
public void testAllFilesPresentInSafecalls() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/smartCasts/safecalls"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("anotherVal.kt")
public void testAnotherVal() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/anotherVal.kt");
doTest(fileName);
}
@TestMetadata("argument.kt")
public void testArgument() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/argument.kt");
doTest(fileName);
}
@TestMetadata("chainAndUse.kt")
public void testChainAndUse() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/chainAndUse.kt");
doTest(fileName);
}
@TestMetadata("chainInChain.kt")
public void testChainInChain() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/chainInChain.kt");
doTest(fileName);
}
@TestMetadata("chainMixedUnsafe.kt")
public void testChainMixedUnsafe() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/chainMixedUnsafe.kt");
doTest(fileName);
}
@TestMetadata("doubleCall.kt")
public void testDoubleCall() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/doubleCall.kt");
doTest(fileName);
}
@TestMetadata("extension.kt")
public void testExtension() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/extension.kt");
doTest(fileName);
}
@TestMetadata("falseArgument.kt")
public void testFalseArgument() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/falseArgument.kt");
doTest(fileName);
}
@TestMetadata("falseChain.kt")
public void testFalseChain() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/falseChain.kt");
doTest(fileName);
}
@TestMetadata("falseExtension.kt")
public void testFalseExtension() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/falseExtension.kt");
doTest(fileName);
}
@TestMetadata("falseSecondArgument.kt")
public void testFalseSecondArgument() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/falseSecondArgument.kt");
doTest(fileName);
}
@TestMetadata("innerReceiver.kt")
public void testInnerReceiver() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/innerReceiver.kt");
doTest(fileName);
}
@TestMetadata("insideCall.kt")
public void testInsideCall() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/insideCall.kt");
doTest(fileName);
}
@TestMetadata("longChain.kt")
public void testLongChain() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/longChain.kt");
doTest(fileName);
}
@TestMetadata("property.kt")
public void testProperty() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/property.kt");
doTest(fileName);
}
@TestMetadata("receiver.kt")
public void testReceiver() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/receiver.kt");
doTest(fileName);
}
@TestMetadata("receiverAndChain.kt")
public void testReceiverAndChain() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/receiverAndChain.kt");
doTest(fileName);
}
@TestMetadata("receiverAndChainFalse.kt")
public void testReceiverAndChainFalse() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/receiverAndChainFalse.kt");
doTest(fileName);
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/simple.kt");
doTest(fileName);
}
@TestMetadata("twoArgs.kt")
public void testTwoArgs() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls/twoArgs.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)