Skip to content

Commit

Permalink
Adding a rule to suggest constructor injection over Provides method
Browse files Browse the repository at this point in the history
  • Loading branch information
WhosNickDoglio committed Apr 13, 2023
1 parent 4bb081f commit e0f4700
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 0 deletions.
2 changes: 2 additions & 0 deletions docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Prefer constructor injection over field injection

## Prefer constructor injection over @Provides methods

## Classes with @Provides or @Binds methods should be annotated with @Module

## A @Binds method parameter should be a subclass of it's return type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.android.tools.lint.detector.api.Issue
import dev.whosnickdoglio.dagger.detectors.ComponentMustBeAbstractDetector
import dev.whosnickdoglio.dagger.detectors.ConstructorInjectionOverFieldInjectionDetector
import dev.whosnickdoglio.dagger.detectors.CorrectBindsUsageDetector
import dev.whosnickdoglio.dagger.detectors.ConstructorInjectionOverProvidesDetector
import dev.whosnickdoglio.dagger.detectors.MissingModuleAnnotationDetector
import dev.whosnickdoglio.dagger.detectors.StaticProvidesDetector

Expand All @@ -22,6 +23,7 @@ class DaggerRulesIssueRegistry : IssueRegistry() {
ConstructorInjectionOverFieldInjectionDetector.ISSUE,
CorrectBindsUsageDetector.ISSUE_BINDS_ABSTRACT,
CorrectBindsUsageDetector.ISSUE_CORRECT_RETURN_TYPE,
ConstructorInjectionOverProvidesDetector.ISSUE,
MissingModuleAnnotationDetector.ISSUE,
StaticProvidesDetector.ISSUE,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (C) 2023 Nicholas Doglio
* SPDX-License-Identifier: MIT
*/
package dev.whosnickdoglio.dagger.detectors

import com.android.tools.lint.client.api.UElementHandler
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Implementation
import com.android.tools.lint.detector.api.Issue
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.TextFormat
import dev.whosnickdoglio.lint.shared.PROVIDES
import org.jetbrains.uast.UAnnotation
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.UElement
import org.jetbrains.uast.util.isConstructorCall

internal class ConstructorInjectionOverProvidesDetector : Detector(), SourceCodeScanner {

override fun getApplicableUastTypes(): List<Class<out UElement>> =
listOf(UAnnotation::class.java)

override fun createUastHandler(context: JavaContext): UElementHandler =
object : UElementHandler() {
override fun visitAnnotation(node: UAnnotation) {
if (node.qualifiedName == PROVIDES) {
val method = node.uastParent as? UCallExpression ?: return
if (method.isConstructorCall()) {
context.report(
issue = ISSUE,
location = context.getLocation(method),
message = ISSUE.getExplanation(TextFormat.TEXT)
)
}
}
}
}

companion object {

private val implementation =
Implementation(
ConstructorInjectionOverProvidesDetector::class.java,
Scope.JAVA_FILE_SCOPE
)
val ISSUE =
Issue.create(
id = "ConstructorInjectionOverProvidesMethods",
briefDescription = "@Provides method used instead of constructor injection",
explanation =
"""
`@Provides` methods are great for adding third party libraries or classes that require Builders or Factories \
to the Dagger graph but for classes with simple constructors you should just add a `@Inject` annotation to the constructor
""",
category = Category.CORRECTNESS,
priority = 5,
severity = Severity.WARNING,
implementation = implementation
)
.setEnabledByDefault(false)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2023 Nicholas Doglio
* SPDX-License-Identifier: MIT
*/
package dev.whosnickdoglio.dagger.detectors

import com.android.tools.lint.checks.infrastructure.TestFiles.kotlin
import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
import dev.whosnickdoglio.stubs.daggerAnnotations
import org.junit.Test

class ConstructorInjectionOverProvidesDetectorTest {

// TODO java and companion object tests
// TODO multiple @Provides

@Test
fun `class that could use constructor injection triggers provides warning`() {
lint()
.files(
daggerAnnotations,
kotlin(
"""
package com.test.android
import dagger.Provides
import dagger.Module
class MyClass
@Module
object MyModule {
@Provides
fun provideMyClass(): MyClass {
return MyClass()
}
}
"""
)
.indented()
)
.issues(ConstructorInjectionOverProvidesDetector.ISSUE)
.run()
.expectClean()
}
}

0 comments on commit e0f4700

Please sign in to comment.