From fb9b1ad0dc78de72421ebacbe17600d5fb0cf108 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Wed, 14 Sep 2022 16:59:37 +0200 Subject: [PATCH] K1: implement warning for upper bound violated in type alias constructors Partially implements KT-47473 --- ...CompilerTestFE10TestdataTestGenerated.java | 6 +++ ...irOldFrontendDiagnosticsTestGenerated.java | 6 +++ ...DiagnosticsWithLightTreeTestGenerated.java | 6 +++ ...ndViolatedInTypealiasConstructorChecker.kt | 53 +++++++++++++++++++ .../jvm/platform/JvmPlatformConfigurator.kt | 1 + .../rendering/DefaultErrorMessages.java | 2 +- .../boundsViolationInTypeAliasExpansion.kt | 2 +- ...dUpperBoundsWithEnabledImprovements.fir.kt | 2 +- ...ancedUpperBoundsWithEnabledImprovements.kt | 4 +- .../typealias/upperBoundViolated.fir.kt | 4 -- .../typealias/upperBoundViolated.kt | 8 +-- .../typealias/upperBoundViolated.txt | 10 +--- .../typealias/upperBoundViolated2.diag.txt | 3 ++ .../typealias/upperBoundViolated2.fir.kt | 4 ++ .../typealias/upperBoundViolated2.kt | 4 ++ .../typealias/upperBoundViolated2.txt | 11 ++++ .../test/runners/DiagnosticTestGenerated.java | 6 +++ 17 files changed, 108 insertions(+), 24 deletions(-) create mode 100644 compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/UpperBoundViolatedInTypealiasConstructorChecker.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated2.diag.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated2.fir.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated2.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated2.txt diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index da3ee7dfa3e..c2926a11019 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -39187,6 +39187,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag public void testUpperBoundViolated() throws Exception { runTest("compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated.kt"); } + + @Test + @TestMetadata("upperBoundViolated2.kt") + public void testUpperBoundViolated2() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated2.kt"); + } } @Nested 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 5ddba88c5ab..62651deb7a3 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 @@ -39251,6 +39251,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti public void testUpperBoundViolated() throws Exception { runTest("compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated.kt"); } + + @Test + @TestMetadata("upperBoundViolated2.kt") + public void testUpperBoundViolated2() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated2.kt"); + } } @Nested 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 5e5904e3b4f..9a2ad184799 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 @@ -39251,6 +39251,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac public void testUpperBoundViolated() throws Exception { runTest("compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated.kt"); } + + @Test + @TestMetadata("upperBoundViolated2.kt") + public void testUpperBoundViolated2() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated2.kt"); + } } @Nested diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/UpperBoundViolatedInTypealiasConstructorChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/UpperBoundViolatedInTypealiasConstructorChecker.kt new file mode 100644 index 00000000000..00a6051025f --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/UpperBoundViolatedInTypealiasConstructorChecker.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.resolve.jvm.checkers + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.psi.KtCallExpression +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker +import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext +import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutorByConstructorMap +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.types.AbstractTypeChecker +import org.jetbrains.kotlin.types.checker.ClassicTypeCheckerState +import org.jetbrains.kotlin.types.checker.ClassicTypeCheckerStateInternals + +object UpperBoundViolatedInTypealiasConstructorChecker : CallChecker { + @OptIn(ClassicTypeCheckerStateInternals::class) + override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { + val resultingDescriptor = resolvedCall.resultingDescriptor as? TypeAliasConstructorDescriptor ?: return + val callExpression = reportOn.getStrictParentOfType() ?: return + val underlyingConstructedType = resultingDescriptor.underlyingConstructorDescriptor.returnType + val underlyingTypeArguments = underlyingConstructedType.arguments + val underlyingTypeParameters = resultingDescriptor.underlyingConstructorDescriptor.returnType.constructor.parameters + val state = ClassicTypeCheckerState(isErrorTypeEqualsToAnything = false) + val substitutor = NewTypeSubstitutorByConstructorMap( + underlyingTypeParameters.withIndex().associate { + Pair(it.value.typeConstructor, underlyingTypeArguments[it.index].type.unwrap()) + } + ) + // Note: necessary only for diagnostic duplication check + val aliasTypeParameters = resolvedCall.candidateDescriptor.typeParameters + val originalTypes = resultingDescriptor.typeAliasDescriptor.underlyingType.arguments.map { it.type } + for ((index, argumentAndParameter) in underlyingTypeArguments.zip(underlyingTypeParameters).withIndex()) { + val (argument, parameter) = argumentAndParameter + // To remove duplication of UPPER_BOUND_VIOLATED + // See createToFreshVariableSubstitutorAndAddInitialConstraints in ResolutionParts.kt, citing: + // ... if (kotlinType == typeParameter.defaultType) i else null ... + if (aliasTypeParameters.getOrNull(index)?.defaultType == originalTypes.getOrNull(index)) continue + + for (upperBound in parameter.upperBounds) { + if (!AbstractTypeChecker.isSubtypeOf(state, argument.type, substitutor.safeSubstitute(upperBound.unwrap()))) { + val typeReference = callExpression.typeArguments.getOrNull(index)?.typeReference ?: continue + context.trace.report(Errors.UPPER_BOUND_VIOLATED_WARNING.on(typeReference, upperBound, argument.type)) + } + } + } + } +} \ No newline at end of file diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt index daf505f1a82..da5adefd6ea 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt @@ -61,6 +61,7 @@ object JvmPlatformConfigurator : PlatformConfiguratorBase( PolymorphicSignatureCallChecker, SamInterfaceConstructorReferenceCallChecker, EnumDeclaringClassDeprecationChecker, + UpperBoundViolatedInTypealiasConstructorChecker, ), additionalTypeCheckers = listOf( 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 ae27297710f..8bd84cce0a7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -594,7 +594,7 @@ public class DefaultErrorMessages { }); MAP.put(UPPER_BOUND_VIOLATED, "Type argument is not within its bounds: should be subtype of ''{0}''", RENDER_TYPE, RENDER_TYPE); - MAP.put(UPPER_BOUND_VIOLATED_WARNING, "Type argument is not within its bounds: should be subtype of ''{0}''", RENDER_TYPE, RENDER_TYPE); + MAP.put(UPPER_BOUND_VIOLATED_WARNING, "Type argument is not within its bounds: ''{1}'' should be subtype of ''{0}''. This warning will become an error in K2", RENDER_TYPE, RENDER_TYPE); MAP.put(FINAL_UPPER_BOUND, "''{0}'' is a final type, and thus a value of the type parameter is predetermined", RENDER_TYPE); MAP.put(UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE, "Extension function type can not be used as an upper bound"); MAP.put(ONLY_ONE_CLASS_BOUND_ALLOWED, "Only one of the upper bounds can be a class"); diff --git a/compiler/testData/diagnostics/tests/typealias/boundsViolationInTypeAliasExpansion.kt b/compiler/testData/diagnostics/tests/typealias/boundsViolationInTypeAliasExpansion.kt index 40739a7922b..7df1b663dea 100644 --- a/compiler/testData/diagnostics/tests/typealias/boundsViolationInTypeAliasExpansion.kt +++ b/compiler/testData/diagnostics/tests/typealias/boundsViolationInTypeAliasExpansion.kt @@ -18,7 +18,7 @@ val test5 = NA() val test6 = NA<Any>() val test7 = NL() val test8 = MMMM<Int>() -val test9dwd = NL() +val test9dwd = NL<Any>() fun test9(x: TC>) {} fun test10(x: TC>) {} diff --git a/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBoundsWithEnabledImprovements.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBoundsWithEnabledImprovements.fir.kt index 4fa544d2149..b5f17242f8f 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBoundsWithEnabledImprovements.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBoundsWithEnabledImprovements.fir.kt @@ -31,7 +31,7 @@ fun main1(x: A) {} fun main2(x: A2) {} fun main3(x: A3) {} fun main3() { - val x = A3() // TODO: support reporting errors on typealias constructor calls + val x = A3() val x2 = A() val y: A3 = A3() } diff --git a/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBoundsWithEnabledImprovements.kt b/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBoundsWithEnabledImprovements.kt index a6f2b4b73cf..46d6f2fb24e 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBoundsWithEnabledImprovements.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBoundsWithEnabledImprovements.kt @@ -31,7 +31,7 @@ fun main1(x: A<Int?>) {} fun main2(x: A2<Int?>) {} fun main3(x: A3) {} fun main3() { - val x = A3() // TODO: support reporting errors on typealias constructor calls + val x = A3<Int?>() val x2 = A<Int?>() - val y: A3 = A3() + val y: A3 = A3<Int?>() } diff --git a/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated.fir.kt index 1bb653be4b2..f134407bf7f 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated.fir.kt @@ -4,7 +4,3 @@ typealias AliasOfNumList = NumList val falseUpperBoundViolation = AliasOfNumList() // Shouldn't be error val missedUpperBoundViolation = NumList() // Should be error - -class Base> -typealias Alias = Base> -val a = Alias() // Also should be error diff --git a/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated.kt b/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated.kt index 7ba80419c8b..1f2928b363e 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated.kt @@ -2,9 +2,5 @@ class NumColl> typealias NumList = NumColl> typealias AliasOfNumList = NumList -val falseUpperBoundViolation = AliasOfNumList<Int>() // Shouldn't be error -val missedUpperBoundViolation = NumList() // Should be error - -class Base> -typealias Alias = Base> -val a = Alias() // Also should be error \ No newline at end of file +val falseUpperBoundViolation = AliasOfNumList<; Int")!>Int>() // Shouldn't be error +val missedUpperBoundViolation = NumList<; List")!>Any>() // Should be error diff --git a/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated.txt b/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated.txt index 2752d490a61..e454ada55bb 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated.txt +++ b/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated.txt @@ -1,22 +1,14 @@ package -public val a: Alias /* = Base> */ public val falseUpperBoundViolation: AliasOfNumList /* = NumColl> */ public val missedUpperBoundViolation: NumList /* = NumColl> */ -public final class Base> { - public constructor Base>() - 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 -} - public final class NumColl> { public constructor NumColl>() 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 } -public typealias Alias = Base> public typealias AliasOfNumList = NumList public typealias NumList = NumColl> + diff --git a/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated2.diag.txt b/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated2.diag.txt new file mode 100644 index 00000000000..15002ab1036 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated2.diag.txt @@ -0,0 +1,3 @@ +/upperBoundViolated2.kt:4:15: warning: type argument is not within its bounds: 'List' should be subtype of 'List'. This warning will become an error in K2 +val a = Alias() // Also should be error + ^ diff --git a/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated2.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated2.fir.kt new file mode 100644 index 00000000000..63575e0972c --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated2.fir.kt @@ -0,0 +1,4 @@ +// !RENDER_DIAGNOSTICS_FULL_TEXT +class Base> +typealias Alias = Base> +val a = Alias() // Also should be error diff --git a/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated2.kt b/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated2.kt new file mode 100644 index 00000000000..945e9b22c92 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated2.kt @@ -0,0 +1,4 @@ +// !RENDER_DIAGNOSTICS_FULL_TEXT +class Base> +typealias Alias = Base> +val a = Alias<; List")!>Any>() // Also should be error diff --git a/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated2.txt b/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated2.txt new file mode 100644 index 00000000000..71e721d5b2d --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated2.txt @@ -0,0 +1,11 @@ +package + +public val a: Alias /* = Base> */ + +public final class Base> { + public constructor Base>() + 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 +} +public typealias Alias = Base> 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 625ff5964cd..55b521683bd 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 @@ -39341,6 +39341,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { public void testUpperBoundViolated() throws Exception { runTest("compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated.kt"); } + + @Test + @TestMetadata("upperBoundViolated2.kt") + public void testUpperBoundViolated2() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/typealias/upperBoundViolated2.kt"); + } } @Nested