diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ArgumentTypeResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ArgumentTypeResolver.java index d300bd6eb39..49044196f31 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ArgumentTypeResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ArgumentTypeResolver.java @@ -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()); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.java index 457230f18f4..672776e0592 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.java @@ -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.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 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())); } } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/BasicCallResolutionContext.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/BasicCallResolutionContext.java index 83b6d6cd1be..008f3e9124d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/BasicCallResolutionContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/BasicCallResolutionContext.java @@ -43,10 +43,12 @@ public class BasicCallResolutionContext extends CallResolutionContext @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 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 CallCandidateResolutionContext create( @@ -100,7 +102,7 @@ public final class CallCandidateResolutionContext 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 @NotNull ContextDependency contextDependency, @NotNull ResolutionResultsCache resolutionResultsCache, @NotNull StatementFilter statementFilter, - boolean collectAllCandidates + boolean collectAllCandidates, + boolean insideSafeCallChain ) { return new CallCandidateResolutionContext( candidateCall, tracing, trace, scope, call, expectedType, dataFlowInfo, contextDependency, checkArguments, resolutionResultsCache, dataFlowInfoForArguments, callChecker, additionalTypeChecker, statementFilter, - explicitExtensionReceiverForInvoke, isAnnotationContext, collectAllCandidates); + explicitExtensionReceiverForInvoke, isAnnotationContext, collectAllCandidates, insideSafeCallChain); } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/CallResolutionContext.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/CallResolutionContext.java index 97e2ed29c96..43468f62501 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/CallResolutionContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/CallResolutionContext.java @@ -52,10 +52,11 @@ public abstract class CallResolutionContext> { @NotNull public final BindingTrace trace; @@ -51,6 +57,9 @@ public abstract class ResolutionContext extends C @NotNull StatementFilter statementFilter, @NotNull Collection> 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 extends C context.expectedType, context.dataFlowInfo, context.contextDependency, context.checkArguments, context.resolutionResultsCache, context.dataFlowInfoForArguments, context.callChecker, context.additionalTypeChecker, context.statementFilter, Lists.>newArrayList(), - context.isAnnotationContext, context.collectAllCandidates); + context.isAnnotationContext, context.collectAllCandidates, context.insideCallChain); } public ResolutionTask( @@ -119,12 +120,13 @@ public class ResolutionTask extends C @NotNull ContextDependency contextDependency, @NotNull ResolutionResultsCache resolutionResultsCache, @NotNull StatementFilter statementFilter, - boolean collectAllCandidates + boolean collectAllCandidates, + boolean insideSafeCallChain ) { return new ResolutionTask( lazyCandidates, tracing, trace, scope, call, expectedType, dataFlowInfo, contextDependency, checkArguments, resolutionResultsCache, dataFlowInfoForArguments, callChecker, additionalTypeChecker, statementFilter, resolvedCalls, - isAnnotationContext, collectAllCandidates); + isAnnotationContext, collectAllCandidates, insideSafeCallChain); } public ResolutionTask replaceContext(@NotNull BasicCallResolutionContext newContext) { @@ -135,7 +137,7 @@ public class ResolutionTask extends C return new ResolutionTask( lazyCandidates, tracing, trace, scope, newCall, expectedType, dataFlowInfo, contextDependency, checkArguments, resolutionResultsCache, dataFlowInfoForArguments, callChecker, additionalTypeChecker, statementFilter, resolvedCalls, - isAnnotationContext, collectAllCandidates); + isAnnotationContext, collectAllCandidates, insideCallChain); } public interface DescriptorCheckStrategy { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/JetTypeInfo.java b/compiler/frontend/src/org/jetbrains/kotlin/types/JetTypeInfo.java index 141864b1236..ba6f61fe9c2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/JetTypeInfo.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/JetTypeInfo.java @@ -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; } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java index fa96a94c62f..cf3dba12770 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -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); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingContext.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingContext.java index 43fc716113e..99e4441c388 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingContext.java @@ -66,7 +66,7 @@ public class ExpressionTypingContext extends ResolutionContext block, diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/anotherVal.kt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/anotherVal.kt new file mode 100644 index 00000000000..12ca8333e34 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/anotherVal.kt @@ -0,0 +1,6 @@ +fun calc(x: List?, y: Int?): Int { + x?.get(y!! - 1) + // y!! above should not provide smart cast here + val yy: Int = y + return yy + (x?.size() ?: 0) +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/anotherVal.txt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/anotherVal.txt new file mode 100644 index 00000000000..da08515d819 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/anotherVal.txt @@ -0,0 +1,3 @@ +package + +internal fun calc(/*0*/ x: kotlin.List?, /*1*/ y: kotlin.Int?): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/argument.kt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/argument.kt new file mode 100644 index 00000000000..b8b751ecc9e --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/argument.kt @@ -0,0 +1,6 @@ +fun calc(x: List?): Int { + // After KT-5840 fix !! assertion should become unnecessary here + x?.get(x!!.size() - 1) + // x?. or x!! above should not provide smart cast here + return x.size() +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/argument.txt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/argument.txt new file mode 100644 index 00000000000..475ed3cb394 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/argument.txt @@ -0,0 +1,3 @@ +package + +internal fun calc(/*0*/ x: kotlin.List?): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/chainAndUse.kt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/chainAndUse.kt new file mode 100644 index 00000000000..9837313f1e1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/chainAndUse.kt @@ -0,0 +1,6 @@ +fun calc(x: List?): Int { + // x should be non-null in arguments list, despite of a chain + x?.subList(0, 1)?.get(x.size()) + // But not here! + return x!!.size() +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/chainAndUse.txt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/chainAndUse.txt new file mode 100644 index 00000000000..475ed3cb394 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/chainAndUse.txt @@ -0,0 +1,3 @@ +package + +internal fun calc(/*0*/ x: kotlin.List?): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/chainInChain.kt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/chainInChain.kt new file mode 100644 index 00000000000..1bb82555bd9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/chainInChain.kt @@ -0,0 +1,6 @@ +fun calc(x: List?, y: List?) { + // x and y should be non-null in arguments list, despite of a chains + x?.subList(y?.subList(1, 2)?.get(y.size()) ?: 0, + y?.get(0) ?: 1) // But safe call is NECESSARY here for y + ?.get(x.size()) +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/chainInChain.txt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/chainInChain.txt new file mode 100644 index 00000000000..d12aedc5310 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/chainInChain.txt @@ -0,0 +1,3 @@ +package + +internal fun calc(/*0*/ x: kotlin.List?, /*1*/ y: kotlin.List?): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/chainMixedUnsafe.kt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/chainMixedUnsafe.kt new file mode 100644 index 00000000000..14dee286e07 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/chainMixedUnsafe.kt @@ -0,0 +1,6 @@ +fun calc(x: List?): Int { + // x should be non-null in arguments list, despite of a chain + x?.subList(0, 1).get(x.size()) + // But not here! + return x!!.size() +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/chainMixedUnsafe.txt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/chainMixedUnsafe.txt new file mode 100644 index 00000000000..475ed3cb394 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/chainMixedUnsafe.txt @@ -0,0 +1,3 @@ +package + +internal fun calc(/*0*/ x: kotlin.List?): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/doubleCall.kt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/doubleCall.kt new file mode 100644 index 00000000000..7723645bfce --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/doubleCall.kt @@ -0,0 +1,6 @@ +fun calc(x: List?): Int { + // x should be non-null in arguments list, including inner call + x?.get(x.get(x.size() - 1).length()) + // but not also here! + return x.size() +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/doubleCall.txt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/doubleCall.txt new file mode 100644 index 00000000000..475ed3cb394 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/doubleCall.txt @@ -0,0 +1,3 @@ +package + +internal fun calc(/*0*/ x: kotlin.List?): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/extension.kt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/extension.kt new file mode 100644 index 00000000000..cec0ecccfd6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/extension.kt @@ -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(x.length() - 1) +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/extension.txt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/extension.txt new file mode 100644 index 00000000000..d275cf61146 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/extension.txt @@ -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 diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseArgument.kt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseArgument.kt new file mode 100644 index 00000000000..5a1a1e230b2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseArgument.kt @@ -0,0 +1,7 @@ +fun foo(y: Int) = y + +fun calc(x: List?): Int { + foo(x!!.size()) + // Here we should have smart cast because of x!!, despite of KT-7204 fixed + return x.size() +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseArgument.txt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseArgument.txt new file mode 100644 index 00000000000..ef18e42ebe9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseArgument.txt @@ -0,0 +1,4 @@ +package + +internal fun calc(/*0*/ x: kotlin.List?): kotlin.Int +internal fun foo(/*0*/ y: kotlin.Int): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseChain.kt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseChain.kt new file mode 100644 index 00000000000..522a5015a58 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseChain.kt @@ -0,0 +1,4 @@ +fun calc(x: List?, y: Int?) { + // Smart cast should work here despite of KT-7204 fixed + x?.subList(0, y!!)?.get(y) +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseChain.txt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseChain.txt new file mode 100644 index 00000000000..8edf01c52ad --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseChain.txt @@ -0,0 +1,3 @@ +package + +internal fun calc(/*0*/ x: kotlin.List?, /*1*/ y: kotlin.Int?): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseExtension.kt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseExtension.kt new file mode 100644 index 00000000000..5f87c4b5dd5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseExtension.kt @@ -0,0 +1,7 @@ +fun String.foo(y: Int) = y + +fun calc(x: List?): Int { + "abc".foo(x!!.size()) + // Here we should have smart cast because of x!!, despite of KT-7204 fixed + return x.size() +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseExtension.txt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseExtension.txt new file mode 100644 index 00000000000..4ae1bcf6341 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseExtension.txt @@ -0,0 +1,4 @@ +package + +internal fun calc(/*0*/ x: kotlin.List?): kotlin.Int +internal fun kotlin.String.foo(/*0*/ y: kotlin.Int): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseSecondArgument.kt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseSecondArgument.kt new file mode 100644 index 00000000000..e0bc8af3355 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseSecondArgument.kt @@ -0,0 +1,5 @@ +fun calc(x: List?, y: Int?): Int { + x?.subList(y!! - 1, y) + // y!! above should not provide smart cast here + return y +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseSecondArgument.txt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseSecondArgument.txt new file mode 100644 index 00000000000..da08515d819 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/falseSecondArgument.txt @@ -0,0 +1,3 @@ +package + +internal fun calc(/*0*/ x: kotlin.List?, /*1*/ y: kotlin.Int?): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/innerReceiver.kt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/innerReceiver.kt new file mode 100644 index 00000000000..bd114a24f58 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/innerReceiver.kt @@ -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(), y.length()) + // No smart cast possible + return y.length() +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/innerReceiver.txt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/innerReceiver.txt new file mode 100644 index 00000000000..325ee25cac4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/innerReceiver.txt @@ -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? diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/insideCall.kt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/insideCall.kt new file mode 100644 index 00000000000..7f59458d336 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/insideCall.kt @@ -0,0 +1,8 @@ +fun foo(y: Int): Int { + return y + 1 +} + +fun calc(x: List?): Int { + // x should be non-null in arguments list + return foo(x?.get(x.size() - 1)!!.length()) +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/insideCall.txt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/insideCall.txt new file mode 100644 index 00000000000..ef18e42ebe9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/insideCall.txt @@ -0,0 +1,4 @@ +package + +internal fun calc(/*0*/ x: kotlin.List?): kotlin.Int +internal fun foo(/*0*/ y: kotlin.Int): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/longChain.kt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/longChain.kt new file mode 100644 index 00000000000..ff79102eb23 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/longChain.kt @@ -0,0 +1,6 @@ +fun calc(x: List?) { + // x should be non-null in arguments list, despite of a chain + x?.subList(0, x.size())?. + subList(0, x.size())?. + get(x.size()) +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/longChain.txt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/longChain.txt new file mode 100644 index 00000000000..bfdfaa8c2f6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/longChain.txt @@ -0,0 +1,3 @@ +package + +internal fun calc(/*0*/ x: kotlin.List?): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/property.kt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/property.kt new file mode 100644 index 00000000000..cc091c171bf --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/property.kt @@ -0,0 +1,6 @@ +data class MyClass(val x: String?) + +fun foo(y: MyClass): Int { + val z = y.x?.subSequence(0, y.x.length()) + return z?.length() ?: -1 +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/property.txt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/property.txt new file mode 100644 index 00000000000..418d5db9acb --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/property.txt @@ -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 +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/receiver.kt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/receiver.kt new file mode 100644 index 00000000000..37b8d93dc99 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/receiver.kt @@ -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, x.length()) + // Smart cast because of x!! in receiver + return x.length() +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/receiver.txt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/receiver.txt new file mode 100644 index 00000000000..17752c6af2e --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/receiver.txt @@ -0,0 +1,4 @@ +package + +internal fun calc(/*0*/ x: kotlin.String?): kotlin.Int +internal fun foo(/*0*/ x: kotlin.String): kotlin.String? diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/receiverAndChain.kt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/receiverAndChain.kt new file mode 100644 index 00000000000..0d832764e9c --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/receiverAndChain.kt @@ -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, x.length())?.length() + // Smart cast because of x!! in receiver + return x.length() +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/receiverAndChain.txt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/receiverAndChain.txt new file mode 100644 index 00000000000..17752c6af2e --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/receiverAndChain.txt @@ -0,0 +1,4 @@ +package + +internal fun calc(/*0*/ x: kotlin.String?): kotlin.Int +internal fun foo(/*0*/ x: kotlin.String): kotlin.String? diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/receiverAndChainFalse.kt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/receiverAndChainFalse.kt new file mode 100644 index 00000000000..471e94ac0a9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/receiverAndChainFalse.kt @@ -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!!, x.length())?.length() + // No smart cast possible + return y +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/receiverAndChainFalse.txt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/receiverAndChainFalse.txt new file mode 100644 index 00000000000..57de172643a --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/receiverAndChainFalse.txt @@ -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? diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/simple.kt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/simple.kt new file mode 100644 index 00000000000..c7bcde5512c --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/simple.kt @@ -0,0 +1,6 @@ +fun calc(x: List?): Int { + // x should be non-null in arguments list + x?.get(x.size() - 1) + // but not also here! + return x.size() +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/simple.txt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/simple.txt new file mode 100644 index 00000000000..475ed3cb394 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/simple.txt @@ -0,0 +1,3 @@ +package + +internal fun calc(/*0*/ x: kotlin.List?): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/twoArgs.kt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/twoArgs.kt new file mode 100644 index 00000000000..b1a5aa039f5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/twoArgs.kt @@ -0,0 +1,6 @@ +fun calc(x: List?): Int { + // x should be non-null in arguments list + x?.subList(x.size() - 1, x.size()) + // but not also here! + return x.size() +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/twoArgs.txt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/twoArgs.txt new file mode 100644 index 00000000000..475ed3cb394 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/twoArgs.txt @@ -0,0 +1,3 @@ +package + +internal fun calc(/*0*/ x: kotlin.List?): kotlin.Int diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index b8fac9bdeea..ecfc8d44c6a 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -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)