Created
September 11, 2023 21:08
-
-
Save princeparadoxes/f6e79fb7af6ee7987934bbb25e5435f9 to your computer and use it in GitHub Desktop.
DaggerUnusedValidator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ru.cian.validate.dagger.deps.unused | |
import com.google.auto.service.AutoService | |
import dagger.Component | |
import dagger.model.BindingGraph | |
import dagger.spi.BindingGraphPlugin | |
import dagger.spi.DiagnosticReporter | |
import javax.lang.model.element.AnnotationValue | |
import javax.lang.model.element.Element | |
import javax.lang.model.element.ElementKind | |
import javax.lang.model.element.ExecutableElement | |
import javax.lang.model.type.DeclaredType | |
import javax.tools.Diagnostic | |
@AutoService(BindingGraphPlugin::class) | |
class DaggerUnusedValidator : BindingGraphPlugin { | |
override fun visitGraph( | |
bindingGraph: BindingGraph, | |
diagnosticReporter: DiagnosticReporter | |
) { | |
val currentComponent = bindingGraph.rootComponentNode() | |
if (!currentComponent.isRealComponent || isSuppress(currentComponent.componentPath().currentComponent())) { | |
return | |
} | |
val dependencies = getDependencies(currentComponent) | |
val bindings = bindingGraph.bindings().map { contextBinding -> contextBinding.key().type().toString() } | |
dependencies.forEach { (dependency, dependencyMethods) -> | |
val unusedMethods = dependencyMethods.subtract(bindings) | |
if (unusedMethods.size == dependencyMethods.size) { | |
diagnosticReporter.reportComponent( | |
Diagnostic.Kind.ERROR, | |
currentComponent, | |
"Dependency ${dependency} is unused" | |
) | |
} | |
} | |
} | |
private fun getDependencies( | |
componentNode: BindingGraph.ComponentNode | |
): Map<String, List<String>> { | |
val dependenciesTypes = getComponentDependencies(componentNode) | |
val dependenciesMap = dependenciesTypes | |
.associateWith { getMethodsReturnTypes(it) } | |
.mapKeys { it.key.toString() } | |
return dependenciesMap | |
} | |
private fun getMethodsReturnTypes(declaredType: DeclaredType): List<String> { | |
val methods = declaredType.asElement().enclosedElements | |
.filter { it.kind == ElementKind.METHOD } | |
.map { it as ExecutableElement } | |
val returnTypes = methods | |
.map { it.returnType.toString() } | |
return returnTypes | |
} | |
@Suppress("UNCHECKED_CAST") | |
private fun getComponentDependencies(componentNode: BindingGraph.ComponentNode): List<DeclaredType> { | |
val dependenciesMethod = getDependenciesMethod(componentNode) | |
return if (dependenciesMethod != null) { | |
(dependenciesMethod.value as List<AnnotationValue>) | |
.map { it.value } | |
.map { it as DeclaredType } | |
.filter { !isSuppress(it.asElement()) } | |
} else { | |
emptyList() | |
} | |
} | |
private fun getDependenciesMethod(componentNode: BindingGraph.ComponentNode): AnnotationValue? { | |
val annotationMirrors = componentNode.componentPath().currentComponent().annotationMirrors | |
val componentAnnotationMirror = annotationMirrors | |
.first { it.annotationType.toString() == Component::class.java.name } | |
val dependenciesMethod = componentAnnotationMirror.elementValues.entries | |
.firstOrNull { it.key.simpleName.toString() == Component::dependencies.name } | |
return dependenciesMethod?.value | |
} | |
private fun isSuppress(element: Element): Boolean { | |
val annotationMirrors = element.annotationMirrors | |
val suppressAnnotationMirror = annotationMirrors | |
.firstOrNull { it.annotationType.toString() == SuppressDaggerUnusedDeps::class.java.name } | |
return suppressAnnotationMirror != null | |
} | |
} | |
@Retention(AnnotationRetention.RUNTIME) | |
@Target(AnnotationTarget.CLASS) | |
annotation class SuppressDaggerUnusedDeps |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment