diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestWithStdlibGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestWithStdlibGenerated.java index ba62289ffd5..928fda2a2bb 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestWithStdlibGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestWithStdlibGenerated.java @@ -1360,6 +1360,11 @@ public class FirOldFrontendDiagnosticsTestWithStdlibGenerated extends AbstractFi runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/extensionReceiver_after.kt"); } + @TestMetadata("externalArguments.kt") + public void testExternalArguments() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/externalArguments.kt"); + } + @TestMetadata("intersectingInfo.kt") public void testIntersectingInfo() throws Exception { runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/intersectingInfo.kt"); @@ -1508,6 +1513,11 @@ public class FirOldFrontendDiagnosticsTestWithStdlibGenerated extends AbstractFi KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/when"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @TestMetadata("kt36818.kt") + public void testKt36818() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/when/kt36818.kt"); + } + @TestMetadata("withSubject.kt") public void testWithSubject() throws Exception { runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/when/withSubject.kt"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt index 69e005ab4c4..a3047b46541 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt @@ -251,10 +251,12 @@ class DiagnosticReporterByTrackingStrategy( ) val dataFlowValue = dataFlowValueFactory.createDataFlowValue(expressionArgument.receiver.receiverValue, context) val call = if (call.callElement is KtBinaryExpression) null else call - smartCastManager.checkAndRecordPossibleCast( - dataFlowValue, smartCastDiagnostic.smartCastType, argumentExpression, context, call, - recordExpressionType = false - ) + if (!expressionArgument.valueArgument.isExternal()) { + smartCastManager.checkAndRecordPossibleCast( + dataFlowValue, smartCastDiagnostic.smartCastType, argumentExpression, context, call, + recordExpressionType = false + ) + } else null } is ReceiverExpressionKotlinCallArgument -> { trace.markAsReported() diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt index b67f5d86775..576485155a2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt @@ -130,7 +130,7 @@ class KotlinResolutionCallbacksImpl( return createSimplePSICallArgument( trace.bindingContext, outerCallContext.statementFilter, outerCallContext.scope.ownerDescriptor, CallMaker.makeExternalValueArgument(ktExpression), DataFlowInfo.EMPTY, typeInfo, languageVersionSettings, - dataFlowValueFactory + dataFlowValueFactory, outerCallContext.call ) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt index bd1de7dc5eb..d58f10a1cec 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt @@ -318,13 +318,15 @@ class KotlinToResolvedCallTransformer( ) } - updateRecordedType( - argumentExpression, - parameter, - newContext, - constantConvertedArgument?.unknownIntegerType?.unwrap(), - resolvedCall.isReallySuccess() - ) + if (!valueArgument.isExternal()) { + updateRecordedType( + argumentExpression, + parameter, + newContext, + constantConvertedArgument?.unknownIntegerType?.unwrap(), + resolvedCall.isReallySuccess() + ) + } } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewCallArguments.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewCallArguments.kt index 8a5bfecd665..6a294effd75 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewCallArguments.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewCallArguments.kt @@ -303,7 +303,8 @@ internal fun createSimplePSICallArgument( contextForArgument.scope.ownerDescriptor, valueArgument, contextForArgument.dataFlowInfo, typeInfoForArgument, contextForArgument.languageVersionSettings, - contextForArgument.dataFlowValueFactory + contextForArgument.dataFlowValueFactory, + contextForArgument.call, ) internal fun createSimplePSICallArgument( @@ -314,7 +315,8 @@ internal fun createSimplePSICallArgument( dataFlowInfoBeforeThisArgument: DataFlowInfo, typeInfoForArgument: KotlinTypeInfo, languageVersionSettings: LanguageVersionSettings, - dataFlowValueFactory: DataFlowValueFactory + dataFlowValueFactory: DataFlowValueFactory, + call: Call ): SimplePSIKotlinCallArgument? { val ktExpression = KtPsiUtil.getLastElementDeparenthesized(valueArgument.getArgumentExpression(), statementFilter) ?: return null @@ -331,10 +333,10 @@ internal fun createSimplePSICallArgument( // so we use a fast-path here to avoid calling transformToReceiverWithSmartCastInfo function ReceiverValueWithSmartCastInfo(expressionReceiver, emptySet(), isStable = true) } else { + val useDataFlowInfoBeforeArgument = call.callType == Call.CallType.CONTAINS transformToReceiverWithSmartCastInfo( ownerDescriptor, bindingContext, - // dataFlowInfoBeforeThisArgument cannot be used here, because of if() { if (x != null) return; x } - typeInfoForArgument.dataFlowInfo, + if (useDataFlowInfoBeforeArgument) dataFlowInfoBeforeThisArgument else typeInfoForArgument.dataFlowInfo, expressionReceiver, languageVersionSettings, dataFlowValueFactory 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 b59ec9ca5ac..51a7124e2d1 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -1402,9 +1402,14 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { ExpressionReceiver receiver = safeGetExpressionReceiver(facade, right, contextWithNoExpectedType); ExpressionTypingContext contextWithDataFlow = context.replaceDataFlowInfo(dataFlowInfo); + Call containsCall = CallMaker.makeCall( + callElement, receiver, null, operationSign, + Collections.singletonList(leftArgument), Call.CallType.CONTAINS + ); + OverloadResolutionResults resolutionResult = components.callResolver.resolveCallWithGivenName( contextWithDataFlow, - CallMaker.makeCall(callElement, receiver, null, operationSign, Collections.singletonList(leftArgument)), + containsCall, operationSign, OperatorNameConventions.CONTAINS); KotlinType containsType = OverloadResolutionResultsUtil.getResultingType(resolutionResult, context); diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/Call.java b/compiler/psi/src/org/jetbrains/kotlin/psi/Call.java index a535b4fb8e8..8e1d8dafb97 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/Call.java +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/Call.java @@ -67,7 +67,7 @@ public interface Call { KtElement getCallElement(); enum CallType { - DEFAULT, ARRAY_GET_METHOD, ARRAY_SET_METHOD, INVOKE + DEFAULT, ARRAY_GET_METHOD, ARRAY_SET_METHOD, INVOKE, CONTAINS } @NotNull diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/externalArguments.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/externalArguments.fir.kt new file mode 100644 index 00000000000..de95bc6b4e1 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/externalArguments.fir.kt @@ -0,0 +1,37 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE +// !WITH_NEW_INFERENCE + +import kotlin.reflect.KProperty + +fun testLambdaArgumentSmartCast(foo: Int?) { + val v = run { + if (foo != null) + return@run foo + 15 + } +} + +class D { + operator fun getValue(ref: Any?, property: KProperty<*>): Int = 42 +} + +fun testSmartCastInDelegate(d: D?) { + if (d == null) return + val v: Int by d +} + +fun testFunctionCallSmartcast(fn: (() -> Unit)?) { + if (fn == null) return + + fn() +} + +fun testCallableRefernceSmartCast() { + fun forReference() {} + + val refernece = if (true) ::forReference else null + if (refernece == null) + return + + refernece() +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/externalArguments.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/externalArguments.kt new file mode 100644 index 00000000000..29b5a273c70 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/externalArguments.kt @@ -0,0 +1,37 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE +// !WITH_NEW_INFERENCE + +import kotlin.reflect.KProperty + +fun testLambdaArgumentSmartCast(foo: Int?) { + val v = run { + if (foo != null) + return@run foo + 15 + } +} + +class D { + operator fun getValue(ref: Any?, property: KProperty<*>): Int = 42 +} + +fun testSmartCastInDelegate(d: D?) { + if (d == null) return + val v: Int by d +} + +fun testFunctionCallSmartcast(fn: (() -> Unit)?) { + if (fn == null) return + + fn() +} + +fun testCallableRefernceSmartCast() { + fun forReference() {} + + val refernece = if (true) ::forReference else null + if (refernece == null) + return + + refernece() +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/externalArguments.txt b/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/externalArguments.txt new file mode 100644 index 00000000000..ecd0195fc1d --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/externalArguments.txt @@ -0,0 +1,14 @@ +package + +public fun testCallableRefernceSmartCast(): kotlin.Unit +public fun testFunctionCallSmartcast(/*0*/ fn: (() -> kotlin.Unit)?): kotlin.Unit +public fun testLambdaArgumentSmartCast(/*0*/ foo: kotlin.Int?): kotlin.Unit +public fun testSmartCastInDelegate(/*0*/ d: D?): kotlin.Unit + +public final class D { + public constructor D() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final operator fun getValue(/*0*/ ref: kotlin.Any?, /*1*/ property: kotlin.reflect.KProperty<*>): kotlin.Int + 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/testsWithStdLib/contracts/smartcasts/when/kt36818.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/when/kt36818.fir.kt new file mode 100644 index 00000000000..100afc833b4 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/when/kt36818.fir.kt @@ -0,0 +1,11 @@ +fun main(x1: Double?, range: ClosedRange) { + when (x1) { + null -> throw Exception() + in range -> {} // error, no smartcast from previous branch, OK in OI + } + + when { + x1 == null -> throw Exception() + x1 in range -> {} + } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/when/kt36818.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/when/kt36818.kt new file mode 100644 index 00000000000..f4f31f677c4 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/when/kt36818.kt @@ -0,0 +1,11 @@ +fun main(x1: Double?, range: ClosedRange) { + when (x1) { + null -> throw Exception() + in range -> {} // error, no smartcast from previous branch, OK in OI + } + + when { + x1 == null -> throw Exception() + x1 in range -> {} + } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/when/kt36818.txt b/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/when/kt36818.txt new file mode 100644 index 00000000000..e847e6ea490 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/when/kt36818.txt @@ -0,0 +1,3 @@ +package + +public fun main(/*0*/ x1: kotlin.Double?, /*1*/ range: kotlin.ranges.ClosedRange): kotlin.Unit diff --git a/compiler/testData/ir/irText/expressions/when.txt b/compiler/testData/ir/irText/expressions/when.txt index 4414cd610fb..e09bfd0cf88 100644 --- a/compiler/testData/ir/irText/expressions/when.txt +++ b/compiler/testData/ir/irText/expressions/when.txt @@ -47,10 +47,11 @@ FILE fqName: fileName:/when.kt then: CONST String type=kotlin.String value="!Number" BRANCH if: CALL 'public final fun contains (element: T of kotlin.collections.contains): kotlin.Boolean [operator] declared in kotlin.collections' type=kotlin.Boolean origin=IN - : kotlin.Any? + : kotlin.Number $receiver: CALL 'public final fun setOf (): kotlin.collections.Set [inline] declared in kotlin.collections' type=kotlin.collections.Set origin=null : kotlin.Nothing - element: GET_VAR 'val tmp_0: kotlin.Any? [val] declared in .testWithSubject' type=kotlin.Any? origin=null + element: TYPE_OP type=kotlin.Number origin=IMPLICIT_CAST typeOperand=kotlin.Number + GET_VAR 'val tmp_0: kotlin.Any? [val] declared in .testWithSubject' type=kotlin.Any? origin=null then: CONST String type=kotlin.String value="nothingness?" BRANCH if: CONST Boolean type=kotlin.Boolean value=true diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index 950135d3129..ed0eb5d1f0e 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -1361,6 +1361,11 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/extensionReceiver_after.kt"); } + @TestMetadata("externalArguments.kt") + public void testExternalArguments() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/externalArguments.kt"); + } + @TestMetadata("intersectingInfo.kt") public void testIntersectingInfo() throws Exception { runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/intersectingInfo.kt"); @@ -1509,6 +1514,11 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/when"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @TestMetadata("kt36818.kt") + public void testKt36818() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/when/kt36818.kt"); + } + @TestMetadata("withSubject.kt") public void testWithSubject() throws Exception { runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/when/withSubject.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java index cc4342d157a..7db0fb9e672 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java @@ -1361,6 +1361,11 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/extensionReceiver_after.kt"); } + @TestMetadata("externalArguments.kt") + public void testExternalArguments() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/externalArguments.kt"); + } + @TestMetadata("intersectingInfo.kt") public void testIntersectingInfo() throws Exception { runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/intersectingInfo.kt"); @@ -1509,6 +1514,11 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/when"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @TestMetadata("kt36818.kt") + public void testKt36818() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/when/kt36818.kt"); + } + @TestMetadata("withSubject.kt") public void testWithSubject() throws Exception { runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/when/withSubject.kt"); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.kt index 151337cd95e..50eafe54d5f 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.kt @@ -257,8 +257,14 @@ object SuperCallCase : FunctionCallCase() { } object DynamicInvokeAndBracketAccessCallCase : FunctionCallCase() { - fun canApply(callInfo: FunctionCallInfo): Boolean = - callInfo.resolvedCall.call.callType != Call.CallType.DEFAULT && callInfo.callableDescriptor.isDynamic() + fun canApply(callInfo: FunctionCallInfo): Boolean { + if (!callInfo.callableDescriptor.isDynamic()) + return false + val callType = callInfo.resolvedCall.call.callType + return callType == Call.CallType.ARRAY_GET_METHOD + || callType == Call.CallType.ARRAY_SET_METHOD + || callType == Call.CallType.INVOKE + } override fun FunctionCallInfo.dispatchReceiver(): JsExpression { val arguments = argumentsInfo.translateArguments