From 0391c24ab21fdf2525b6a7a94e8482d659c7ea05 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Tue, 25 Jul 2017 17:58:40 +0300 Subject: [PATCH] Provide diagnostic for typealias using deprecated class or constructor #KT-19205 Fixed Target versions 1.1.5 --- .../jetbrains/kotlin/diagnostics/Errors.java | 2 + .../rendering/DefaultErrorMessages.java | 9 ++- .../kotlin/resolve/deprecationUtil.kt | 57 ++++++++++++++----- .../tests/deprecated/typealiasConstructor.kt | 6 +- .../deprecated/typealiasForDeprecatedClass.kt | 19 +++++-- .../typealiasForDeprecatedClass.txt | 13 +++++ 6 files changed, 84 insertions(+), 22 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 7a6d03697aa..f213a6271fc 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -99,6 +99,8 @@ public interface Errors { DiagnosticFactory2 DEPRECATION = DiagnosticFactory2.create(WARNING); DiagnosticFactory2 DEPRECATION_ERROR = DiagnosticFactory2.create(ERROR); + DiagnosticFactory3 TYPEALIAS_EXPANSION_DEPRECATION = DiagnosticFactory3.create(WARNING); + DiagnosticFactory3 TYPEALIAS_EXPANSION_DEPRECATION_ERROR = DiagnosticFactory3.create(ERROR); DiagnosticFactory3> SINCE_KOTLIN_INFO_DEPRECATION = DiagnosticFactory3.create(WARNING); DiagnosticFactory3> SINCE_KOTLIN_INFO_DEPRECATION_ERROR = DiagnosticFactory3.create(ERROR); 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 25770fd05da..236feac8b7f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -322,14 +322,19 @@ public class DefaultErrorMessages { MAP.put(DEPRECATION, "''{0}'' is deprecated. {1}", DEPRECATION_RENDERER, STRING); MAP.put(DEPRECATION_ERROR, "Using ''{0}'' is an error. {1}", DEPRECATION_RENDERER, STRING); + MAP.put(TYPEALIAS_EXPANSION_DEPRECATION, "''{0}'' uses ''{1}'', which is deprecated. {2}", DEPRECATION_RENDERER, DEPRECATION_RENDERER, STRING); + MAP.put(TYPEALIAS_EXPANSION_DEPRECATION_ERROR, "''{0}'' uses ''{1}'', which is an error. {2}", DEPRECATION_RENDERER, DEPRECATION_RENDERER, STRING); + DiagnosticParameterRenderer> sinceKotlinInfoMessage = (pair, renderingContext) -> { String message = pair.getSecond(); return pair.getFirst().getVersionString() + (message != null ? ". " + message : ""); }; MAP.put(SINCE_KOTLIN_INFO_DEPRECATION, "''{0}''{1} should not be used in Kotlin {2}", DEPRECATION_RENDERER, - (obj, renderingContext) -> obj.equals(SinceKotlinInfo.Version.INFINITY) ? "" : " is only supported since Kotlin " + obj.asString() + " and", sinceKotlinInfoMessage); + (obj, renderingContext) -> obj.equals(SinceKotlinInfo.Version.INFINITY) ? "" : " is only supported since Kotlin " + obj.asString() + " and", + sinceKotlinInfoMessage); MAP.put(SINCE_KOTLIN_INFO_DEPRECATION_ERROR, "''{0}''{1} cannot be used in Kotlin {2}", DEPRECATION_RENDERER, - (obj, renderingContext) -> obj.equals(SinceKotlinInfo.Version.INFINITY) ? "" : " is only available since Kotlin " + obj.asString() + " and", sinceKotlinInfoMessage); + (obj, renderingContext) -> obj.equals(SinceKotlinInfo.Version.INFINITY) ? "" : " is only available since Kotlin " + obj.asString() + " and", + sinceKotlinInfoMessage); MAP.put(API_NOT_AVAILABLE, "This declaration is only available since Kotlin {0} and cannot be used with the specified API version {1}", STRING, STRING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt index 6e2c9d21b36..a5936c30985 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt @@ -131,6 +131,20 @@ private data class DeprecatedBySinceKotlinInfo( get() = sinceKotlinInfo.version } +private data class DeprecatedTypealiasByAnnotation( + val typeAliasTarget: TypeAliasDescriptor, + val nested: DeprecatedByAnnotation +) : Deprecation { + override val target get() = typeAliasTarget + override val deprecationLevel get() = nested.deprecationLevel + override val message get() = nested.message +} + +private fun Deprecation.wrapInTypeAliasExpansion(typeAliasDescriptor: TypeAliasDescriptor) = when { + this is DeprecatedByAnnotation -> DeprecatedTypealiasByAnnotation(typeAliasDescriptor, this) + else -> this +} + fun DeclarationDescriptor.getDeprecations(languageVersionSettings: LanguageVersionSettings): List { val deprecations = this.getOwnDeprecations(languageVersionSettings) if (deprecations.isNotEmpty()) { @@ -201,7 +215,12 @@ private fun DeclarationDescriptor.getOwnDeprecations(languageVersionSettings: La val annotation = target.annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.deprecated) ?: target.annotations.findAnnotation(JAVA_DEPRECATED) if (annotation != null) { - result.add(DeprecatedByAnnotation(annotation, target)) + val deprecatedByAnnotation = DeprecatedByAnnotation(annotation, target) + val deprecation = when (target) { + is TypeAliasConstructorDescriptor -> DeprecatedTypealiasByAnnotation(target.typeAliasDescriptor, deprecatedByAnnotation) + else -> deprecatedByAnnotation + } + result.add(deprecation) } val sinceKotlinInfo = @@ -232,7 +251,7 @@ private fun DeclarationDescriptor.getOwnDeprecations(languageVersionSettings: La when (this) { is TypeAliasDescriptor -> { - result.addAll(expandedType.deprecationsByConstituentTypes(languageVersionSettings)) + expandedType.deprecationsByConstituentTypes(languageVersionSettings).mapTo(result) { it.wrapInTypeAliasExpansion(this) } } is TypeAliasConstructorDescriptor -> { result.addAll(typeAliasDescriptor.getOwnDeprecations(languageVersionSettings)) @@ -257,20 +276,32 @@ internal fun createDeprecationDiagnostic( element: PsiElement, deprecation: Deprecation, languageVersionSettings: LanguageVersionSettings ): Diagnostic { val targetOriginal = deprecation.target.original - if (deprecation is DeprecatedBySinceKotlinInfo) { - val factory = when (deprecation.deprecationLevel) { - WARNING -> Errors.SINCE_KOTLIN_INFO_DEPRECATION - ERROR, HIDDEN -> Errors.SINCE_KOTLIN_INFO_DEPRECATION_ERROR + return when (deprecation) { + is DeprecatedBySinceKotlinInfo -> { + val factory = when (deprecation.deprecationLevel) { + WARNING -> Errors.SINCE_KOTLIN_INFO_DEPRECATION + ERROR, HIDDEN -> Errors.SINCE_KOTLIN_INFO_DEPRECATION_ERROR + } + factory.on(element, targetOriginal, deprecation.sinceKotlinVersion, + languageVersionSettings.languageVersion to deprecation.message) } - return factory.on(element, targetOriginal, deprecation.sinceKotlinVersion, - languageVersionSettings.languageVersion to deprecation.message) - } - val factory = when (deprecation.deprecationLevel) { - WARNING -> Errors.DEPRECATION - ERROR, HIDDEN -> Errors.DEPRECATION_ERROR + is DeprecatedTypealiasByAnnotation -> { + val factory = when (deprecation.deprecationLevel) { + WARNING -> Errors.TYPEALIAS_EXPANSION_DEPRECATION + ERROR, HIDDEN -> Errors.TYPEALIAS_EXPANSION_DEPRECATION_ERROR + } + factory.on(element, deprecation.typeAliasTarget.original, deprecation.nested.target.original, deprecation.nested.message ?: "") + } + + else -> { + val factory = when (deprecation.deprecationLevel) { + WARNING -> Errors.DEPRECATION + ERROR, HIDDEN -> Errors.DEPRECATION_ERROR + } + factory.on(element, targetOriginal, deprecation.message ?: "") + } } - return factory.on(element, targetOriginal, deprecation.message ?: "") } // values from kotlin.DeprecationLevel diff --git a/compiler/testData/diagnostics/tests/deprecated/typealiasConstructor.kt b/compiler/testData/diagnostics/tests/deprecated/typealiasConstructor.kt index b47cd33d008..7c1d0708538 100644 --- a/compiler/testData/diagnostics/tests/deprecated/typealiasConstructor.kt +++ b/compiler/testData/diagnostics/tests/deprecated/typealiasConstructor.kt @@ -10,8 +10,8 @@ typealias DeprecatedClassAlias = DeprecatedClass typealias WithDeprecatedCtorAlias = WithDeprecatedCtor typealias ArrayListOfDeprecatedClass = ArrayList<DeprecatedClass> -class Test1 : DeprecatedClassAlias() +class Test1 : DeprecatedClassAlias() -class Test2 : WithDeprecatedCtorAlias() +class Test2 : WithDeprecatedCtorAlias() -val test3 = ArrayListOfDeprecatedClass() \ No newline at end of file +val test3 = ArrayListOfDeprecatedClass() \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/deprecated/typealiasForDeprecatedClass.kt b/compiler/testData/diagnostics/tests/deprecated/typealiasForDeprecatedClass.kt index 419fb163c61..45454cd459c 100644 --- a/compiler/testData/diagnostics/tests/deprecated/typealiasForDeprecatedClass.kt +++ b/compiler/testData/diagnostics/tests/deprecated/typealiasForDeprecatedClass.kt @@ -1,10 +1,21 @@ @Deprecated("") class Foo +@Deprecated("", level = DeprecationLevel.ERROR) +class Err + typealias Test1 = Foo typealias Test2 = List<Foo> -typealias Test3 = List<Test2> +typealias Test3 = List<Test2> -fun use1(b: Test1) = b -fun use2(b: Test2) = b -fun use3(b: Test3) = b \ No newline at end of file +typealias TestErr1 = Err +typealias TestErr2 = List<Err> +typealias TestErr3 = List<TestErr2> + +fun use1(b: Test1) = b +fun use2(b: Test2) = b +fun use3(b: Test3) = b + +fun useErr1(b: TestErr1) = b +fun useErr2(b: TestErr2) = b +fun useErr3(b: TestErr3) = b \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/deprecated/typealiasForDeprecatedClass.txt b/compiler/testData/diagnostics/tests/deprecated/typealiasForDeprecatedClass.txt index 18b49254397..67bc04fa044 100644 --- a/compiler/testData/diagnostics/tests/deprecated/typealiasForDeprecatedClass.txt +++ b/compiler/testData/diagnostics/tests/deprecated/typealiasForDeprecatedClass.txt @@ -3,6 +3,16 @@ package public fun use1(/*0*/ b: Test1 /* = Foo */): Test1 /* = Foo */ public fun use2(/*0*/ b: Test2 /* = kotlin.collections.List */): Test2 /* = kotlin.collections.List */ public fun use3(/*0*/ b: Test3 /* = kotlin.collections.List */> */): Test3 /* = kotlin.collections.List */> */ +public fun useErr1(/*0*/ b: TestErr1 /* = Err */): TestErr1 /* = Err */ +public fun useErr2(/*0*/ b: TestErr2 /* = kotlin.collections.List */): TestErr2 /* = kotlin.collections.List */ +public fun useErr3(/*0*/ b: TestErr3 /* = kotlin.collections.List */> */): TestErr3 /* = kotlin.collections.List */> */ + +@kotlin.Deprecated(level = DeprecationLevel.ERROR, message = "") public final class Err { + public constructor Err() + 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 +} @kotlin.Deprecated(message = "") public final class Foo { public constructor Foo() @@ -13,3 +23,6 @@ public fun use3(/*0*/ b: Test3 /* = kotlin.collections.List public typealias Test3 = kotlin.collections.List +public typealias TestErr1 = Err +public typealias TestErr2 = kotlin.collections.List +public typealias TestErr3 = kotlin.collections.List