From ffc1d22ae3449b379a757cd6be3cc886183d5abe Mon Sep 17 00:00:00 2001 From: nicholasdoglio Date: Sat, 27 Apr 2024 15:56:16 -0400 Subject: [PATCH] Make Anvil detectors configurable for anvil scopes --- .../MissingContributesBindingDetector.kt | 1 + .../detectors/MissingContributesToDetector.kt | 73 +++++++++++++++---- .../MissingContributesToDetectorTest.kt | 50 ++++++++++++- 3 files changed, 109 insertions(+), 15 deletions(-) diff --git a/lint/anvil/src/main/java/dev/whosnickdoglio/anvil/detectors/MissingContributesBindingDetector.kt b/lint/anvil/src/main/java/dev/whosnickdoglio/anvil/detectors/MissingContributesBindingDetector.kt index b6ad9cb5..482ddf8c 100644 --- a/lint/anvil/src/main/java/dev/whosnickdoglio/anvil/detectors/MissingContributesBindingDetector.kt +++ b/lint/anvil/src/main/java/dev/whosnickdoglio/anvil/detectors/MissingContributesBindingDetector.kt @@ -26,6 +26,7 @@ import org.jetbrains.uast.kotlin.isKotlin * `@ContributesBinding` or `@ContributesMultibinding` annotations for classes that use Dagger and * implement an interface or abstract class. */ +// TODO configurable internal class MissingContributesBindingDetector : Detector(), SourceCodeScanner { override fun getApplicableUastTypes(): List> = listOf(UClass::class.java) diff --git a/lint/anvil/src/main/java/dev/whosnickdoglio/anvil/detectors/MissingContributesToDetector.kt b/lint/anvil/src/main/java/dev/whosnickdoglio/anvil/detectors/MissingContributesToDetector.kt index 0de0b39e..b606ef6e 100644 --- a/lint/anvil/src/main/java/dev/whosnickdoglio/anvil/detectors/MissingContributesToDetector.kt +++ b/lint/anvil/src/main/java/dev/whosnickdoglio/anvil/detectors/MissingContributesToDetector.kt @@ -14,6 +14,7 @@ import com.android.tools.lint.detector.api.JavaContext import com.android.tools.lint.detector.api.Scope import com.android.tools.lint.detector.api.Severity import com.android.tools.lint.detector.api.SourceCodeScanner +import com.android.tools.lint.detector.api.StringOption import com.android.tools.lint.detector.api.TextFormat import dev.whosnickdoglio.lint.annotations.anvil.CONTRIBUTES_TO import dev.whosnickdoglio.lint.annotations.dagger.MODULE @@ -36,16 +37,47 @@ internal class MissingContributesToDetector : Detector(), SourceCodeScanner { val element = node.uastParent as? UClass ?: return if (!element.hasAnnotation(CONTRIBUTES_TO)) { + val anvilScopes = + customAnvilScopes.getValue(context).orEmpty().split(",").filter { + it.isNotEmpty() + } + context.report( Incident(context, ISSUE) .location(context.getNameLocation(element)) .message(ISSUE.getExplanation(TextFormat.RAW)) .fix( - fix() - .name("Add @ContributesTo annotation") - .annotate(CONTRIBUTES_TO, context, element) - .autoFix(robot = true, independent = true) - .build() + if (anvilScopes.isEmpty()) { + fix() + .name("Add @ContributesTo annotation") + .annotate(CONTRIBUTES_TO, context, element) + .autoFix(robot = true, independent = true) + .build() + } else { + fix() + .alternatives() + .apply { + anvilScopes.forEach { scope -> + add( + fix() + .name( + "Contribute to ${scope.substringAfterLast(".")} " + ) + .annotate( + "$CONTRIBUTES_TO($scope::class)", + context, + element, + ) + .autoFix( + robot = true, + independent = true, + ) + .build() + ) + } + } + .build() + } ) ) } @@ -58,18 +90,31 @@ internal class MissingContributesToDetector : Detector(), SourceCodeScanner { private val implementation = Implementation(MissingContributesToDetector::class.java, Scope.JAVA_FILE_SCOPE) + internal const val CUSTOM_ANVIL_SCOPE_OPTION_KEY = "anvilScopes" + + private val customAnvilScopes = + StringOption( + name = CUSTOM_ANVIL_SCOPE_OPTION_KEY, + description = "A comma separated list of fully qualified custom Hilt components", + explanation = + "Hilt provides you the ability to define custom Components if the " + + "preexisting ones don't work for your use case, If you have any custom Hilt components " + + "defined they can be added to the quickfix suggestions with this option. ", + ) + internal val ISSUE = Issue.create( - id = "MissingContributesToAnnotation", - briefDescription = "Module missing @ContributesTo annotation", - explanation = - """ + id = "MissingContributesToAnnotation", + briefDescription = "Module missing @ContributesTo annotation", + explanation = + """ This Dagger module is missing a `@ContributesTo` annotation for Anvil to pick it up. See https://whosnickdoglio.dev/dagger-rules/rules/#a-class-annotated-with-module-should-also-be-annotated-with-contributesto for more information. """, - category = Category.CORRECTNESS, - priority = 5, - severity = Severity.ERROR, - implementation = implementation, - ) + category = Category.CORRECTNESS, + priority = 5, + severity = Severity.ERROR, + implementation = implementation, + ) + .setOptions(listOf(customAnvilScopes)) } } diff --git a/lint/anvil/src/test/java/dev/whosnickdoglio/anvil/detectors/MissingContributesToDetectorTest.kt b/lint/anvil/src/test/java/dev/whosnickdoglio/anvil/detectors/MissingContributesToDetectorTest.kt index e59f2a51..7f15335c 100644 --- a/lint/anvil/src/test/java/dev/whosnickdoglio/anvil/detectors/MissingContributesToDetectorTest.kt +++ b/lint/anvil/src/test/java/dev/whosnickdoglio/anvil/detectors/MissingContributesToDetectorTest.kt @@ -257,7 +257,55 @@ class MissingContributesToDetectorTest { } @Test - fun `java provides module without @ContributesTo annotation shows an error`() { + fun `kotlin @Module without @ContributesTo with lint option set shows error with expected quickfix`() { + TestLintTask.lint() + .files( + daggerAnnotations, + TestFiles.kotlin( + """ + import dagger.Module + import dagger.Provides + + @Module + class MyModule { + + @Provides fun provideMyThing(): String = "Hello World" + + @Provides fun provideAnotherThing(): Int = 1 + + } + """ + ) + .indented(), + ) + .issues(MissingContributesToDetector.ISSUE) + .configureOption( + MissingContributesToDetector.CUSTOM_ANVIL_SCOPE_OPTION_KEY, + "dev.whosnickdoglio.anvil.AppScope", + ) + .run() + .expect( + """ + src/MyModule.kt:5: Error: This Dagger module is missing a @ContributesTo annotation for Anvil to pick it up. See https://whosnickdoglio.dev/dagger-rules/rules/#a-class-annotated-with-module-should-also-be-annotated-with-contributesto for more information. [MissingContributesToAnnotation] + class MyModule { + ~~~~~~~~ + 1 errors, 0 warnings + """ + .trimIndent() + ) + .expectErrorCount(1) + .expectFixDiffs( + """ + Autofix for src/MyModule.kt line 5: Contribute to AppScope : + @@ -4 +4 + + @com.squareup.anvil.annotations.ContributesTo(dev.whosnickdoglio.anvil.AppScope::class) + """ + .trimIndent() + ) + } + + @Test + fun `java provides module without @ContributesTo annotation shows no error`() { TestLintTask.lint() .files( daggerAnnotations,