From 51edf2b35112f82def7de736525665ad7b2408d9 Mon Sep 17 00:00:00 2001 From: Victor Petukhov Date: Thu, 13 Feb 2020 16:09:50 +0300 Subject: [PATCH] NI: introduce warning about implicitly inferred Nothing with existing non-Nothing expected type ^KT-35406 Fixed --- ...irOldFrontendDiagnosticsTestGenerated.java | 11 +- .../jetbrains/kotlin/diagnostics/Errors.java | 3 +- .../kotlin/diagnostics/diagnosticUtils.kt | 12 ++ .../rendering/DefaultErrorMessages.java | 3 +- ...plicitNothingAsTypeParameterCallChecker.kt | 144 ++++++++++++++++-- .../genericFunctionsWithNullableTypes.kt | 2 +- .../lambdaWithVariableAndNothing.kt | 4 +- .../completion/nothingFromNestedCall.kt | 2 +- .../discriminateNothingForReifiedParameter.kt | 8 +- ...othingAgainstNotNothingExpectedType.fir.kt | 105 +++++++++++++ ...citNothingAgainstNotNothingExpectedType.kt | 105 +++++++++++++ ...itNothingAgainstNotNothingExpectedType.txt | 21 +++ ...=> implicitNothingInReturnPosition.fir.kt} | 0 ....kt => implicitNothingInReturnPosition.kt} | 8 +- .../implicitNothingOnDelegates.kt | 2 +- .../diagnostics/notLinked/dfa/pos/12.kt | 4 +- .../checkers/DiagnosticsTestGenerated.java | 11 +- .../DiagnosticsUsingJavacTestGenerated.java | 11 +- 18 files changed, 414 insertions(+), 42 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/typeParameters/implicitNothingAgainstNotNothingExpectedType.fir.kt create mode 100644 compiler/testData/diagnostics/tests/typeParameters/implicitNothingAgainstNotNothingExpectedType.kt create mode 100644 compiler/testData/diagnostics/tests/typeParameters/implicitNothingAgainstNotNothingExpectedType.txt rename compiler/testData/diagnostics/tests/typeParameters/{implicitNothingAsTypeParameter.fir.kt => implicitNothingInReturnPosition.fir.kt} (100%) rename compiler/testData/diagnostics/tests/typeParameters/{implicitNothingAsTypeParameter.kt => implicitNothingInReturnPosition.kt} (85%) diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java index c9ea1000714..bd4583913e7 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java @@ -23347,9 +23347,14 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte runTest("compiler/testData/diagnostics/tests/typeParameters/functionTypeAsUpperBound.kt"); } - @TestMetadata("implicitNothingAsTypeParameter.kt") - public void testImplicitNothingAsTypeParameter() throws Exception { - runTest("compiler/testData/diagnostics/tests/typeParameters/implicitNothingAsTypeParameter.kt"); + @TestMetadata("implicitNothingAgainstNotNothingExpectedType.kt") + public void testImplicitNothingAgainstNotNothingExpectedType() throws Exception { + runTest("compiler/testData/diagnostics/tests/typeParameters/implicitNothingAgainstNotNothingExpectedType.kt"); + } + + @TestMetadata("implicitNothingInReturnPosition.kt") + public void testImplicitNothingInReturnPosition() throws Exception { + runTest("compiler/testData/diagnostics/tests/typeParameters/implicitNothingInReturnPosition.kt"); } @TestMetadata("implicitNothingOnDelegates.kt") diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index f9c6ae8ec17..e12f31f6423 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -780,7 +780,8 @@ public interface Errors { DiagnosticFactory0 TYPE_INFERENCE_FAILED_ON_SPECIAL_CONSTRUCT = DiagnosticFactory0.create(ERROR, SPECIAL_CONSTRUCT_TOKEN); - DiagnosticFactory0 IMPLICIT_NOTHING_AS_TYPE_PARAMETER = DiagnosticFactory0.create(WARNING); + DiagnosticFactory0 IMPLICIT_NOTHING_TYPE_ARGUMENT_IN_RETURN_POSITION = DiagnosticFactory0.create(WARNING); + DiagnosticFactory0 IMPLICIT_NOTHING_TYPE_ARGUMENT_AGAINST_NOT_NOTHING_EXPECTED_TYPE = DiagnosticFactory0.create(WARNING); // Reflection diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/diagnosticUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/diagnosticUtils.kt index c6a4957c83f..156d00bec73 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/diagnosticUtils.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/diagnosticUtils.kt @@ -117,6 +117,18 @@ fun BindingTrace.reportDiagnosticOnce(diagnostic: Diagnostic) { report(diagnostic) } +fun BindingTrace.reportDiagnosticOnceWrtDiagnosticFactoryList( + diagnosticToReport: Diagnostic, + vararg diagnosticFactories: DiagnosticFactory<*>, +) { + val hasAlreadyReportedDiagnosticFromListOrSameType = bindingContext.diagnostics.forElement(diagnosticToReport.psiElement) + .any { diagnostic -> diagnostic.factory == diagnosticToReport.factory || diagnosticFactories.any { it == diagnostic.factory } } + + if (hasAlreadyReportedDiagnosticFromListOrSameType) return + + report(diagnosticToReport) +} + class TypeMismatchDueToTypeProjectionsData( val expectedType: KotlinType, val expressionType: KotlinType, diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 047486ff380..f5ad72b9c60 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -1001,7 +1001,8 @@ public class DefaultErrorMessages { MAP.put(ILLEGAL_SUSPEND_PROPERTY_ACCESS, "Suspend property ''{0}'' should be accessed only from a coroutine or suspend function", NAME); MAP.put(ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL, "Restricted suspending functions can only invoke member or extension suspending functions on their restricted coroutine scope"); MAP.put(NON_MODIFIER_FORM_FOR_BUILT_IN_SUSPEND, "''suspend'' function can only be called in a form of modifier of a lambda: suspend { ... }"); - MAP.put(IMPLICIT_NOTHING_AS_TYPE_PARAMETER, "One of the type variables was implicitly inferred to Nothing. Please, specify type arguments explicitly to hide this warning."); + MAP.put(IMPLICIT_NOTHING_TYPE_ARGUMENT_IN_RETURN_POSITION, "Returning type parameter has been inferred to Nothing implicitly. Please, specify type arguments explicitly to hide this warning. Nothing can produce an exception at runtime."); + MAP.put(IMPLICIT_NOTHING_TYPE_ARGUMENT_AGAINST_NOT_NOTHING_EXPECTED_TYPE, "Returning type parameter has been inferred to Nothing implicitly because Nothing is more specific than specified expected type. Please specify type arguments explicitly in accordance with expected type to hide this warning. Nothing can produce an exception at runtime."); MAP.put(RETURN_FOR_BUILT_IN_SUSPEND, "Using implicit label for this lambda is prohibited"); MAP.put(MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND, "Calls having a form of ''suspend {}'' are deprecated because ''suspend'' in the context will have a meaning of a modifier. Add empty argument list to the call: ''suspend() { ... }''"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/ImplicitNothingAsTypeParameterCallChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/ImplicitNothingAsTypeParameterCallChecker.kt index f74bebff00e..535ca857115 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/ImplicitNothingAsTypeParameterCallChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/ImplicitNothingAsTypeParameterCallChecker.kt @@ -9,12 +9,20 @@ import com.intellij.psi.PsiElement import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType import org.jetbrains.kotlin.builtins.isFunctionOrSuspendFunctionType import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.diagnostics.reportDiagnosticOnceWrtDiagnosticFactoryList +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.SPECIAL_FUNCTION_NAMES -import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.calls.callUtil.getParameterForArgument +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.resolve.calls.tower.NewResolvedCallImpl +import org.jetbrains.kotlin.resolve.calls.tower.psiExpression +import org.jetbrains.kotlin.resolve.calls.tower.psiKotlinCall import org.jetbrains.kotlin.types.DeferredType +import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils -import org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils import org.jetbrains.kotlin.types.typeUtil.isNothing +import org.jetbrains.kotlin.types.typeUtil.isNothingOrNullableNothing import org.jetbrains.kotlin.types.typeUtil.isTypeParameter object ImplicitNothingAsTypeParameterCallChecker : CallChecker { @@ -34,27 +42,131 @@ object ImplicitNothingAsTypeParameterCallChecker : CallChecker { * return inv() * } */ - override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { + private fun checkByReturnPositionWithoutExpected( + resolvedCall: ResolvedCall<*>, + reportOn: PsiElement, + context: CallCheckerContext, + ): Boolean { val resultingDescriptor = resolvedCall.resultingDescriptor + val expectedType = context.resolutionContext.expectedType val inferredReturnType = resultingDescriptor.returnType val isBuiltinFunctionalType = resolvedCall.resultingDescriptor.dispatchReceiverParameter?.value?.type?.isBuiltinFunctionalType == true - if (inferredReturnType is DeferredType || isBuiltinFunctionalType) - return - if (resultingDescriptor.name !in SPECIAL_FUNCTION_NAMES && resolvedCall.call.typeArguments.isEmpty()) { - val lambdasFromArgumentsReturnTypes = - resolvedCall.candidateDescriptor.valueParameters.filter { it.type.isFunctionOrSuspendFunctionType } - .map { it.returnType?.arguments?.last()?.type }.toSet() - val unsubstitutedReturnType = resultingDescriptor.original.returnType - val expectedType = context.resolutionContext.expectedType - val hasImplicitNothing = inferredReturnType?.isNothing() == true && - unsubstitutedReturnType?.isTypeParameter() == true && - (TypeUtils.noExpectedType(expectedType) || !expectedType.isNothing()) + if (inferredReturnType is DeferredType || isBuiltinFunctionalType) return false + if (resultingDescriptor.name in SPECIAL_FUNCTION_NAMES || resolvedCall.call.typeArguments.isNotEmpty()) return false - if (hasImplicitNothing && unsubstitutedReturnType !in lambdasFromArgumentsReturnTypes) { - context.trace.report(Errors.IMPLICIT_NOTHING_AS_TYPE_PARAMETER.on(reportOn)) + val lambdasFromArgumentsReturnTypes = + resolvedCall.candidateDescriptor.valueParameters.filter { it.type.isFunctionOrSuspendFunctionType } + .map { it.returnType?.arguments?.last()?.type }.toSet() + val unsubstitutedReturnType = resultingDescriptor.original.returnType + val hasImplicitNothing = inferredReturnType?.isNothing() == true && + unsubstitutedReturnType?.isTypeParameter() == true && + (TypeUtils.noExpectedType(expectedType) || !expectedType.isNothing()) + + if (hasImplicitNothing && unsubstitutedReturnType !in lambdasFromArgumentsReturnTypes) { + context.trace.reportDiagnosticOnceWrtDiagnosticFactoryList( + Errors.IMPLICIT_NOTHING_TYPE_ARGUMENT_IN_RETURN_POSITION.on(reportOn), + Errors.IMPLICIT_NOTHING_TYPE_ARGUMENT_AGAINST_NOT_NOTHING_EXPECTED_TYPE, + ) + return true + } + + return false + } + + private fun ResolvedAtom.getResolvedCallAtom(bindingContext: BindingContext): ResolvedCallAtom? { + if (this is SingleCallResolutionResult) return resultCallAtom + + val resolutionAtom = atom as? KotlinCallArgument ?: return null + val resolvedCall = resolutionAtom.psiExpression.getResolvedCall(bindingContext) + + return if (resolvedCall is NewResolvedCallImpl) resolvedCall.resolvedCallAtom else null + } + + private fun findFunctionsWithImplicitNothingAndReport(resolvedAtoms: List, context: CallCheckerContext): Boolean { + var hasAlreadyReportedAtDepth = false + + for (resolvedAtom in resolvedAtoms) { + val subResolveAtoms = resolvedAtom.subResolvedAtoms + + if (!subResolveAtoms.isNullOrEmpty() && findFunctionsWithImplicitNothingAndReport(subResolveAtoms, context)) { + hasAlreadyReportedAtDepth = true + continue + } + + val resolvedCallAtom = resolvedAtom.getResolvedCallAtom(context.trace.bindingContext) ?: continue + val candidateDescriptor = resolvedCallAtom.candidateDescriptor + val isReturnTypeOwnTypeParameter = candidateDescriptor.typeParameters.any { it.defaultType == candidateDescriptor.returnType } + val isSpecialCall = candidateDescriptor.name in SPECIAL_FUNCTION_NAMES + val hasExplicitTypeArguments = resolvedCallAtom.atom.psiKotlinCall.typeArguments.isNotEmpty() // not required + + if (!isSpecialCall && isReturnTypeOwnTypeParameter && !hasExplicitTypeArguments) { + context.trace.reportDiagnosticOnceWrtDiagnosticFactoryList( + Errors.IMPLICIT_NOTHING_TYPE_ARGUMENT_AGAINST_NOT_NOTHING_EXPECTED_TYPE.on( + resolvedCallAtom.atom.psiKotlinCall.psiCall.run { calleeExpression ?: callElement }, + ), + Errors.IMPLICIT_NOTHING_TYPE_ARGUMENT_IN_RETURN_POSITION, + ) + hasAlreadyReportedAtDepth = true } } + + return hasAlreadyReportedAtDepth + } + + private fun getSubResolvedAtomsToAnalyze( + resolvedCall: ResolvedCall<*>, + expectedType: KotlinType, + bindingContext: BindingContext, + ): List? { + if (resolvedCall !is NewResolvedCallImpl) return null + + val hasNotNothingExpectedType = !TypeUtils.noExpectedType(expectedType) && !expectedType.isNothingOrNullableNothing() + val hasNothingReturnType = resolvedCall.resultingDescriptor.returnType?.isNothingOrNullableNothing() == true + val isSubResolvedAtomsNotEmpty = !resolvedCall.resolvedCallAtom.subResolvedAtoms.isNullOrEmpty() + + if (hasNotNothingExpectedType && hasNothingReturnType && isSubResolvedAtomsNotEmpty) { + return resolvedCall.resolvedCallAtom.subResolvedAtoms + } + + val resolvedAtomsFromArguments = resolvedCall.valueArguments.values.mapNotNull { argument -> + if (argument !is ExpressionValueArgument) return@mapNotNull null + + val resolvedCallForArgument = + argument.valueArgument?.getArgumentExpression()?.getResolvedCall(bindingContext) as? NewResolvedCallImpl + ?: return@mapNotNull null + val expectedTypeForArgument = resolvedCall.getParameterForArgument(argument.valueArgument)?.type ?: return@mapNotNull null + + getSubResolvedAtomsToAnalyze(resolvedCallForArgument, expectedTypeForArgument, bindingContext) + }.flatten() + + val extensionReceiver = resolvedCall.resolvedCallAtom.extensionReceiverArgument?.psiExpression + val resolvedAtomsFromExtensionReceiver = extensionReceiver?.run { + val extensionReceiverResolvedCall = getResolvedCall(bindingContext) + // It's needed to exclude invoke with extension (when resolved call for extension equals to common resolved call) + if (extensionReceiverResolvedCall == resolvedCall) return@run null + + getSubResolvedAtomsToAnalyze( + getResolvedCall(bindingContext) ?: return@run null, + resolvedCall.resultingDescriptor.extensionReceiverParameter?.type ?: return@run null, + bindingContext, + ) + } + + return if (resolvedAtomsFromExtensionReceiver != null) { + resolvedAtomsFromArguments + resolvedAtomsFromExtensionReceiver + } else resolvedAtomsFromArguments + } + + private fun checkAgainstNotNothingExpectedType(resolvedCall: ResolvedCall<*>, context: CallCheckerContext): Boolean { + val subResolvedAtoms = + getSubResolvedAtomsToAnalyze(resolvedCall, context.resolutionContext.expectedType, context.trace.bindingContext) ?: return false + + return findFunctionsWithImplicitNothingAndReport(subResolvedAtoms, context) + } + + override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { + checkByReturnPositionWithoutExpected(resolvedCall, reportOn, context) || checkAgainstNotNothingExpectedType(resolvedCall, context) } } diff --git a/compiler/testData/diagnostics/tests/callableReference/generic/genericFunctionsWithNullableTypes.kt b/compiler/testData/diagnostics/tests/callableReference/generic/genericFunctionsWithNullableTypes.kt index 6c8123287db..5519d74be29 100644 --- a/compiler/testData/diagnostics/tests/callableReference/generic/genericFunctionsWithNullableTypes.kt +++ b/compiler/testData/diagnostics/tests/callableReference/generic/genericFunctionsWithNullableTypes.kt @@ -30,7 +30,7 @@ fun test(x: T) { baz(null, null, ::foo) val s3: Pair = bar(null, null, ::foo) - val s4: Pair = bar(null, null, ::foo) + val s4: Pair = bar(null, null, ::foo) val s5: Pair = bar(1, "", ::foo) val (a1: Int, b1: String) = bar(1, "", ::foo) diff --git a/compiler/testData/diagnostics/tests/inference/completion/lambdaWithVariableAndNothing.kt b/compiler/testData/diagnostics/tests/inference/completion/lambdaWithVariableAndNothing.kt index cc50ca60bde..71f897e9e54 100644 --- a/compiler/testData/diagnostics/tests/inference/completion/lambdaWithVariableAndNothing.kt +++ b/compiler/testData/diagnostics/tests/inference/completion/lambdaWithVariableAndNothing.kt @@ -13,7 +13,7 @@ fun noSmartCast4(arg: E4?, fn: F): E4 = TODO() fun testSmartCast(s: String?) { id( if (s != null) "" - else smartCast(null) { "" } + else smartCast(null) { "" } ) s.length } @@ -46,7 +46,7 @@ fun testNoSmartCast3(s: String?) { fun testNoSmartCast4(s: String?) { id( if (s != null) ( {""} ) - else noSmartCast4(null) { "" } + else noSmartCast4(null) { "" } ) s.length } diff --git a/compiler/testData/diagnostics/tests/inference/completion/nothingFromNestedCall.kt b/compiler/testData/diagnostics/tests/inference/completion/nothingFromNestedCall.kt index 407707c2e8f..03268c239d7 100644 --- a/compiler/testData/diagnostics/tests/inference/completion/nothingFromNestedCall.kt +++ b/compiler/testData/diagnostics/tests/inference/completion/nothingFromNestedCall.kt @@ -9,5 +9,5 @@ fun invOut(y: K?): Inv> = TODO() fun test(x: Inv>): R = TODO() fun testNothing() { - test(invOut(null)) checkType { _() } + test(invOut(null)) checkType { _() } } diff --git a/compiler/testData/diagnostics/tests/inference/nothingType/discriminateNothingForReifiedParameter.kt b/compiler/testData/diagnostics/tests/inference/nothingType/discriminateNothingForReifiedParameter.kt index 24c7d1bc089..71d26d1ee81 100644 --- a/compiler/testData/diagnostics/tests/inference/nothingType/discriminateNothingForReifiedParameter.kt +++ b/compiler/testData/diagnostics/tests/inference/nothingType/discriminateNothingForReifiedParameter.kt @@ -14,7 +14,7 @@ inline fun materializeReifiedUnbound(): M = TODO() fun select(a: T, b: T): T = TODO() fun test1() { - take(null) + take(null) } fun test2() { @@ -22,20 +22,20 @@ fun test2() { } fun test3() { - takeReifiedUnbound(null) + takeReifiedUnbound(null) } fun test4(): Bound = takeReifiedUnbound(null) fun test5(): Bound? = select( null, - materialize() + materialize() ) fun test6() { select( null, - materializeReified() + materializeReified() ) } diff --git a/compiler/testData/diagnostics/tests/typeParameters/implicitNothingAgainstNotNothingExpectedType.fir.kt b/compiler/testData/diagnostics/tests/typeParameters/implicitNothingAgainstNotNothingExpectedType.fir.kt new file mode 100644 index 00000000000..7a8ea91d5bb --- /dev/null +++ b/compiler/testData/diagnostics/tests/typeParameters/implicitNothingAgainstNotNothingExpectedType.fir.kt @@ -0,0 +1,105 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -UNCHECKED_CAST -UNUSED_EXPRESSION -UNREACHABLE_CODE +// !LANGUAGE: +NewInference + +fun materialize(): K = null as K +fun materializeWithGenericArg(x: T): K = null as K + +fun id(x: K): K = null as K +fun K.idFromReceiver(): K = null as K +fun K.idFromReceiverWithArg(x: String): K = null as K + +fun select(x: S, y: S): S = x + +class Foo { + fun idFromClassTypeArg(): T = null as T + fun idFromClassTypeArgWithAnotherTypeArg(): T = null as T + fun materialize(): K = null as K +} + +fun test() { + if (true) materialize() else null + + val x1: String? = if (true) materialize() else null + + val x2: String? = if (true) materializeWithGenericArg("") else null + + val x3: String? = if (true) { + if (true) materialize() else null + } else null + + val x4: String? = if (true) { + select(materialize(), null) + } else null + + val x5: String? = select(if (true) materialize() else null, null) + + val x6: String? = select(materialize(), null) + + val x7: String? = select(null.idFromReceiver(), null) + + val x8: String? = select(null.idFromReceiverWithArg(""), null) + + val foo = Foo() + + val x9: String? = select(foo.materialize(), null) + val x10: String? = select(foo.idFromClassTypeArgWithAnotherTypeArg(), null) + val x11: String? = select(foo.idFromClassTypeArg(), null) + + foo.run { + val x12: String? = select(materialize(), null) + val x13: String? = select(idFromClassTypeArgWithAnotherTypeArg(), null) + val x14: String? = select(idFromClassTypeArg(), null) + } + + val boolean: Boolean? = true + + val x15: String? = when (boolean) { + true -> select(materialize(), null) + false -> select(materialize(), null) + null -> null + } + + val x16: String? = when (boolean) { + true -> null + false -> materialize() + null -> null + } + + val x17: String? = when (boolean) { + true -> if (true) null else materialize() + false -> if (true) materialize() else null + null -> if (true) null else null + } + + val x18: String? = try { + materialize() + } catch (e: Exception) { + null + } + + val x19: String? = if (true) materialize() else null + + val x20: String? = if (true) materialize() else null + + val x21: String? = if (true) materialize() else TODO() + + val x22: String? = if (true) return else materialize() + + val x23: String? = if (true) id(null) else null + + foo1(if (true) materialize() else null) + + val x24 = id(foo1(if (true) materialize() else null)) + + val x25 = select(materialize(), null).foo2() + + // TODO + val x26 = select(materialize(), null).run { foo2() } + + val x27: () -> String? = { + id(id(if (true) materialize() else null)) + } +} + +fun foo1(x: String?) {} +fun String?.foo2() {} diff --git a/compiler/testData/diagnostics/tests/typeParameters/implicitNothingAgainstNotNothingExpectedType.kt b/compiler/testData/diagnostics/tests/typeParameters/implicitNothingAgainstNotNothingExpectedType.kt new file mode 100644 index 00000000000..ef986cec344 --- /dev/null +++ b/compiler/testData/diagnostics/tests/typeParameters/implicitNothingAgainstNotNothingExpectedType.kt @@ -0,0 +1,105 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -UNCHECKED_CAST -UNUSED_EXPRESSION -UNREACHABLE_CODE +// !LANGUAGE: +NewInference + +fun materialize(): K = null as K +fun materializeWithGenericArg(x: T): K = null as K + +fun id(x: K): K = null as K +fun K.idFromReceiver(): K = null as K +fun K.idFromReceiverWithArg(x: String): K = null as K + +fun select(x: S, y: S): S = x + +class Foo { + fun idFromClassTypeArg(): T = null as T + fun idFromClassTypeArgWithAnotherTypeArg(): T = null as T + fun materialize(): K = null as K +} + +fun test() { + if (true) materialize() else null + + val x1: String? = if (true) materialize() else null + + val x2: String? = if (true) materializeWithGenericArg("") else null + + val x3: String? = if (true) { + if (true) materialize() else null + } else null + + val x4: String? = if (true) { + select(materialize(), null) + } else null + + val x5: String? = select(if (true) materialize() else null, null) + + val x6: String? = select(materialize(), null) + + val x7: String? = select(null.idFromReceiver(), null) + + val x8: String? = select(null.idFromReceiverWithArg(""), null) + + val foo = Foo() + + val x9: String? = select(foo.materialize(), null) + val x10: String? = select(foo.idFromClassTypeArgWithAnotherTypeArg(), null) + val x11: String? = select(foo.idFromClassTypeArg(), null) + + foo.run { + val x12: String? = select(materialize(), null) + val x13: String? = select(idFromClassTypeArgWithAnotherTypeArg(), null) + val x14: String? = select(idFromClassTypeArg(), null) + } + + val boolean: Boolean? = true + + val x15: String? = when (boolean) { + true -> select(materialize(), null) + false -> select(materialize(), null) + null -> null + } + + val x16: String? = when (boolean) { + true -> null + false -> materialize() + null -> null + } + + val x17: String? = when (boolean) { + true -> if (true) null else materialize() + false -> if (true) materialize() else null + null -> if (true) null else null + } + + val x18: String? = try { + materialize() + } catch (e: Exception) { + null + } + + val x19: String? = if (true) materialize() else null + + val x20: String? = if (true) materialize() else null + + val x21: String? = if (true) materialize() else TODO() + + val x22: String? = if (true) return else materialize() + + val x23: String? = if (true) id(null) else null + + foo1(if (true) materialize() else null) + + val x24 = id(foo1(if (true) materialize() else null)) + + val x25 = select(materialize(), null).foo2() + + // TODO + val x26 = select(materialize(), null).run { foo2() } + + val x27: () -> String? = { + id(id(if (true) materialize() else null)) + } +} + +fun foo1(x: String?) {} +fun String?.foo2() {} diff --git a/compiler/testData/diagnostics/tests/typeParameters/implicitNothingAgainstNotNothingExpectedType.txt b/compiler/testData/diagnostics/tests/typeParameters/implicitNothingAgainstNotNothingExpectedType.txt new file mode 100644 index 00000000000..e08c69b96b2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/typeParameters/implicitNothingAgainstNotNothingExpectedType.txt @@ -0,0 +1,21 @@ +package + +public fun foo1(/*0*/ x: kotlin.String?): kotlin.Unit +public fun id(/*0*/ x: K): K +public fun materialize(): K +public fun materializeWithGenericArg(/*0*/ x: T): K +public fun select(/*0*/ x: S, /*1*/ y: S): S +public fun test(): kotlin.Unit +public fun kotlin.String?.foo2(): kotlin.Unit +public fun K.idFromReceiver(): K +public fun K.idFromReceiverWithArg(/*0*/ x: kotlin.String): K + +public final class Foo { + public constructor Foo() + 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 final fun idFromClassTypeArg(): T + public final fun idFromClassTypeArgWithAnotherTypeArg(): T + public final fun materialize(): K + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/typeParameters/implicitNothingAsTypeParameter.fir.kt b/compiler/testData/diagnostics/tests/typeParameters/implicitNothingInReturnPosition.fir.kt similarity index 100% rename from compiler/testData/diagnostics/tests/typeParameters/implicitNothingAsTypeParameter.fir.kt rename to compiler/testData/diagnostics/tests/typeParameters/implicitNothingInReturnPosition.fir.kt diff --git a/compiler/testData/diagnostics/tests/typeParameters/implicitNothingAsTypeParameter.kt b/compiler/testData/diagnostics/tests/typeParameters/implicitNothingInReturnPosition.kt similarity index 85% rename from compiler/testData/diagnostics/tests/typeParameters/implicitNothingAsTypeParameter.kt rename to compiler/testData/diagnostics/tests/typeParameters/implicitNothingInReturnPosition.kt index dc748041ba2..5006759104e 100644 --- a/compiler/testData/diagnostics/tests/typeParameters/implicitNothingAsTypeParameter.kt +++ b/compiler/testData/diagnostics/tests/typeParameters/implicitNothingInReturnPosition.kt @@ -40,7 +40,7 @@ class Context fun Any.decodeIn(typeFrom: Context): T = something() fun Any?.decodeOut1(typeFrom: Context): T { - return this?.decodeIn(typeFrom) ?: kotlin.Unit + return this?.decodeIn(typeFrom) ?: kotlin.Unit } fun Any.decodeOut2(typeFrom: Context): T { @@ -48,11 +48,11 @@ fun Any.decodeOut2(typeFrom: Context): T { } fun Any.decodeOut3(typeFrom: Context): T { - val x = this.decodeIn(typeFrom) + val x = this.decodeIn(typeFrom) } fun Any.decodeOut4(typeFrom: Context): T { - val x: Any = this.decodeIn(typeFrom) + val x: Any = this.decodeIn(typeFrom) } class TrieNode { @@ -91,7 +91,7 @@ interface Worker interface RenderContext val emptyOrNull: List? = null -val x = emptyOrNull?.get(0) +val x = emptyOrNull?.get(0) val errorCompletion = { e: Throwable -> throw Exception() } diff --git a/compiler/testData/diagnostics/tests/typeParameters/implicitNothingOnDelegates.kt b/compiler/testData/diagnostics/tests/typeParameters/implicitNothingOnDelegates.kt index c545e095951..dc200ea99da 100644 --- a/compiler/testData/diagnostics/tests/typeParameters/implicitNothingOnDelegates.kt +++ b/compiler/testData/diagnostics/tests/typeParameters/implicitNothingOnDelegates.kt @@ -18,6 +18,6 @@ private object Scope { fun materialize(): T = Any() as T fun test(i: Inv) { - val p: Int by Scope.Delegate(i) + val p: Int by Scope.Delegate(i) } } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/12.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/12.kt index 67dac68bbf2..448bc09db02 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/12.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/12.kt @@ -3831,12 +3831,12 @@ fun Nothing.case_63() { this.hashCode() hashCode() - apply { + apply { this hashCode() this.hashCode() } - also { + also { it it.hashCode() } diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 73f8ec451c9..1f07b9d8dae 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -23429,9 +23429,14 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { runTest("compiler/testData/diagnostics/tests/typeParameters/functionTypeAsUpperBound.kt"); } - @TestMetadata("implicitNothingAsTypeParameter.kt") - public void testImplicitNothingAsTypeParameter() throws Exception { - runTest("compiler/testData/diagnostics/tests/typeParameters/implicitNothingAsTypeParameter.kt"); + @TestMetadata("implicitNothingAgainstNotNothingExpectedType.kt") + public void testImplicitNothingAgainstNotNothingExpectedType() throws Exception { + runTest("compiler/testData/diagnostics/tests/typeParameters/implicitNothingAgainstNotNothingExpectedType.kt"); + } + + @TestMetadata("implicitNothingInReturnPosition.kt") + public void testImplicitNothingInReturnPosition() throws Exception { + runTest("compiler/testData/diagnostics/tests/typeParameters/implicitNothingInReturnPosition.kt"); } @TestMetadata("implicitNothingOnDelegates.kt") diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 9d13b211964..9a8269ac0d8 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -23349,9 +23349,14 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/typeParameters/functionTypeAsUpperBound.kt"); } - @TestMetadata("implicitNothingAsTypeParameter.kt") - public void testImplicitNothingAsTypeParameter() throws Exception { - runTest("compiler/testData/diagnostics/tests/typeParameters/implicitNothingAsTypeParameter.kt"); + @TestMetadata("implicitNothingAgainstNotNothingExpectedType.kt") + public void testImplicitNothingAgainstNotNothingExpectedType() throws Exception { + runTest("compiler/testData/diagnostics/tests/typeParameters/implicitNothingAgainstNotNothingExpectedType.kt"); + } + + @TestMetadata("implicitNothingInReturnPosition.kt") + public void testImplicitNothingInReturnPosition() throws Exception { + runTest("compiler/testData/diagnostics/tests/typeParameters/implicitNothingInReturnPosition.kt"); } @TestMetadata("implicitNothingOnDelegates.kt")