diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index d14b99dae2b..0e2c7e78112 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -223,6 +223,7 @@ public interface Errors { DiagnosticFactory1 DEPRECATED_SYMBOL = DiagnosticFactory1.create(WARNING); DiagnosticFactory2 DEPRECATED_SYMBOL_WITH_MESSAGE = DiagnosticFactory2.create(WARNING); + DiagnosticFactory1 DEPRECATED_DECAPITALIZED_ANNOTATION = DiagnosticFactory1.create(WARNING); // Objects 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 c4c61d4e453..f8e19fdec10 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -273,6 +273,7 @@ public class DefaultErrorMessages { MAP.put(DEPRECATED_SYMBOL, "''{0}'' is deprecated.", DEPRECATION_RENDERER); MAP.put(DEPRECATED_SYMBOL_WITH_MESSAGE, "''{0}'' is deprecated. {1}", DEPRECATION_RENDERER, STRING); + MAP.put(DEPRECATED_DECAPITALIZED_ANNOTATION, "Decapitalized annotations are deprecated. Use ''{0}'' instead", DEPRECATION_RENDERER); MAP.put(LOCAL_OBJECT_NOT_ALLOWED, "Named object ''{0}'' is a singleton and cannot be local. Try to use anonymous object instead", NAME); MAP.put(ENUM_CLASS_CONSTRUCTOR_CALL, "Enum types cannot be instantiated"); 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 ace49dcd77d..ee9971fd2df 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/validation/DeprecatedSymbolValidator.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/validation/DeprecatedSymbolValidator.kt @@ -33,6 +33,10 @@ import org.jetbrains.kotlin.resolve.annotations.argumentValue import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget.PROPERTY_GETTER import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget.PROPERTY_SETTER import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe +import org.jetbrains.kotlin.resolve.scopes.DECAPITALIZED_DEPRECATED_ANNOTATIONS +import org.jetbrains.kotlin.resolve.scopes.DECAPITALIZED_SHORT_NAMES public class DeprecatedSymbolValidator : SymbolUsageValidator { private val JAVA_DEPRECATED = FqName(java.lang.Deprecated::class.java.name) @@ -46,6 +50,9 @@ public class DeprecatedSymbolValidator : SymbolUsageValidator { else if (targetDescriptor is PropertyDescriptor) { propertyGetterWorkaround(targetDescriptor, trace, element) } + else { + checkDecapitalizedAnnotation(element, targetDescriptor, trace) + } } override fun validateTypeUsage(targetDescriptor: ClassifierDescriptor, trace: BindingTrace, element: PsiElement) { @@ -64,6 +71,32 @@ public class DeprecatedSymbolValidator : SymbolUsageValidator { val (annotation, target) = deprecated trace.report(createDeprecationDiagnostic(element, target, annotation)) } + else { + checkDecapitalizedAnnotation(element, targetDescriptor, trace) + } + } + + private fun checkDecapitalizedAnnotation(element: PsiElement, targetDescriptor: DeclarationDescriptor, trace: BindingTrace) { + val effectiveTargetDescriptor = when (targetDescriptor) { + is ConstructorDescriptor -> targetDescriptor.containingDeclaration + else -> targetDescriptor + } as? ClassDescriptor ?: return + + val unsafeFqName = effectiveTargetDescriptor.fqNameUnsafe + if (!unsafeFqName.isSafe) return + + val simpleName = unwrapSimpleName(element) ?: return + if (unsafeFqName.toSafe() in DECAPITALIZED_DEPRECATED_ANNOTATIONS && simpleName.getReferencedName() in DECAPITALIZED_SHORT_NAMES) { + trace.report(Errors.DEPRECATED_DECAPITALIZED_ANNOTATION.on(simpleName, effectiveTargetDescriptor)) + } + } + + private fun unwrapSimpleName(element: PsiElement): JetSimpleNameExpression? { + return when (element) { + is JetConstructorCalleeExpression -> (element.typeReference?.typeElement as? JetUserType)?.referenceExpression + is JetSimpleNameExpression -> element + else -> null + } } private fun DeclarationDescriptor.getDeprecatedAnnotation(): Pair? { diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/decapitalized.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/decapitalized.kt new file mode 100644 index 00000000000..ccd1eba5aab --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/decapitalized.kt @@ -0,0 +1,23 @@ +// !DIAGNOSTICS: -DEPRECATED_UNESCAPED_ANNOTATION -UNUSED_PARAMETER +import kotlin.Extension as extension +import kotlin.jvm.strictfp as sfp +class A { + @deprecated("") fun foo() {} + companion object { + @jvmStatic fun bar() {} + } + + throws(java.lang.RuntimeException::class) + synchronized fun baz() { + @suppress("UNCHECKED_CAST") + (1 as T) + } + + kotlin.jvm.jvmName("y") + jvmOverloads() + fun x(x: Int = 1) {} +} + +sfp fun bar( + x: deprecated +): @extension String.() -> Int = null!! diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/decapitalized.txt b/compiler/testData/diagnostics/testsWithStdLib/annotations/decapitalized.txt new file mode 100644 index 00000000000..bf88d070489 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/decapitalized.txt @@ -0,0 +1,21 @@ +package + +kotlin.jvm.Strictfp() public fun bar(/*0*/ x: kotlin.Deprecated): @[kotlin.Extension()] kotlin.String.() -> kotlin.Int + +public final class A { + public constructor A() + kotlin.Throws(exceptionClasses = {java.lang.RuntimeException::class}) kotlin.jvm.Synchronized() public final fun baz(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + kotlin.Deprecated(value = "") public final fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + kotlin.jvm.JvmName(name = "y") kotlin.jvm.JvmOverloads() public final fun x(/*0*/ x: kotlin.Int = ...): kotlin.Unit + + public companion object Companion { + private constructor Companion() + kotlin.jvm.JvmStatic() public final fun bar(): kotlin.Unit + 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/JetDiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java index d76e3b2b3e9..8a2d14d24cc 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java @@ -91,6 +91,12 @@ public class JetDiagnosticsTestWithStdLibGenerated extends AbstractJetDiagnostic doTest(fileName); } + @TestMetadata("decapitalized.kt") + public void testDecapitalized() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/annotations/decapitalized.kt"); + doTest(fileName); + } + @TestMetadata("defaultValueMustBeConstant.kt") public void testDefaultValueMustBeConstant() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/annotations/defaultValueMustBeConstant.kt"); diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/JetPsiChecker.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/JetPsiChecker.kt index 43c6332c626..7471e573bb6 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/JetPsiChecker.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/JetPsiChecker.kt @@ -153,7 +153,8 @@ public open class JetPsiChecker : Annotator, HighlightRangeExtension { when (factory) { Errors.DEPRECATED_SYMBOL, Errors.DEPRECATED_SYMBOL_WITH_MESSAGE, - Errors.DEPRECATED_TRAIT_KEYWORD + Errors.DEPRECATED_TRAIT_KEYWORD, + Errors.DEPRECATED_DECAPITALIZED_ANNOTATION -> annotation.setTextAttributes(CodeInsightColors.DEPRECATED_ATTRIBUTES) }