From d932d5b0a51f920418a28ed2ed92b2a23af41d3d Mon Sep 17 00:00:00 2001 From: "Denis.Zharkov" Date: Wed, 26 May 2021 17:44:05 +0300 Subject: [PATCH] FIR: Support adding expect type to calls in `foo() as R` position See https://kotlinlang.org/docs/whatsnew12.html#support-for-foo-as-a-shorthand-for-this-foo --- ...irOldFrontendDiagnosticsTestGenerated.java | 6 ++ ...DiagnosticsWithLightTreeTestGenerated.java | 6 ++ .../FirUselessTypeOperationCallChecker.kt | 10 +++- .../kotlin/fir/resolve/ResolutionMode.kt | 7 ++- .../fir/resolve/inference/FirCallCompleter.kt | 60 +++++++++++++++---- .../FirDeclarationsResolveTransformer.kt | 4 +- .../FirExpressionsResolveTransformer.kt | 33 +++++++++- .../box/reified/expectedTypeFromCast.kt | 1 - .../commonCaseForInference.fir.kt | 2 +- .../expectedTypeDoubleReceiver.fir.kt | 6 +- .../inference/expectedTypeFromCast.fir.kt | 10 ++-- .../expectedTypeFromCastComplexExpression.kt | 6 +- .../expectedTypeFromCastParenthesized.fir.kt | 10 ++-- .../inference/expectedTypeWithGenerics.fir.kt | 6 +- .../expectedTypeWithGenericsSafeCalls.kt | 11 ++++ .../tests/inference/findViewById.fir.kt | 58 ++++++++++++++++++ .../tests/inference/findViewById.kt | 17 +++--- .../diagnostics/tests/inference/kt30405.kt | 4 +- .../test/runners/DiagnosticTestGenerated.java | 6 ++ ...CompilerTestFE10TestdataTestGenerated.java | 5 ++ 20 files changed, 219 insertions(+), 49 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/inference/expectedTypeWithGenericsSafeCalls.kt create mode 100644 compiler/testData/diagnostics/tests/inference/findViewById.fir.kt diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index 0fb8136376c..e6fbe1bed71 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -12197,6 +12197,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/inference/expectedTypeWithGenerics.kt"); } + @Test + @TestMetadata("expectedTypeWithGenericsSafeCalls.kt") + public void testExpectedTypeWithGenericsSafeCalls() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/expectedTypeWithGenericsSafeCalls.kt"); + } + @Test @TestMetadata("extensionLambdasAndArrow.kt") public void testExtensionLambdasAndArrow() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index c8ab746a16d..156f0caa93b 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -12197,6 +12197,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/inference/expectedTypeWithGenerics.kt"); } + @Test + @TestMetadata("expectedTypeWithGenericsSafeCalls.kt") + public void testExpectedTypeWithGenericsSafeCalls() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/expectedTypeWithGenericsSafeCalls.kt"); + } + @Test @TestMetadata("extensionLambdasAndArrow.kt") public void testExtensionLambdasAndArrow() throws Exception { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirUselessTypeOperationCallChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirUselessTypeOperationCallChecker.kt index ed1343ef196..ea181225553 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirUselessTypeOperationCallChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirUselessTypeOperationCallChecker.kt @@ -9,8 +9,10 @@ import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn +import org.jetbrains.kotlin.fir.declarations.FirFunction import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.resolve.fullyExpandedType +import org.jetbrains.kotlin.fir.resolve.inference.isFunctionForExpectTypeFromCastFeature import org.jetbrains.kotlin.fir.typeContext import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.types.AbstractTypeChecker @@ -34,7 +36,7 @@ object FirUselessTypeOperationCallChecker : FirTypeOperatorCallChecker() { } else { targetType } - if (isRefinementUseless(context, candidateType, refinedTargetType, shouldCheckForExactType(expression, context))) { + if (isRefinementUseless(context, candidateType, refinedTargetType, shouldCheckForExactType(expression, context), arg)) { when (expression.operation) { FirOperation.IS -> reporter.reportOn(expression.source, FirErrors.USELESS_IS_CHECK, true, context) FirOperation.NOT_IS -> reporter.reportOn(expression.source, FirErrors.USELESS_IS_CHECK, false, context) @@ -62,8 +64,14 @@ object FirUselessTypeOperationCallChecker : FirTypeOperatorCallChecker() { candidateType: ConeKotlinType, targetType: ConeKotlinType, shouldCheckForExactType: Boolean, + arg: FirExpression, ): Boolean { return if (shouldCheckForExactType) { + if (arg is FirFunctionCall) { + val function = arg.toResolvedCallableSymbol()?.fir as? FirFunction + if (function != null && function.isFunctionForExpectTypeFromCastFeature()) return false + } + isExactTypeCast(context, candidateType, targetType) } else { isUpcast(context, candidateType, targetType) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt index a553e5322e6..4b0569e09ca 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt @@ -26,11 +26,16 @@ sealed class ResolutionMode { class WithStatus(val status: FirDeclarationStatus) : ResolutionMode() class LambdaResolution(val expectedReturnTypeRef: FirResolvedTypeRef?) : ResolutionMode() + + class WithExpectedTypeFromCast( + val expectedTypeRef: FirTypeRef, + ) : ResolutionMode() } -fun ResolutionMode.expectedType(components: BodyResolveComponents): FirTypeRef? = when (this) { +fun ResolutionMode.expectedType(components: BodyResolveComponents, allowFromCast: Boolean = false): FirTypeRef? = when (this) { is ResolutionMode.WithExpectedType -> expectedTypeRef is ResolutionMode.ContextIndependent -> components.noExpectedType + is ResolutionMode.WithExpectedTypeFromCast -> expectedTypeRef.takeIf { allowFromCast } else -> null } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt index 67fb922668c..8af3cb2017c 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt @@ -8,16 +8,14 @@ package org.jetbrains.kotlin.fir.resolve.inference import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin +import org.jetbrains.kotlin.fir.declarations.FirFunction import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirResolvable import org.jetbrains.kotlin.fir.expressions.FirStatement import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment import org.jetbrains.kotlin.fir.resolve.ResolutionMode -import org.jetbrains.kotlin.fir.resolve.calls.Candidate -import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate -import org.jetbrains.kotlin.fir.resolve.calls.ResolutionContext -import org.jetbrains.kotlin.fir.resolve.calls.isUnitOrFlexibleUnit +import org.jetbrains.kotlin.fir.resolve.calls.* import org.jetbrains.kotlin.fir.resolve.expectedType import org.jetbrains.kotlin.fir.resolve.inference.model.ConeArgumentConstraintPosition import org.jetbrains.kotlin.fir.resolve.inference.model.ConeExpectedTypeConstraintPosition @@ -59,20 +57,22 @@ class FirCallCompleter( expectedTypeRef: FirTypeRef?, expectedTypeMismatchIsReportedInChecker: Boolean = false, ): CompletionResult where T : FirResolvable, T : FirStatement = - completeCall(call, expectedTypeRef, mayBeCoercionToUnitApplied = false, expectedTypeMismatchIsReportedInChecker) + completeCall(call, expectedTypeRef, mayBeCoercionToUnitApplied = false, expectedTypeMismatchIsReportedInChecker, isFromCast = false) fun completeCall(call: T, data: ResolutionMode): CompletionResult where T : FirResolvable, T : FirStatement = completeCall( call, - data.expectedType(components), + data.expectedType(components, allowFromCast = true), (data as? ResolutionMode.WithExpectedType)?.mayBeCoercionToUnitApplied == true, (data as? ResolutionMode.WithExpectedType)?.expectedTypeMismatchIsReportedInChecker == true, + isFromCast = data is ResolutionMode.WithExpectedTypeFromCast, ) private fun completeCall( call: T, expectedTypeRef: FirTypeRef?, mayBeCoercionToUnitApplied: Boolean, expectedTypeMismatchIsReportedInChecker: Boolean, + isFromCast: Boolean, ): CompletionResult where T : FirResolvable, T : FirStatement { val typeRef = components.typeFromCallee(call) @@ -93,15 +93,24 @@ class FirCallCompleter( } if (expectedTypeRef is FirResolvedTypeRef) { - val expectedTypeConstraintPosition = ConeExpectedTypeConstraintPosition(expectedTypeMismatchIsReportedInChecker) - if (expectedTypeRef.coneType.isUnitOrFlexibleUnit && mayBeCoercionToUnitApplied) { - if (candidate.system.notFixedTypeVariables.isNotEmpty()) { - candidate.system.addSubtypeConstraintIfCompatible( - initialType, expectedTypeRef.type, expectedTypeConstraintPosition + if (isFromCast) { + if (candidate.isFunctionForExpectTypeFromCastFeature()) { + candidate.system.addSubtypeConstraint( + initialType, expectedTypeRef.type, + ConeExpectedTypeConstraintPosition(expectedTypeMismatchIsReportedInChecker = false), ) } } else { - candidate.system.addSubtypeConstraint(initialType, expectedTypeRef.type, expectedTypeConstraintPosition) + val expectedTypeConstraintPosition = ConeExpectedTypeConstraintPosition(expectedTypeMismatchIsReportedInChecker) + if (expectedTypeRef.coneType.isUnitOrFlexibleUnit && mayBeCoercionToUnitApplied) { + if (candidate.system.notFixedTypeVariables.isNotEmpty()) { + candidate.system.addSubtypeConstraintIfCompatible( + initialType, expectedTypeRef.type, expectedTypeConstraintPosition + ) + } + } else { + candidate.system.addSubtypeConstraint(initialType, expectedTypeRef.type, expectedTypeConstraintPosition) + } } } @@ -288,3 +297,30 @@ class FirCallCompleter( this, TypeApproximatorConfiguration.FinalApproximationAfterResolutionAndInference ) ?: this } + +private fun Candidate.isFunctionForExpectTypeFromCastFeature(): Boolean { + if (typeArgumentMapping != TypeArgumentMapping.NoExplicitArguments) return false + val fir = symbol.fir as? FirFunction ?: return false + + return fir.isFunctionForExpectTypeFromCastFeature() +} + +// Expect type is only being added to calls in a position of cast argument: foo() as R +// And that call should be resolved to something materialize()-like: it returns its single generic parameter and doesn't have value parameters +// fun materialize(): T +fun FirFunction<*>.isFunctionForExpectTypeFromCastFeature(): Boolean { + val typeParameter = typeParameters.singleOrNull() ?: return false + + val returnType = returnTypeRef.coneTypeSafe() ?: return false + + if ((returnType.lowerBoundIfFlexible() as? ConeTypeParameterType)?.lookupTag != typeParameter.symbol.toLookupTag()) return false + + fun FirTypeRef.isBadType() = + coneTypeSafe() + ?.contains { (it.lowerBoundIfFlexible() as? ConeTypeParameterType)?.lookupTag == typeParameter.symbol.toLookupTag() } != false + + if (valueParameters.any { it.returnTypeRef.isBadType() } || receiverTypeRef?.isBadType() == true) return false + + return true +} + diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt index 52cf5e9d2b6..a8bab5ff04a 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt @@ -760,8 +760,8 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor ) lambda.addReturn() } - is ResolutionMode.WithStatus -> { - throw AssertionError("Should not be here in WithStatus mode") + is ResolutionMode.WithStatus, is ResolutionMode.WithExpectedTypeFromCast -> { + throw AssertionError("Should not be here in WithStatus/WithExpectedTypeFromCast mode") } } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt index 949d55fe153..2c3246ad967 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt @@ -594,7 +594,7 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform ): FirStatement { val resolved = components.typeResolverTransformer.withAllowedBareTypes { typeOperatorCall.transformConversionTypeRef(transformer, ResolutionMode.ContextIndependent) - }.transformOtherChildren(transformer, ResolutionMode.ContextIndependent) + }.transformTypeOperatorCallChildren() val conversionTypeRef = resolved.conversionTypeRef.withTypeArgumentsForBareType(resolved.argument) resolved.transformChildren(object : FirDefaultTransformer() { @@ -636,6 +636,37 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform return resolved } + private fun FirTypeOperatorCall.transformTypeOperatorCallChildren(): FirTypeOperatorCall { + if (operation == FirOperation.AS || operation == FirOperation.SAFE_AS) { + val argument = argumentList.arguments.singleOrNull() ?: error("Not a single argument: ${this.render()}") + + // For calls in the form of (materialize() as MyClass) we've got a special rule that adds expect type to the `materialize()` call + // AS operator doesn't add expected type to any other expressions + // See https://kotlinlang.org/docs/whatsnew12.html#support-for-foo-as-a-shorthand-for-this-foo + // And limitations at org.jetbrains.kotlin.fir.resolve.inference.FirCallCompleterKt.isFunctionForExpectTypeFromCastFeature(org.jetbrains.kotlin.fir.declarations.FirFunction) + if (argument is FirFunctionCall || (argument is FirSafeCallExpression && argument.regularQualifiedAccess is FirFunctionCall)) { + val expectedType = conversionTypeRef.coneTypeSafe()?.takeIf { + // is not bare type + it !is ConeClassLikeType || + it.typeArguments.isNotEmpty() || + (it.lookupTag.toSymbol(session)?.fir as? FirTypeParameterRefsOwner)?.typeParameters?.isEmpty() == true + }?.let { + if (operation == FirOperation.SAFE_AS) + it.withNullability(ConeNullability.NULLABLE, session.typeContext) + else + it + } + + if (expectedType != null) { + val newMode = ResolutionMode.WithExpectedTypeFromCast(conversionTypeRef.withReplacedConeType(expectedType)) + return transformOtherChildren(transformer, newMode) + } + } + } + + return transformOtherChildren(transformer, ResolutionMode.ContextIndependent) + } + override fun transformCheckNotNullCall( checkNotNullCall: FirCheckNotNullCall, data: ResolutionMode, diff --git a/compiler/testData/codegen/box/reified/expectedTypeFromCast.kt b/compiler/testData/codegen/box/reified/expectedTypeFromCast.kt index a2e8c4aff38..4318eebe754 100644 --- a/compiler/testData/codegen/box/reified/expectedTypeFromCast.kt +++ b/compiler/testData/codegen/box/reified/expectedTypeFromCast.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR // TARGET_BACKEND: JVM // WITH_RUNTIME diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/commonCaseForInference.fir.kt b/compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/commonCaseForInference.fir.kt index b4a01b0f4e4..b822b045f4f 100644 --- a/compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/commonCaseForInference.fir.kt +++ b/compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/commonCaseForInference.fir.kt @@ -8,6 +8,6 @@ object CommonCase { operator fun Fas.provideDelegate(host: D, p: Any?): Fas = TODO() operator fun Fas.getValue(receiver: E, p: Any?): R = TODO() - val Long.test1: String by delegate() // common test, not working because of Inference1 + val Long.test1: String by delegate() // common test, not working because of Inference1 val Long.test2: String by delegate() // should work } diff --git a/compiler/testData/diagnostics/tests/inference/expectedTypeDoubleReceiver.fir.kt b/compiler/testData/diagnostics/tests/inference/expectedTypeDoubleReceiver.fir.kt index 4698302d0c6..302429632f2 100644 --- a/compiler/testData/diagnostics/tests/inference/expectedTypeDoubleReceiver.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/expectedTypeDoubleReceiver.fir.kt @@ -13,6 +13,6 @@ val asA = foo().foo()).fooA() as A val no2A = A().fooA().fooA() as A -val correct1 = A().fooA() as A -val correct2 = foo().fooA() as A -val correct3 = A().fooA().fooA() as A +val correct1 = A().fooA() as A +val correct2 = foo().fooA() as A +val correct3 = A().fooA().fooA() as A diff --git a/compiler/testData/diagnostics/tests/inference/expectedTypeFromCast.fir.kt b/compiler/testData/diagnostics/tests/inference/expectedTypeFromCast.fir.kt index 32f182a4bff..ea67e02b73d 100644 --- a/compiler/testData/diagnostics/tests/inference/expectedTypeFromCast.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/expectedTypeFromCast.fir.kt @@ -4,17 +4,17 @@ fun foo(): T = TODO() fun id(value: V) = value -val asString = foo() as String +val asString = foo() as String val viaId = id(foo()) as String -val insideId = id(foo() as String) +val insideId = id(foo() as String) -val asList = foo() as List +val asList = foo() as List -val asStarList = foo() as List<*> +val asStarList = foo() as List<*> -val safeAs = foo() as? String +val safeAs = foo() as? String val fromIs = foo() is String val fromNoIs = foo() !is String diff --git a/compiler/testData/diagnostics/tests/inference/expectedTypeFromCastComplexExpression.kt b/compiler/testData/diagnostics/tests/inference/expectedTypeFromCastComplexExpression.kt index ac08a6cf509..8ec0196aee5 100644 --- a/compiler/testData/diagnostics/tests/inference/expectedTypeFromCastComplexExpression.kt +++ b/compiler/testData/diagnostics/tests/inference/expectedTypeFromCastComplexExpression.kt @@ -11,6 +11,6 @@ class A { } } -val x = A().foo() as String -val y = A.foo2() as String -val z = pp.A.foo2() as String +val x = A().foo() as String +val y = A.foo2() as String +val z = pp.A.foo2() as String diff --git a/compiler/testData/diagnostics/tests/inference/expectedTypeFromCastParenthesized.fir.kt b/compiler/testData/diagnostics/tests/inference/expectedTypeFromCastParenthesized.fir.kt index 807e949989f..2fcb8d78216 100644 --- a/compiler/testData/diagnostics/tests/inference/expectedTypeFromCastParenthesized.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/expectedTypeFromCastParenthesized.fir.kt @@ -8,15 +8,15 @@ fun foo(): T = TODO() fun id(value: V) = value -val par1 = (foo()) as String -val par2 = ((foo())) as String +val par1 = (foo()) as String +val par2 = ((foo())) as String -val par3 = (dd@ (foo())) as String +val par3 = (dd@ (foo())) as String -val par4 = ( @bar() (foo())) as String +val par4 = ( @bar() (foo())) as String object X { fun foo(): T = TODO() } -val par5 = ( @bar() X.foo()) as String +val par5 = ( @bar() X.foo()) as String diff --git a/compiler/testData/diagnostics/tests/inference/expectedTypeWithGenerics.fir.kt b/compiler/testData/diagnostics/tests/inference/expectedTypeWithGenerics.fir.kt index 039bd2c4f46..01f06e0c623 100644 --- a/compiler/testData/diagnostics/tests/inference/expectedTypeWithGenerics.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/expectedTypeWithGenerics.fir.kt @@ -5,13 +5,13 @@ class X { } fun test(x: X) { - val y = x.foo() as Int + val y = x.foo() as Int } fun g() { fun foo(): T = TODO() - val y = foo() as Int + val y = foo() as Int - val y2 = foo() as D + val y2 = foo() as D } diff --git a/compiler/testData/diagnostics/tests/inference/expectedTypeWithGenericsSafeCalls.kt b/compiler/testData/diagnostics/tests/inference/expectedTypeWithGenericsSafeCalls.kt new file mode 100644 index 00000000000..b8e8f78f928 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/expectedTypeWithGenericsSafeCalls.kt @@ -0,0 +1,11 @@ +// FIR_IDENTICAL +// SKIP_TXT +// !LANGUAGE: +ExpectedTypeFromCast + +class X { + fun foo(): T = TODO() +} + +fun test(x: X?) { + val y = x?.foo() as Int +} diff --git a/compiler/testData/diagnostics/tests/inference/findViewById.fir.kt b/compiler/testData/diagnostics/tests/inference/findViewById.fir.kt new file mode 100644 index 00000000000..79e20e83328 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/findViewById.fir.kt @@ -0,0 +1,58 @@ +// !LANGUAGE: +ExpectedTypeFromCast +// !DIAGNOSTICS: -UNUSED_VARIABLE -DEBUG_INFO_LEAKING_THIS + +// FILE: a/View.java +package a; + +public class View { + +} + +// FILE: a/Test.java +package a; + +public class Test { + public T findViewById(int id); +} + +// FILE: 1.kt +package a + + +class X : View() + +class Y : View() + +val xExplicit: X = Test().findViewById(0) +val xCast = Test().findViewById(0) as X + +val xCastExplicitType = Test().findViewById(0) as X +val xSafeCastExplicitType = Test().findViewById(0) as? X + +val yExplicit: Y = Test().findViewById(0) +val yCast = Test().findViewById(0) as Y + + +class TestChild : Test() { + val xExplicit: X = findViewById(0) + val xCast = findViewById(0) as X + + val yExplicit: Y = findViewById(0) + val yCast = findViewById(0) as Y +} + +fun test(t: Test) { + val xExplicit: X = t.findViewById(0) + val xCast = t.findViewById(0) as X + + val yExplicit: Y = t.findViewById(0) + val yCast = t.findViewById(0) as Y +} + +fun test2(t: Test?) { + val xSafeCallSafeCast = t?.findViewById(0) as? X + val xSafeCallSafeCastExplicitType = t?.findViewById(0) as? X + + val xSafeCallCast = t?.findViewById(0) as X + val xSafeCallCastExplicitType = t?.findViewById(0) as X +} diff --git a/compiler/testData/diagnostics/tests/inference/findViewById.kt b/compiler/testData/diagnostics/tests/inference/findViewById.kt index c23deda5324..3ab2d546c51 100644 --- a/compiler/testData/diagnostics/tests/inference/findViewById.kt +++ b/compiler/testData/diagnostics/tests/inference/findViewById.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // !LANGUAGE: +ExpectedTypeFromCast // !DIAGNOSTICS: -UNUSED_VARIABLE -DEBUG_INFO_LEAKING_THIS @@ -25,35 +24,35 @@ class X : View() class Y : View() val xExplicit: X = Test().findViewById(0) -val xCast = Test().findViewById(0) as X +val xCast = Test().findViewById(0) as X val xCastExplicitType = Test().findViewById(0) as X val xSafeCastExplicitType = Test().findViewById(0) as? X val yExplicit: Y = Test().findViewById(0) -val yCast = Test().findViewById(0) as Y +val yCast = Test().findViewById(0) as Y class TestChild : Test() { val xExplicit: X = findViewById(0) - val xCast = findViewById(0) as X + val xCast = findViewById(0) as X val yExplicit: Y = findViewById(0) - val yCast = findViewById(0) as Y + val yCast = findViewById(0) as Y } fun test(t: Test) { val xExplicit: X = t.findViewById(0) - val xCast = t.findViewById(0) as X + val xCast = t.findViewById(0) as X val yExplicit: Y = t.findViewById(0) - val yCast = t.findViewById(0) as Y + val yCast = t.findViewById(0) as Y } fun test2(t: Test?) { - val xSafeCallSafeCast = t?.findViewById(0) as? X + val xSafeCallSafeCast = t?.findViewById(0) as? X val xSafeCallSafeCastExplicitType = t?.findViewById(0) as? X - val xSafeCallCast = t?.findViewById(0) as X + val xSafeCallCast = t?.findViewById(0) as X val xSafeCallCastExplicitType = t?.findViewById(0) as X } diff --git a/compiler/testData/diagnostics/tests/inference/kt30405.kt b/compiler/testData/diagnostics/tests/inference/kt30405.kt index dc3ac4939f0..43ab6a5a570 100644 --- a/compiler/testData/diagnostics/tests/inference/kt30405.kt +++ b/compiler/testData/diagnostics/tests/inference/kt30405.kt @@ -8,9 +8,9 @@ inline fun foo(): T { } fun test() { - val fooCall = foo() as String // T in foo should be inferred to String + val fooCall = foo() as String // T in foo should be inferred to String fooCall checkType { _() } - val safeFooCall = foo() as? String + val safeFooCall = foo() as? String safeFooCall checkType { _() } } diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 01cc18b02a4..30992682298 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -12203,6 +12203,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/inference/expectedTypeWithGenerics.kt"); } + @Test + @TestMetadata("expectedTypeWithGenericsSafeCalls.kt") + public void testExpectedTypeWithGenericsSafeCalls() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/expectedTypeWithGenericsSafeCalls.kt"); + } + @Test @TestMetadata("extensionLambdasAndArrow.kt") public void testExtensionLambdasAndArrow() throws Exception { diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index 125b0622607..c85580bb64f 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -10626,6 +10626,11 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/inference/expectedTypeWithGenerics.kt"); } + @TestMetadata("expectedTypeWithGenericsSafeCalls.kt") + public void testExpectedTypeWithGenericsSafeCalls() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/expectedTypeWithGenericsSafeCalls.kt"); + } + @TestMetadata("extensionLambdasAndArrow.kt") public void testExtensionLambdasAndArrow() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/extensionLambdasAndArrow.kt");