From db07d783a28343af28f1c3a924d62d2cff79245a Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Tue, 12 Jan 2016 19:28:22 +0300 Subject: [PATCH] Member is considered deprecated if it overrides only deprecated members Property accessor is considered deprecated when the property is Provide IDE inspection to strikeout members overriding deprecated members (like in java) --- .../kotlin/resolve/deprecationUtil.kt | 132 ++++++++++++--- .../validation/DeprecatedSymbolValidator.kt | 37 ++--- .../tests/deprecated/deprecatedInheritance.kt | 140 ++++++++++++++++ .../deprecated/deprecatedInheritance.txt | 156 ++++++++++++++++++ .../deprecatedPropertyInheritance.kt | 135 +++++++++++++++ .../deprecatedPropertyInheritance.txt | 115 +++++++++++++ .../checkers/DiagnosticsTestGenerated.java | 12 ++ .../OverridingDeprecatedMember.html | 5 + idea/src/META-INF/plugin.xml | 8 + .../OverridingDeprecatedMemberInspection.kt | 58 +++++++ .../inspectionData/expected.xml | 62 +++++++ .../inspectionData/inspections.test | 1 + .../overridingDeprecatedMember/test.kt | 47 ++++++ .../codeInsight/InspectionTestGenerated.java | 6 + 14 files changed, 871 insertions(+), 43 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/deprecated/deprecatedInheritance.kt create mode 100644 compiler/testData/diagnostics/tests/deprecated/deprecatedInheritance.txt create mode 100644 compiler/testData/diagnostics/tests/deprecated/deprecatedPropertyInheritance.kt create mode 100644 compiler/testData/diagnostics/tests/deprecated/deprecatedPropertyInheritance.txt create mode 100644 idea/resources/inspectionDescriptions/OverridingDeprecatedMember.html create mode 100644 idea/src/org/jetbrains/kotlin/idea/inspections/OverridingDeprecatedMemberInspection.kt create mode 100644 idea/testData/inspections/overridingDeprecatedMember/inspectionData/expected.xml create mode 100644 idea/testData/inspections/overridingDeprecatedMember/inspectionData/inspections.test create mode 100644 idea/testData/inspections/overridingDeprecatedMember/test.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt index 18916f492d4..3eb9956bb14 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt @@ -23,34 +23,133 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.annotations.argumentValue +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import java.util.* private val JAVA_DEPRECATED = FqName("java.lang.Deprecated") -fun DeclarationDescriptor.getDeprecatedAnnotation(): Pair? { +interface Deprecation { + val deprecationLevel: DeprecationLevelValue + val message: String + val target: DeclarationDescriptor +} + +fun Deprecation.deprecatedByOverriddenMessage(): String? = (this as? DeprecatedByOverridden)?.additionalMessage() + +private data class DeprecatedByAnnotation(private val annotation: AnnotationDescriptor, override val target: DeclarationDescriptor) : Deprecation { + override val deprecationLevel: DeprecationLevelValue + get() { + val level = annotation.argumentValue("level") as? ClassDescriptor + + return when (level?.name?.asString()) { + "WARNING" -> DeprecationLevelValue.WARNING + "ERROR" -> DeprecationLevelValue.ERROR + "HIDDEN" -> DeprecationLevelValue.HIDDEN + else -> DeprecationLevelValue.WARNING + } + } + + override val message: String + get() = annotation.argumentValue("message") as? String ?: "" +} + +private data class DeprecatedByOverridden(private val deprecations: Collection) : Deprecation { + init { + assert(deprecations.isNotEmpty()) + assert(deprecations.none { + it is DeprecatedByOverridden + }) + } + + override val deprecationLevel: DeprecationLevelValue = deprecations.map(Deprecation::deprecationLevel).min()!! + + override val target: DeclarationDescriptor + get() = deprecations.first().target + + override val message: String + get() { + val message = deprecations.filter { it.deprecationLevel == this.deprecationLevel }.map { it.message }.toSet().joinToString(". ") + return "${additionalMessage()}. $message" + } + + internal fun additionalMessage() = "Overrides deprecated member in '${DescriptorUtils.getContainingClass(target)!!.fqNameSafe.asString()}'" +} + +fun DeclarationDescriptor.getDeprecation(): Deprecation? { + val deprecation = this.getDeprecationByAnnotation() + if (deprecation != null) { + return deprecation + } + + if (this is CallableMemberDescriptor) { + return deprecationByOverridden(this) + } + + return null +} + +private fun deprecationByOverridden(root: CallableMemberDescriptor): Deprecation? { + val visited = HashSet() + val deprecations = LinkedHashSet() + var hasUndeprecatedOverridden = false + + fun traverse(node: CallableMemberDescriptor) { + if (node in visited) return + + visited.add(node) + + val deprecatedAnnotation = node.getDeprecationByAnnotation() + val overriddenDescriptors = node.overriddenDescriptors + when { + deprecatedAnnotation != null -> { + deprecations.add(deprecatedAnnotation) + } + overriddenDescriptors.isEmpty() -> { + hasUndeprecatedOverridden = true + return + } + else -> { + overriddenDescriptors.forEach { traverse(it) } + } + } + } + + traverse(root) + + if (hasUndeprecatedOverridden || deprecations.isEmpty()) return null + + return DeprecatedByOverridden(deprecations) +} + +private fun DeclarationDescriptor.getDeprecationByAnnotation(): DeprecatedByAnnotation? { val ownAnnotation = getDeclaredDeprecatedAnnotation(AnnotationUseSiteTarget.getAssociatedUseSiteTarget(this)) if (ownAnnotation != null) - return ownAnnotation to this + return DeprecatedByAnnotation(ownAnnotation, this) when (this) { is ConstructorDescriptor -> { val classDescriptor = containingDeclaration val classAnnotation = classDescriptor.getDeclaredDeprecatedAnnotation() if (classAnnotation != null) - return classAnnotation to classDescriptor + return DeprecatedByAnnotation(classAnnotation, classDescriptor) } is PropertyAccessorDescriptor -> { val propertyDescriptor = correspondingProperty val target = if (this is PropertyGetterDescriptor) AnnotationUseSiteTarget.PROPERTY_GETTER else AnnotationUseSiteTarget.PROPERTY_SETTER - val accessorAnnotation = propertyDescriptor.getDeclaredDeprecatedAnnotation(target, false) - if (accessorAnnotation != null) - return accessorAnnotation to this + val useSiteAnnotationOnProperty = propertyDescriptor.getDeclaredDeprecatedAnnotation(target, false) + if (useSiteAnnotationOnProperty != null) + return DeprecatedByAnnotation(useSiteAnnotationOnProperty, this) + + val propertyAnnotation = propertyDescriptor.getDeclaredDeprecatedAnnotation() + if (propertyAnnotation != null) + return DeprecatedByAnnotation(propertyAnnotation, propertyDescriptor) val classDescriptor = containingDeclaration as? ClassDescriptor if (classDescriptor != null && classDescriptor.isCompanionObject) { val classAnnotation = classDescriptor.getDeclaredDeprecatedAnnotation() if (classAnnotation != null) - return classAnnotation to classDescriptor + return DeprecatedByAnnotation(classAnnotation, classDescriptor) } } } @@ -74,31 +173,16 @@ private fun DeclarationDescriptor.getDeclaredDeprecatedAnnotation( return null } -// Reflects values from kotlin.DeprecationLevel +// values from kotlin.DeprecationLevel enum class DeprecationLevelValue { WARNING, ERROR, HIDDEN } -fun AnnotationDescriptor.getDeprecatedAnnotationLevel(): DeprecationLevelValue? { - val level = this.argumentValue("level") as? ClassDescriptor - - return when(level?.name?.asString()) { - "WARNING" -> DeprecationLevelValue.WARNING - "ERROR" -> DeprecationLevelValue.ERROR - "HIDDEN" -> DeprecationLevelValue.HIDDEN - else -> null - } -} - -fun DeclarationDescriptor.getDeprecatedAnnotationLevel(): DeprecationLevelValue? { - return getDeprecatedAnnotation()?.first?.getDeprecatedAnnotationLevel() -} - @Deprecated("Should be removed together with kotlin.HiddenDeclaration") private val HIDDEN_ANNOTATION_FQ_NAME = FqName("kotlin.HiddenDeclaration") fun DeclarationDescriptor.isHiddenInResolution(): Boolean { if (this is FunctionDescriptor && this.isHiddenToOvercomeSignatureClash) return true return annotations.findAnnotation(HIDDEN_ANNOTATION_FQ_NAME) != null - || getDeprecatedAnnotationLevel() == DeprecationLevelValue.HIDDEN + || getDeprecation()?.deprecationLevel == DeprecationLevelValue.HIDDEN } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/validation/DeprecatedSymbolValidator.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/validation/DeprecatedSymbolValidator.kt index 1b1b1dee4bf..0901a2a6e0a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/validation/DeprecatedSymbolValidator.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/validation/DeprecatedSymbolValidator.kt @@ -21,27 +21,28 @@ import com.intellij.psi.tree.TokenSet import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.ClassifierDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.descriptors.PropertySetterDescriptor import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.Deprecation import org.jetbrains.kotlin.resolve.DeprecationLevelValue -import org.jetbrains.kotlin.resolve.annotations.argumentValue import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall -import org.jetbrains.kotlin.resolve.getDeprecatedAnnotation -import org.jetbrains.kotlin.resolve.getDeprecatedAnnotationLevel +import org.jetbrains.kotlin.resolve.getDeprecation class DeprecatedSymbolValidator : SymbolUsageValidator { override fun validateCall(resolvedCall: ResolvedCall<*>?, targetDescriptor: CallableDescriptor, trace: BindingTrace, element: PsiElement) { - val deprecated = targetDescriptor.getDeprecatedAnnotation() - if (deprecated != null) { - val (annotation, target) = deprecated - trace.report(createDeprecationDiagnostic(element, target, annotation)) + val deprecation = targetDescriptor.getDeprecation() + + // avoid duplicating diagnostic when deprecation for property effectively deprecates setter + if (targetDescriptor is PropertySetterDescriptor && targetDescriptor.correspondingProperty.getDeprecation() == deprecation) return + + if (deprecation != null) { + trace.report(createDeprecationDiagnostic(element, deprecation)) } else if (targetDescriptor is PropertyDescriptor) { propertyGetterWorkaround(resolvedCall, targetDescriptor, trace, element) @@ -59,21 +60,19 @@ class DeprecatedSymbolValidator : SymbolUsageValidator { if (superExpression != null && superExpression.getCalleeExpression().getConstructorReferenceExpression() == element) return - val deprecated = targetDescriptor.getDeprecatedAnnotation() - if (deprecated != null) { - val (annotation, target) = deprecated - trace.report(createDeprecationDiagnostic(element, target, annotation)) + val deprecation = targetDescriptor.getDeprecation() + if (deprecation != null) { + trace.report(createDeprecationDiagnostic(element, deprecation)) } } - private fun createDeprecationDiagnostic(element: PsiElement, descriptor: DeclarationDescriptor, deprecated: AnnotationDescriptor): Diagnostic { - val message = deprecated.argumentValue("message") as? String ?: "" - - if (deprecated.getDeprecatedAnnotationLevel() == DeprecationLevelValue.ERROR) { - return Errors.DEPRECATION_ERROR.on(element, descriptor.original, message) + private fun createDeprecationDiagnostic(element: PsiElement, deprecation: Deprecation): Diagnostic { + val targetOriginal = deprecation.target.original + if (deprecation.deprecationLevel == DeprecationLevelValue.ERROR) { + return Errors.DEPRECATION_ERROR.on(element, targetOriginal, deprecation.message) } - return Errors.DEPRECATION.on(element, descriptor.original, message) + return Errors.DEPRECATION.on(element, targetOriginal, deprecation.message) } private val PROPERTY_SET_OPERATIONS = TokenSet.create(KtTokens.EQ, KtTokens.PLUSEQ, KtTokens.MINUSEQ, KtTokens.MULTEQ, diff --git a/compiler/testData/diagnostics/tests/deprecated/deprecatedInheritance.kt b/compiler/testData/diagnostics/tests/deprecated/deprecatedInheritance.kt new file mode 100644 index 00000000000..114ae51efe5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/deprecated/deprecatedInheritance.kt @@ -0,0 +1,140 @@ +package foo + +interface WarningDeprecated { + @Deprecated("", level = DeprecationLevel.WARNING) + fun f() { + + } +} + +interface ErrorDeprecated { + @Deprecated("", level = DeprecationLevel.ERROR) + fun f() { + + } +} + +interface HiddenDeprecated { + @Deprecated("", level = DeprecationLevel.HIDDEN) + fun f() { + + } +} + +interface NotDeprecated { + fun f() { + + } +} + +open class WE : WarningDeprecated, ErrorDeprecated { + override fun f() { + + } +} + +open class WH : WarningDeprecated, HiddenDeprecated { + override fun f() { + + } +} + +open class EH : ErrorDeprecated, HiddenDeprecated { + override fun f() { + + } +} + +open class NW : WarningDeprecated, NotDeprecated { + override fun f() { + + } +} + +open class NE : ErrorDeprecated, NotDeprecated { + override fun f() { + + } +} + +open class NH : HiddenDeprecated, NotDeprecated { + override fun f() { + + } +} + +open class WEH: WarningDeprecated, ErrorDeprecated, HiddenDeprecated { + override fun f() { + + } +} + +open class NWEH: NotDeprecated, WarningDeprecated, ErrorDeprecated, HiddenDeprecated { + override fun f() { + + } +} + +class WE2: WE() + +class NWE2: WE(), NotDeprecated + +class NWE3: WE(), NotDeprecated { + override fun f() { + } +} + +interface E2: ErrorDeprecated +interface W2: WarningDeprecated + +interface EW2: E2, W2 { + override fun f() { + } +} + +interface HEW2: EW2, HiddenDeprecated { + override fun f() { + } +} + +interface ExplicitError: HEW2 { + @Deprecated("", level = DeprecationLevel.ERROR) + override fun f() { + super.f() + } +} + +fun use( + wd: WarningDeprecated, ed: ErrorDeprecated, hd: HiddenDeprecated, + we: WE, wh: WH, eh: EH, nw: NW, ne: NE, nh: NH, + weh: WEH, nweh: NWEH, + we2: WE2, nwe2: NWE2, nwe3: NWE3, + e2: E2, w2: W2, ew2: EW2, hew2: HEW2, + explicitError: ExplicitError +) { + wd.f() + ed.f() + hd.f() + + we.f() + wh.f() + eh.f() + + nw.f() + ne.f() + nh.f() + + weh.f() + nweh.f() + + we2.f() + nwe2.f() + nwe3.f() + + e2.f() + w2.f() + ew2.f() + hew2.f() + + explicitError.f() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/deprecated/deprecatedInheritance.txt b/compiler/testData/diagnostics/tests/deprecated/deprecatedInheritance.txt new file mode 100644 index 00000000000..1663b51ed9e --- /dev/null +++ b/compiler/testData/diagnostics/tests/deprecated/deprecatedInheritance.txt @@ -0,0 +1,156 @@ +package + +package foo { + public fun use(/*0*/ wd: foo.WarningDeprecated, /*1*/ ed: foo.ErrorDeprecated, /*2*/ hd: foo.HiddenDeprecated, /*3*/ we: foo.WE, /*4*/ wh: foo.WH, /*5*/ eh: foo.EH, /*6*/ nw: foo.NW, /*7*/ ne: foo.NE, /*8*/ nh: foo.NH, /*9*/ weh: foo.WEH, /*10*/ nweh: foo.NWEH, /*11*/ we2: foo.WE2, /*12*/ nwe2: foo.NWE2, /*13*/ nwe3: foo.NWE3, /*14*/ e2: foo.E2, /*15*/ w2: foo.W2, /*16*/ ew2: foo.EW2, /*17*/ hew2: foo.HEW2, /*18*/ explicitError: foo.ExplicitError): kotlin.Unit + + public interface E2 : foo.ErrorDeprecated { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @kotlin.Deprecated(level = DeprecationLevel.ERROR, message = "") public open override /*1*/ /*fake_override*/ fun f(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public open class EH : foo.ErrorDeprecated, foo.HiddenDeprecated { + public constructor EH() + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ fun f(): kotlin.Unit + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String + } + + public interface EW2 : foo.E2, foo.W2 { + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ fun f(): kotlin.Unit + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String + } + + public interface ErrorDeprecated { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @kotlin.Deprecated(level = DeprecationLevel.ERROR, message = "") public open fun f(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public interface ExplicitError : foo.HEW2 { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @kotlin.Deprecated(level = DeprecationLevel.ERROR, message = "") public open override /*1*/ fun f(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public interface HEW2 : foo.EW2, foo.HiddenDeprecated { + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ fun f(): kotlin.Unit + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String + } + + public interface HiddenDeprecated { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @kotlin.Deprecated(level = DeprecationLevel.HIDDEN, message = "") public open fun f(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public open class NE : foo.ErrorDeprecated, foo.NotDeprecated { + public constructor NE() + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ fun f(): kotlin.Unit + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String + } + + public open class NH : foo.HiddenDeprecated, foo.NotDeprecated { + public constructor NH() + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ fun f(): kotlin.Unit + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String + } + + public open class NW : foo.WarningDeprecated, foo.NotDeprecated { + public constructor NW() + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ fun f(): kotlin.Unit + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String + } + + public final class NWE2 : foo.WE, foo.NotDeprecated { + public constructor NWE2() + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ /*fake_override*/ fun f(): kotlin.Unit + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String + } + + public final class NWE3 : foo.WE, foo.NotDeprecated { + public constructor NWE3() + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ fun f(): kotlin.Unit + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String + } + + public open class NWEH : foo.NotDeprecated, foo.WarningDeprecated, foo.ErrorDeprecated, foo.HiddenDeprecated { + public constructor NWEH() + public open override /*4*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*4*/ fun f(): kotlin.Unit + public open override /*4*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*4*/ /*fake_override*/ fun toString(): kotlin.String + } + + public interface NotDeprecated { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open fun f(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public interface W2 : foo.WarningDeprecated { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @kotlin.Deprecated(level = DeprecationLevel.WARNING, message = "") public open override /*1*/ /*fake_override*/ fun f(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public open class WE : foo.WarningDeprecated, foo.ErrorDeprecated { + public constructor WE() + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ fun f(): kotlin.Unit + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String + } + + public final class WE2 : foo.WE { + public constructor WE2() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun f(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public open class WEH : foo.WarningDeprecated, foo.ErrorDeprecated, foo.HiddenDeprecated { + public constructor WEH() + public open override /*3*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*3*/ fun f(): kotlin.Unit + public open override /*3*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*3*/ /*fake_override*/ fun toString(): kotlin.String + } + + public open class WH : foo.WarningDeprecated, foo.HiddenDeprecated { + public constructor WH() + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ fun f(): kotlin.Unit + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String + } + + public interface WarningDeprecated { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @kotlin.Deprecated(level = DeprecationLevel.WARNING, message = "") public open fun f(): kotlin.Unit + 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/tests/deprecated/deprecatedPropertyInheritance.kt b/compiler/testData/diagnostics/tests/deprecated/deprecatedPropertyInheritance.kt new file mode 100644 index 00000000000..5c208d6b88d --- /dev/null +++ b/compiler/testData/diagnostics/tests/deprecated/deprecatedPropertyInheritance.kt @@ -0,0 +1,135 @@ +package foo + +interface HiddenDeprecated { + @Deprecated("", level = DeprecationLevel.HIDDEN) + var p: Int +} + +interface NoDeprecation { + var p: Int +} + + +open class WarningDeprecated { + @Deprecated("", level = DeprecationLevel.WARNING) + open var p: Int = 3 +} + +open class ErrorDeprecated { + @Deprecated("", level = DeprecationLevel.ERROR) + open var p: Int = 3 +} + +open class GetterDeprecated { + open var p: Int = 3 + @Deprecated("") get +} + +open class SetterDeprecated { + open var p: Int = 3 + @Deprecated("") set +} + +class WD: WarningDeprecated() { + override var p: Int + get() = 3 + set(value) {} +} + +class ED: ErrorDeprecated() { + override var p: Int + get() = 3 + set(value) { + } +} + +class GD: GetterDeprecated() { + override var p: Int + get() = 3 + set(value) { + } +} + +class SD: SetterDeprecated() { + override var p: Int + get() = 3 + set(value) { + } +} + +class SDH: SetterDeprecated(), HiddenDeprecated { + override var p: Int + get() = 3 + set(value) { + } +} + +class EDH: ErrorDeprecated(), HiddenDeprecated { + override var p: Int + get() = 3 + set(value) { + } +} + +class NED: ErrorDeprecated(), NoDeprecation { + override var p: Int + get() = 3 + set(value) { + } +} + +class Diff { + @Deprecated("", level = DeprecationLevel.WARNING) + var p: Int + @Deprecated("", level = DeprecationLevel.ERROR) get() = 3 + @Deprecated("", level = DeprecationLevel.HIDDEN) set(value) { + + } +} + +fun use( + warningDeprecated: WarningDeprecated, errorDeprecated: ErrorDeprecated, setterDeprecated: SetterDeprecated, + getterDeprecated: GetterDeprecated, hiddenDeprecated: HiddenDeprecated, + wd: WD, ed: ED, gd: GD, sd: SD, + sdh: SDH, edh: EDH, ned: NED, + diff: Diff +) { + warningDeprecated.p + warningDeprecated.p = 1 + + errorDeprecated.p + errorDeprecated.p = 1 + + getterDeprecated.p + getterDeprecated.p = 1 + + setterDeprecated.p + setterDeprecated.p = 1 + + hiddenDeprecated.p + hiddenDeprecated.p = 1 + + wd.p + wd.p = 1 + + ed.p + ed.p = 1 + + gd.p + gd.p = 1 + + sd.p + sd.p = 1 + + sdh.p + sdh.p = 1 + + edh.p + edh.p = 1 + + ned.p + ned.p = 1 + + diff.p + diff.p = 1 +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/deprecated/deprecatedPropertyInheritance.txt b/compiler/testData/diagnostics/tests/deprecated/deprecatedPropertyInheritance.txt new file mode 100644 index 00000000000..52b85367b65 --- /dev/null +++ b/compiler/testData/diagnostics/tests/deprecated/deprecatedPropertyInheritance.txt @@ -0,0 +1,115 @@ +package + +package foo { + public fun use(/*0*/ warningDeprecated: foo.WarningDeprecated, /*1*/ errorDeprecated: foo.ErrorDeprecated, /*2*/ setterDeprecated: foo.SetterDeprecated, /*3*/ getterDeprecated: foo.GetterDeprecated, /*4*/ hiddenDeprecated: foo.HiddenDeprecated, /*5*/ wd: foo.WD, /*6*/ ed: foo.ED, /*7*/ gd: foo.GD, /*8*/ sd: foo.SD, /*9*/ sdh: foo.SDH, /*10*/ edh: foo.EDH, /*11*/ ned: foo.NED, /*12*/ diff: foo.Diff): kotlin.Unit + + public final class Diff { + public constructor Diff() + @kotlin.Deprecated(level = DeprecationLevel.WARNING, message = "") public final var p: kotlin.Int + 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 ED : foo.ErrorDeprecated { + public constructor ED() + public open override /*1*/ var p: kotlin.Int + 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 EDH : foo.ErrorDeprecated, foo.HiddenDeprecated { + public constructor EDH() + public open override /*2*/ var p: kotlin.Int + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String + } + + public open class ErrorDeprecated { + public constructor ErrorDeprecated() + @kotlin.Deprecated(level = DeprecationLevel.ERROR, message = "") public open var p: kotlin.Int + 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 GD : foo.GetterDeprecated { + public constructor GD() + public open override /*1*/ var p: kotlin.Int + 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 open class GetterDeprecated { + public constructor GetterDeprecated() + public open var p: kotlin.Int + 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 interface HiddenDeprecated { + @kotlin.Deprecated(level = DeprecationLevel.HIDDEN, message = "") public abstract var p: kotlin.Int + 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 NED : foo.ErrorDeprecated, foo.NoDeprecation { + public constructor NED() + public open override /*2*/ var p: kotlin.Int + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String + } + + public interface NoDeprecation { + public abstract var p: kotlin.Int + 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 SD : foo.SetterDeprecated { + public constructor SD() + public open override /*1*/ var p: kotlin.Int + 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 SDH : foo.SetterDeprecated, foo.HiddenDeprecated { + public constructor SDH() + public open override /*2*/ var p: kotlin.Int + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String + } + + public open class SetterDeprecated { + public constructor SetterDeprecated() + public open var p: kotlin.Int + 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 WD : foo.WarningDeprecated { + public constructor WD() + public open override /*1*/ var p: kotlin.Int + 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 open class WarningDeprecated { + public constructor WarningDeprecated() + @kotlin.Deprecated(level = DeprecationLevel.WARNING, message = "") public open var p: kotlin.Int + 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 + } +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 985d2f0630f..f746717f17f 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -4956,6 +4956,18 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("deprecatedInheritance.kt") + public void testDeprecatedInheritance() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/deprecated/deprecatedInheritance.kt"); + doTest(fileName); + } + + @TestMetadata("deprecatedPropertyInheritance.kt") + public void testDeprecatedPropertyInheritance() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/deprecated/deprecatedPropertyInheritance.kt"); + doTest(fileName); + } + @TestMetadata("functionUsage.kt") public void testFunctionUsage() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/deprecated/functionUsage.kt"); diff --git a/idea/resources/inspectionDescriptions/OverridingDeprecatedMember.html b/idea/resources/inspectionDescriptions/OverridingDeprecatedMember.html new file mode 100644 index 00000000000..54d3953197a --- /dev/null +++ b/idea/resources/inspectionDescriptions/OverridingDeprecatedMember.html @@ -0,0 +1,5 @@ + + +This inspection reports deprecated members being overridden. + + diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index dd3277f94bf..86c09967bf2 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1270,6 +1270,14 @@ level="WARNING" /> + + + + test.kt + 15 + light_idea_test_case + + Overriding deprecated member + Overrides deprecated member in 'foo.I' + + + test.kt + 20 + light_idea_test_case + + Overriding deprecated member + Overrides deprecated member in 'foo.I' + + + test.kt + 25 + light_idea_test_case + + Overriding deprecated member + Overrides deprecated member in 'foo.I' + + + + test.kt + 31 + light_idea_test_case + + Overriding deprecated member + Overrides deprecated member in 'foo.I' + + + + test.kt + 32 + light_idea_test_case + + Overriding deprecated member + Overrides deprecated member in 'foo.I' + + + + test.kt + 30 + light_idea_test_case + + Overriding deprecated member + Overrides deprecated member in 'foo.I' + + + + test.kt + 39 + light_idea_test_case + + Overriding deprecated member + Overrides deprecated member in 'foo.I' + + diff --git a/idea/testData/inspections/overridingDeprecatedMember/inspectionData/inspections.test b/idea/testData/inspections/overridingDeprecatedMember/inspectionData/inspections.test new file mode 100644 index 00000000000..cd9b3e9457f --- /dev/null +++ b/idea/testData/inspections/overridingDeprecatedMember/inspectionData/inspections.test @@ -0,0 +1 @@ +// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.OverridingDeprecatedMemberInspection \ No newline at end of file diff --git a/idea/testData/inspections/overridingDeprecatedMember/test.kt b/idea/testData/inspections/overridingDeprecatedMember/test.kt new file mode 100644 index 00000000000..ff2fa6ebc9e --- /dev/null +++ b/idea/testData/inspections/overridingDeprecatedMember/test.kt @@ -0,0 +1,47 @@ +package foo + +interface I { + @Deprecated("") + fun f(): Int + + @Deprecated("") + var p: Int + + var i: Int + @Deprecated("") set +} + +interface II : I { + override fun f(): Int { + } +} + +interface III : I { + override fun f(): Int { + } +} + +class C : I { + override fun f(): Int { + } +} + +interface IP : I { + override var p: Int + get() = throw UnsupportedOperationException() + set(value) { + } +} + +interface IA : I { + override var i: Int + get() = throw UnsupportedOperationException() + set(value) { + } +} + +interface Explicit : I { + @Deprecated("a") + override fun f(): Int { + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java index ca5fdaca7b1..1dd319e3793 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java @@ -118,6 +118,12 @@ public class InspectionTestGenerated extends AbstractInspectionTest { doTest(fileName); } + @TestMetadata("overridingDeprecatedMember/inspectionData/inspections.test") + public void testOverridingDeprecatedMember_inspectionData_Inspections_test() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/overridingDeprecatedMember/inspectionData/inspections.test"); + doTest(fileName); + } + @TestMetadata("redundantSamConstructor/inspectionData/inspections.test") public void testRedundantSamConstructor_inspectionData_Inspections_test() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/redundantSamConstructor/inspectionData/inspections.test");