diff --git a/reflect-lint/src/main/java/dagger/reflect/lint/WrongRetentionDetector.java b/reflect-lint/src/main/java/dagger/reflect/lint/WrongRetentionDetector.java index d92acdf7..db5108b5 100644 --- a/reflect-lint/src/main/java/dagger/reflect/lint/WrongRetentionDetector.java +++ b/reflect-lint/src/main/java/dagger/reflect/lint/WrongRetentionDetector.java @@ -6,6 +6,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.Collections; import java.util.List; +import kotlin.annotation.AnnotationRetention; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.uast.UAnnotation; @@ -30,8 +31,6 @@ public final class WrongRetentionDetector extends Detector implements Detector.U "kotlin.annotation.AnnotationRetention.RUNTIME"; private static final String FIX_ANNOTATION_RETENTION_JAVA = "@java.lang.annotation.Retention(" + FIX_RETENTION_TYPE_JAVA + ")\n"; - private static final String FIX_ANNOTATION_RETENTION_KOTLIN = - "@kotlin.annotation.Retention(" + FIX_RETENTION_TYPE_KOTLIN + ")\n"; @Override public List> getApplicableUastTypes() { @@ -56,15 +55,15 @@ public void visitClass(@NotNull UClass node) { final boolean isKotlin = Lint.isKotlin(node); final UAnnotation retentionAnnotation = node.findAnnotation(isKotlin ? ANNOTATION_RETENTION_KOTLIN : ANNOTATION_RETENTION_JAVA); - if (retentionAnnotation == null) { - final UAnnotation reflectRelatedAnnotation = - qualifierAnnotation != null ? qualifierAnnotation : mapKeyAnnotation; - reportMissingRetention(context, isKotlin, node, reflectRelatedAnnotation); - } else { + if (retentionAnnotation != null) { final String retentionPolicy = getRetentionPolicy(context, isKotlin, retentionAnnotation); if (!"RUNTIME".equals(retentionPolicy)) { reportWrongRetentionType(context, isKotlin, retentionAnnotation, retentionPolicy); } + } else if (!isKotlin) { + final UAnnotation reflectRelatedAnnotation = + qualifierAnnotation != null ? qualifierAnnotation : mapKeyAnnotation; + reportMissingRetention(context, node, reflectRelatedAnnotation); } } }; @@ -72,20 +71,19 @@ public void visitClass(@NotNull UClass node) { private static void reportMissingRetention( @NotNull JavaContext context, - boolean isKotlin, @NotNull UClass node, @NotNull UAnnotation reflectRelatedAnnotation) { context.report( ISSUE_WRONG_RETENTION, node, context.getNameLocation(node), - "Annotation used by Dagger Reflect must be annotated with `@Retention(RUNTIME)`.", + "Java annotations used by Dagger Reflect must be annotated with `@Retention(RUNTIME)`.", LintFix.create() .replace() .name("Add: `@Retention(RUNTIME)`") .range(context.getLocation(reflectRelatedAnnotation)) .beginning() - .with(isKotlin ? FIX_ANNOTATION_RETENTION_KOTLIN : FIX_ANNOTATION_RETENTION_JAVA) + .with(FIX_ANNOTATION_RETENTION_JAVA) .reformat(true) .shortenNames() .build()); @@ -102,7 +100,7 @@ private static void reportWrongRetentionType( retentionAnnotation, context.getLocation(retentionAnnotation), String.format( - "Annotation used by Dagger Reflect must be annotated with `@Retention(RUNTIME)` but is `@Retention(%s)`.", + "Annotations used by Dagger Reflect must have RUNTIME retention. Found %s.", actualRetention), LintFix.create() .name("Replace with: `@Retention(RUNTIME)`") @@ -116,61 +114,52 @@ private static void reportWrongRetentionType( @NotNull private static String getRetentionPolicy( - @NotNull JavaContext context, boolean isKotlin, @NotNull UAnnotation retentationAnnotation) { - final UExpression annotationValue = UastLintUtils.getAnnotationValue(retentationAnnotation); - final String retentionPolicyQualifiedName = - isKotlin - ? getQualifiedNameForValueKotlin(context, annotationValue) - : getQualifiedNameForValueJava(context, annotationValue); - final String retentionPolicy = getRetentionPolicyForQualifiedName(retentionPolicyQualifiedName); - if (retentionPolicy != null) { - return retentionPolicy; + @NotNull JavaContext context, boolean isKotlin, @NotNull UAnnotation retentionAnnotation) { + final UExpression annotationValue = UastLintUtils.getAnnotationValue(retentionAnnotation); + if (isKotlin) { + return getRetentionPolicyKotlin(context, annotationValue); + } else { + return getRetentionPolicyJava(context, annotationValue); } - throw new IllegalStateException("RetentionPolicy must not be null if @Retention is present"); } @NotNull - private static String getQualifiedNameForValueKotlin( + private static String getRetentionPolicyKotlin( @NotNull JavaContext context, @Nullable UExpression annotationValue) { final Object evaluatedAnnotationValue = ConstantEvaluator.evaluate(context, annotationValue); if (evaluatedAnnotationValue instanceof kotlin.Pair) { final kotlin.Pair value = (kotlin.Pair) evaluatedAnnotationValue; - final String qualifiedName = (value.getFirst() + "." + value.getSecond()); - return qualifiedName.replace("/", "."); + final String qualifiedName = (value.getFirst() + "." + value.getSecond()).replace("/", "."); + for (AnnotationRetention retention : AnnotationRetention.values()) { + if (qualifiedName.equals(CLASS_KOTLIN_RETENTION_POLICY + "." + retention.name())) { + return retention.name(); + } + } } - throw new IllegalStateException("RetentionPolicy must not be null if @Retention is present"); + throw new IllegalStateException("AnnotationRetention not found"); } @NotNull - private static String getQualifiedNameForValueJava( + private static String getRetentionPolicyJava( @NotNull JavaContext context, @Nullable UExpression annotationValue) { final Object evaluatedAnnotationValue = ConstantEvaluator.evaluate(context, annotationValue); if (evaluatedAnnotationValue instanceof PsiEnumConstant) { - return UastLintUtils.getQualifiedName((PsiEnumConstant) evaluatedAnnotationValue); - } - throw new IllegalStateException("RetentionPolicy must not be null if @Retention is present"); - } - - @Nullable - private static String getRetentionPolicyForQualifiedName(@NotNull String retentionPolicy) { - // Values are same for Kotlin and Java - for (RetentionPolicy policy : RetentionPolicy.values()) { - final String javaQualifiedName = CLASS_JAVA_RETENTION_POLICY + "." + policy.name(); - final String kotlinQualifiedName = CLASS_KOTLIN_RETENTION_POLICY + "." + policy.name(); - if (javaQualifiedName.equals(retentionPolicy) - || kotlinQualifiedName.equals(retentionPolicy)) { - return policy.name(); + final String qualifiedName = UastLintUtils.getQualifiedName( + (PsiEnumConstant) evaluatedAnnotationValue); + for (RetentionPolicy policy : RetentionPolicy.values()) { + if (qualifiedName.equals(CLASS_JAVA_RETENTION_POLICY + "." + policy.name())) { + return policy.name(); + } } } - return null; + throw new IllegalStateException("RetentionPolicy must not be null if @Retention is present"); } public static final Issue ISSUE_WRONG_RETENTION = Issue.create( "WrongRetention", - "Dagger annotations need to have Runtime Retention", - "To make annotation accessible during runtime for Dagger Reflect, " - + "the need to have the Retention annotation with the runtime RetentionPolicy.", + "Annotations used by Dagger Reflect must have RUNTIME retention", + "Annotations with SOURCE or CLASS/BINARY retention are not visible to reflection", Category.CORRECTNESS, 10, Severity.ERROR, diff --git a/reflect-lint/src/test/java/dagger/reflect/lint/java/WrongRetentionDetectorTest.java b/reflect-lint/src/test/java/dagger/reflect/lint/java/WrongRetentionDetectorTest.java index f9ecb3a5..e71bad0d 100644 --- a/reflect-lint/src/test/java/dagger/reflect/lint/java/WrongRetentionDetectorTest.java +++ b/reflect-lint/src/test/java/dagger/reflect/lint/java/WrongRetentionDetectorTest.java @@ -79,7 +79,7 @@ public void ignoresQualifierAnnotationWithRuntimeRetention() { } @Test - public void reportsQualifierAnnotationWithWrongRetentionAsStaticImport() { + public void reportsQualifierAnnotationWithSourceRetentionAsStaticImport() { lint() .files( java( @@ -97,7 +97,7 @@ public void reportsQualifierAnnotationWithWrongRetentionAsStaticImport() { .issues(WrongRetentionDetector.ISSUE_WRONG_RETENTION) .run() .expect( - "src/foo/MyQualifier.java:9: Error: Annotation used by Dagger Reflect must be annotated with @Retention(RUNTIME) but is @Retention(SOURCE). [WrongRetention]\n" + "src/foo/MyQualifier.java:9: Error: Annotations used by Dagger Reflect must have RUNTIME retention. Found SOURCE. [WrongRetention]\n" + "@Retention(SOURCE)\n" + "~~~~~~~~~~~~~~~~~~\n" + "1 errors, 0 warnings") @@ -108,6 +108,36 @@ public void reportsQualifierAnnotationWithWrongRetentionAsStaticImport() { + "+ @Retention(java.lang.annotation.RetentionPolicy.RUNTIME)"); } + @Test + public void reportsQualifierAnnotationWithClassRetentionAsStaticImport() { + lint() + .files( + java( + "package foo;\n" + + "\n" + + "import static java.lang.annotation.RetentionPolicy.CLASS;\n" + + "\n" + + "import javax.inject.Qualifier;\n" + + "import java.lang.annotation.Retention;\n" + + "\n" + + "@Qualifier\n" + + "@Retention(CLASS)\n" + + "public @interface MyQualifier {}"), + QUALIFIER_STUB) + .issues(WrongRetentionDetector.ISSUE_WRONG_RETENTION) + .run() + .expect( + "src/foo/MyQualifier.java:9: Error: Annotations used by Dagger Reflect must have RUNTIME retention. Found CLASS. [WrongRetention]\n" + + "@Retention(CLASS)\n" + + "~~~~~~~~~~~~~~~~~\n" + + "1 errors, 0 warnings") + .expectFixDiffs( + "Fix for src/foo/MyQualifier.java line 9: Replace with: `@Retention(RUNTIME)`:\n" + + "@@ -9 +9\n" + + "- @Retention(CLASS)\n" + + "+ @Retention(java.lang.annotation.RetentionPolicy.RUNTIME)"); + } + @Test public void reportsQualifierAnnotationWithWrongRetentionAsNormalImport() { lint() @@ -126,7 +156,7 @@ public void reportsQualifierAnnotationWithWrongRetentionAsNormalImport() { .issues(WrongRetentionDetector.ISSUE_WRONG_RETENTION) .run() .expect( - "src/foo/MyQualifier.java:8: Error: Annotation used by Dagger Reflect must be annotated with @Retention(RUNTIME) but is @Retention(SOURCE). [WrongRetention]\n" + "src/foo/MyQualifier.java:8: Error: Annotations used by Dagger Reflect must have RUNTIME retention. Found SOURCE. [WrongRetention]\n" + "@Retention(RetentionPolicy.SOURCE)\n" + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "1 errors, 0 warnings") @@ -155,7 +185,7 @@ public void reportsQualifierAnnotationWithWrongRetentionAsNormalImportWithAssign .issues(WrongRetentionDetector.ISSUE_WRONG_RETENTION) .run() .expect( - "src/foo/MyQualifier.java:8: Error: Annotation used by Dagger Reflect must be annotated with @Retention(RUNTIME) but is @Retention(SOURCE). [WrongRetention]\n" + "src/foo/MyQualifier.java:8: Error: Annotations used by Dagger Reflect must have RUNTIME retention. Found SOURCE. [WrongRetention]\n" + "@Retention(value = RetentionPolicy.SOURCE)\n" + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "1 errors, 0 warnings") @@ -181,7 +211,7 @@ public void reportsQualifierAnnotationWithoutRetention() { .issues(WrongRetentionDetector.ISSUE_WRONG_RETENTION) .run() .expect( - "src/foo/MyQualifier.java:6: Error: Annotation used by Dagger Reflect must be annotated with @Retention(RUNTIME). [WrongRetention]\n" + "src/foo/MyQualifier.java:6: Error: Java annotations used by Dagger Reflect must be annotated with @Retention(RUNTIME). [WrongRetention]\n" + "public @interface MyQualifier {}\n" + " ~~~~~~~~~~~\n" + "1 errors, 0 warnings") @@ -259,7 +289,7 @@ public void reportsMapKeyAnnotationWithWrongRetention() { .issues(WrongRetentionDetector.ISSUE_WRONG_RETENTION) .run() .expect( - "src/foo/MyMapKey.java:9: Error: Annotation used by Dagger Reflect must be annotated with @Retention(RUNTIME) but is @Retention(SOURCE). [WrongRetention]\n" + "src/foo/MyMapKey.java:9: Error: Annotations used by Dagger Reflect must have RUNTIME retention. Found SOURCE. [WrongRetention]\n" + "@Retention(SOURCE)\n" + "~~~~~~~~~~~~~~~~~~\n" + "1 errors, 0 warnings") @@ -285,7 +315,7 @@ public void reportsMapKeyAnnotationWithoutRetention() { .issues(WrongRetentionDetector.ISSUE_WRONG_RETENTION) .run() .expect( - "src/foo/MyMapKey.java:6: Error: Annotation used by Dagger Reflect must be annotated with @Retention(RUNTIME). [WrongRetention]\n" + "src/foo/MyMapKey.java:6: Error: Java annotations used by Dagger Reflect must be annotated with @Retention(RUNTIME). [WrongRetention]\n" + "public @interface MyMapKey {}\n" + " ~~~~~~~~\n" + "1 errors, 0 warnings") diff --git a/reflect-lint/src/test/java/dagger/reflect/lint/kotlin/WrongRetentionDetectorTest.java b/reflect-lint/src/test/java/dagger/reflect/lint/kotlin/WrongRetentionDetectorTest.java index a4b4caca..a4b7131f 100644 --- a/reflect-lint/src/test/java/dagger/reflect/lint/kotlin/WrongRetentionDetectorTest.java +++ b/reflect-lint/src/test/java/dagger/reflect/lint/kotlin/WrongRetentionDetectorTest.java @@ -54,6 +54,23 @@ public void ignoresIrrelevantQualifierAnnotation() { .expectClean(); } + @Test + public void ignoresQualifierAnnotationWithDefaultRetention() { + lint() + .files( + kotlin( + "package foo\n" + + "\n" + + "import javax.inject.Qualifier\n" + + "\n" + + "@Qualifier\n" + + "internal annotation class MyQualifier"), + QUALIFIER_STUB) + .issues(WrongRetentionDetector.ISSUE_WRONG_RETENTION) + .run() + .expectClean(); + } + @Test public void ignoresQualifierAnnotationWithRuntimeRetention() { lint() @@ -74,7 +91,7 @@ public void ignoresQualifierAnnotationWithRuntimeRetention() { } @Test - public void reportsQualifierAnnotationWithWrongRetentionAsStaticImport() { + public void reportsQualifierAnnotationWithSourceRetentionAsStaticImport() { lint() .files( kotlin( @@ -90,7 +107,7 @@ public void reportsQualifierAnnotationWithWrongRetentionAsStaticImport() { .issues(WrongRetentionDetector.ISSUE_WRONG_RETENTION) .run() .expect( - "src/foo/MyQualifier.kt:7: Error: Annotation used by Dagger Reflect must be annotated with @Retention(RUNTIME) but is @Retention(SOURCE). [WrongRetention]\n" + "src/foo/MyQualifier.kt:7: Error: Annotations used by Dagger Reflect must have RUNTIME retention. Found SOURCE. [WrongRetention]\n" + "@Retention(SOURCE)\n" + "~~~~~~~~~~~~~~~~~~\n" + "1 errors, 0 warnings") @@ -102,34 +119,35 @@ public void reportsQualifierAnnotationWithWrongRetentionAsStaticImport() { } @Test - public void reportsQualifierAnnotationWithWrongRetentionAsNormalImport() { + public void reportsQualifierAnnotationWithBinaryRetentionAsStaticImport() { lint() .files( kotlin( "package foo\n" + "\n" + "import javax.inject.Qualifier\n" + + "import kotlin.annotation.AnnotationRetention.BINARY\n" + "\n" + "@Qualifier\n" - + "@Retention(AnnotationRetention.SOURCE)\n" + + "@Retention(BINARY)\n" + "internal annotation class MyQualifier"), QUALIFIER_STUB) .issues(WrongRetentionDetector.ISSUE_WRONG_RETENTION) .run() .expect( - "src/foo/MyQualifier.kt:6: Error: Annotation used by Dagger Reflect must be annotated with @Retention(RUNTIME) but is @Retention(SOURCE). [WrongRetention]\n" - + "@Retention(AnnotationRetention.SOURCE)\n" - + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "src/foo/MyQualifier.kt:7: Error: Annotations used by Dagger Reflect must have RUNTIME retention. Found BINARY. [WrongRetention]\n" + + "@Retention(BINARY)\n" + + "~~~~~~~~~~~~~~~~~~\n" + "1 errors, 0 warnings") .expectFixDiffs( - "Fix for src/foo/MyQualifier.kt line 6: Replace with: `@Retention(RUNTIME)`:\n" - + "@@ -6 +6\n" - + "- @Retention(AnnotationRetention.SOURCE)\n" + "Fix for src/foo/MyQualifier.kt line 7: Replace with: `@Retention(RUNTIME)`:\n" + + "@@ -7 +7\n" + + "- @Retention(BINARY)\n" + "+ @Retention(kotlin.annotation.AnnotationRetention.RUNTIME)"); } @Test - public void reportsQualifierAnnotationWithWrongRetentionAsNormalImportWithAssignment() { + public void reportsQualifierAnnotationWithWrongRetentionAsNormalImport() { lint() .files( kotlin( @@ -138,25 +156,25 @@ public void reportsQualifierAnnotationWithWrongRetentionAsNormalImportWithAssign + "import javax.inject.Qualifier\n" + "\n" + "@Qualifier\n" - + "@Retention(value = AnnotationRetention.SOURCE)\n" + + "@Retention(AnnotationRetention.SOURCE)\n" + "internal annotation class MyQualifier"), QUALIFIER_STUB) .issues(WrongRetentionDetector.ISSUE_WRONG_RETENTION) .run() .expect( - "src/foo/MyQualifier.kt:6: Error: Annotation used by Dagger Reflect must be annotated with @Retention(RUNTIME) but is @Retention(SOURCE). [WrongRetention]\n" - + "@Retention(value = AnnotationRetention.SOURCE)\n" - + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "src/foo/MyQualifier.kt:6: Error: Annotations used by Dagger Reflect must have RUNTIME retention. Found SOURCE. [WrongRetention]\n" + + "@Retention(AnnotationRetention.SOURCE)\n" + + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "1 errors, 0 warnings") .expectFixDiffs( "Fix for src/foo/MyQualifier.kt line 6: Replace with: `@Retention(RUNTIME)`:\n" + "@@ -6 +6\n" - + "- @Retention(value = AnnotationRetention.SOURCE)\n" - + "+ @Retention(value = kotlin.annotation.AnnotationRetention.RUNTIME)"); + + "- @Retention(AnnotationRetention.SOURCE)\n" + + "+ @Retention(kotlin.annotation.AnnotationRetention.RUNTIME)"); } @Test - public void reportsQualifierAnnotationWithoutRetention() { + public void reportsQualifierAnnotationWithWrongRetentionAsNormalImportWithAssignment() { lint() .files( kotlin( @@ -165,19 +183,21 @@ public void reportsQualifierAnnotationWithoutRetention() { + "import javax.inject.Qualifier\n" + "\n" + "@Qualifier\n" + + "@Retention(value = AnnotationRetention.SOURCE)\n" + "internal annotation class MyQualifier"), QUALIFIER_STUB) .issues(WrongRetentionDetector.ISSUE_WRONG_RETENTION) .run() .expect( - "src/foo/MyQualifier.kt:6: Error: Annotation used by Dagger Reflect must be annotated with @Retention(RUNTIME). [WrongRetention]\n" - + "internal annotation class MyQualifier\n" - + " ~~~~~~~~~~~\n" + "src/foo/MyQualifier.kt:6: Error: Annotations used by Dagger Reflect must have RUNTIME retention. Found SOURCE. [WrongRetention]\n" + + "@Retention(value = AnnotationRetention.SOURCE)\n" + + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "1 errors, 0 warnings") .expectFixDiffs( - "Fix for src/foo/MyQualifier.kt line 6: Add: `@Retention(RUNTIME)`:\n" - + "@@ -5 +5\n" - + "+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME)"); + "Fix for src/foo/MyQualifier.kt line 6: Replace with: `@Retention(RUNTIME)`:\n" + + "@@ -6 +6\n" + + "- @Retention(value = AnnotationRetention.SOURCE)\n" + + "+ @Retention(value = kotlin.annotation.AnnotationRetention.RUNTIME)"); } // MapKey Annotation @@ -209,17 +229,15 @@ public void ignoresIrrelevantMapKeyAnnotation() { } @Test - public void ignoresMapKeyAnnotationWithRuntimeRetention() { + public void ignoresMapKeyAnnotationWithDefaultRetention() { lint() .files( kotlin( "package foo\n" + "\n" + "import dagger.MapKey\n" - + "import kotlin.annotation.AnnotationRetention.RUNTIME\n" + "\n" + "@MapKey\n" - + "@Retention(RUNTIME)\n" + "internal annotation class MyMapKey"), MAP_KEY_STUB) .issues(WrongRetentionDetector.ISSUE_WRONG_RETENTION) @@ -228,34 +246,26 @@ public void ignoresMapKeyAnnotationWithRuntimeRetention() { } @Test - public void reportsMapKeyAnnotationWithWrongRetention() { + public void ignoresMapKeyAnnotationWithRuntimeRetention() { lint() .files( kotlin( "package foo\n" + "\n" + "import dagger.MapKey\n" + + "import kotlin.annotation.AnnotationRetention.RUNTIME\n" + "\n" + "@MapKey\n" - + "@Retention(AnnotationRetention.SOURCE)\n" + + "@Retention(RUNTIME)\n" + "internal annotation class MyMapKey"), MAP_KEY_STUB) .issues(WrongRetentionDetector.ISSUE_WRONG_RETENTION) .run() - .expect( - "src/foo/MyMapKey.kt:6: Error: Annotation used by Dagger Reflect must be annotated with @Retention(RUNTIME) but is @Retention(SOURCE). [WrongRetention]\n" - + "@Retention(AnnotationRetention.SOURCE)\n" - + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" - + "1 errors, 0 warnings") - .expectFixDiffs( - "Fix for src/foo/MyMapKey.kt line 6: Replace with: `@Retention(RUNTIME)`:\n" - + "@@ -6 +6\n" - + "- @Retention(AnnotationRetention.SOURCE)\n" - + "+ @Retention(kotlin.annotation.AnnotationRetention.RUNTIME)"); + .expectClean(); } @Test - public void reportsMapKeyAnnotationWithoutRetention() { + public void reportsMapKeyAnnotationWithWrongRetention() { lint() .files( kotlin( @@ -264,19 +274,21 @@ public void reportsMapKeyAnnotationWithoutRetention() { + "import dagger.MapKey\n" + "\n" + "@MapKey\n" + + "@Retention(AnnotationRetention.SOURCE)\n" + "internal annotation class MyMapKey"), MAP_KEY_STUB) .issues(WrongRetentionDetector.ISSUE_WRONG_RETENTION) .run() .expect( - "src/foo/MyMapKey.kt:6: Error: Annotation used by Dagger Reflect must be annotated with @Retention(RUNTIME). [WrongRetention]\n" - + "internal annotation class MyMapKey\n" - + " ~~~~~~~~\n" + "src/foo/MyMapKey.kt:6: Error: Annotations used by Dagger Reflect must have RUNTIME retention. Found SOURCE. [WrongRetention]\n" + + "@Retention(AnnotationRetention.SOURCE)\n" + + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "1 errors, 0 warnings") .expectFixDiffs( - "Fix for src/foo/MyMapKey.kt line 6: Add: `@Retention(RUNTIME)`:\n" - + "@@ -5 +5\n" - + "+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME)"); + "Fix for src/foo/MyMapKey.kt line 6: Replace with: `@Retention(RUNTIME)`:\n" + + "@@ -6 +6\n" + + "- @Retention(AnnotationRetention.SOURCE)\n" + + "+ @Retention(kotlin.annotation.AnnotationRetention.RUNTIME)"); } private static final LintDetectorTest.TestFile MAP_KEY_STUB =