All Downloads are FREE. Search and download functionalities are using the official Maven repository.

schwarz.it.lightsaber.checkers.UnusedModules.kt Maven / Gradle / Ivy

Go to download

A Dagger 2 plugin to find unused dependencies declared in your Modules and Components.

There is a newer version: 0.0.20
Show newest version
package schwarz.it.lightsaber.checkers

import dagger.spi.model.BindingGraph
import dagger.spi.model.DaggerProcessingEnv
import schwarz.it.lightsaber.CodePosition
import schwarz.it.lightsaber.Finding
import schwarz.it.lightsaber.domain.Module
import schwarz.it.lightsaber.domain.hasSuppress
import schwarz.it.lightsaber.utils.TreeNode
import schwarz.it.lightsaber.utils.getDeclaredModules
import schwarz.it.lightsaber.utils.getModulesCodePosition
import schwarz.it.lightsaber.utils.getUsedModules

internal fun checkUnusedModules(
    bindingGraph: BindingGraph,
    daggerProcessingEnv: DaggerProcessingEnv,
): List {
    val used = bindingGraph.getUsedModules()
    return bindingGraph.componentNodes()
        .flatMap { component ->
            component.getDeclaredModules(bindingGraph, daggerProcessingEnv)
                .flatMap {
                    getErrorMessages(
                        used = used,
                        node = it,
                        daggerProcessingEnv = daggerProcessingEnv,
                        codePosition = { component.getModulesCodePosition(daggerProcessingEnv) },
                    )
                }
                .filterNot { it.module.toString().startsWith("anvil.module.") }
                .map {
                    Finding(
                        it.message,
                        it.codePosition,
                        component.componentPath().currentComponent()::hasSuppress,
                    )
                }
        }
}

private fun getErrorMessages(
    used: Set,
    node: TreeNode,
    daggerProcessingEnv: DaggerProcessingEnv,
    codePosition: () -> CodePosition,
    path: List = emptyList(),
): List {
    return buildList {
        if (!used.contains(node.value)) {
            val usedChildren = findUsedChildren(used, node)
            add(
                UnusedModuleFinding(
                    generateMessage(usedChildren.map { it.value }, node.value, path),
                    codePosition.invoke(),
                    node.value,
                ),
            )
            val newPath = path.plus(node.value.toString())
            addAll(
                usedChildren.flatMap { child ->
                    getErrorMessages(
                        used = used,
                        node = child,
                        daggerProcessingEnv = daggerProcessingEnv,
                        codePosition = { node.value.getIncludesCodePosition(daggerProcessingEnv) },
                        path = newPath,
                    )
                },
            )
        } else {
            val newPath = path.plus(node.value.toString())
            addAll(
                node.children.flatMap { child ->
                    getErrorMessages(
                        used = used,
                        node = child,
                        daggerProcessingEnv,
                        codePosition = { node.value.getIncludesCodePosition(daggerProcessingEnv) },
                        path = newPath,
                    )
                },
            )
        }
    }
}

data class UnusedModuleFinding(val message: String, val codePosition: CodePosition, val module: Module)

private fun generateMessage(
    usedChildren: List,
    node: Module,
    path: List = emptyList(),
): String {
    val prefix = if (path.isEmpty()) {
        "The @Module `$node`"
    } else {
        "The @Module `$node` included by `${path.joinToString(" → ")}`"
    }
    return when (usedChildren.size) {
        0 -> "$prefix is not used."
        1 -> "$prefix is not used but its child `${usedChildren.single()}` is used."
        else -> "$prefix is not used but its children ${usedChildren.joinToString { "`$it`" }} are used."
    }
}

private fun findUsedChildren(
    used: Set,
    node: TreeNode,
): List> {
    return node.children.flatMap {
        if (used.contains(it.value)) {
            listOf(it)
        } else {
            findUsedChildren(used, it)
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy