diff --git a/compose-lint-checks/src/main/java/slack/lint/compose/ViewModelInjectionDetector.kt b/compose-lint-checks/src/main/java/slack/lint/compose/ViewModelInjectionDetector.kt index 0c8eb6dd..254bc7d8 100644 --- a/compose-lint-checks/src/main/java/slack/lint/compose/ViewModelInjectionDetector.kt +++ b/compose-lint-checks/src/main/java/slack/lint/compose/ViewModelInjectionDetector.kt @@ -8,15 +8,28 @@ import com.android.tools.lint.detector.api.Issue import com.android.tools.lint.detector.api.JavaContext 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 org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.KtProperty import slack.lint.compose.util.* import slack.lint.compose.util.sourceImplementation -class ViewModelInjectionDetector : ComposableFunctionDetector(), SourceCodeScanner { +class ViewModelInjectionDetector +@JvmOverloads +constructor(private val userFactories: StringSetLintOption = StringSetLintOption(USER_FACTORIES)) : + ComposableFunctionDetector(userFactories), SourceCodeScanner { companion object { + + internal val USER_FACTORIES = + StringOption( + "viewmodel-factories", + "A comma-separated list of viewModel factories.", + null, + "This property should define comma-separated list of allowed viewModel factory function names." + ) + private fun errorMessage(factoryName: String): String = """ Implicit dependencies of composables should be made explicit. @@ -27,14 +40,15 @@ class ViewModelInjectionDetector : ComposableFunctionDetector(), SourceCodeScann val ISSUE = Issue.create( - id = "ComposeViewModelInjection", - briefDescription = "Implicit dependencies of composables should be made explicit", - explanation = "Replaced when reporting", - category = Category.CORRECTNESS, - priority = Priorities.NORMAL, - severity = Severity.ERROR, - implementation = sourceImplementation() - ) + id = "ComposeViewModelInjection", + briefDescription = "Implicit dependencies of composables should be made explicit", + explanation = "Replaced when reporting", + category = Category.CORRECTNESS, + priority = Priorities.NORMAL, + severity = Severity.ERROR, + implementation = sourceImplementation() + ) + .setOptions(listOf(USER_FACTORIES)) private val KnownViewModelFactories by lazy { setOf( @@ -51,13 +65,14 @@ class ViewModelInjectionDetector : ComposableFunctionDetector(), SourceCodeScann if (function.isOverride || function.definedInInterface) return val bodyBlock = function.bodyBlockExpression ?: return + val allFactoryNames = KnownViewModelFactories + userFactories.value bodyBlock .findChildrenByClass() .flatMap { property -> property .findDirectChildrenByClass() - .filter { KnownViewModelFactories.contains(it.calleeExpression?.text) } + .filter { it.calleeExpression?.text in allFactoryNames } .map { property to it.calleeExpression!!.text } } .forEach { (property, viewModelFactoryName) -> diff --git a/compose-lint-checks/src/test/java/slack/lint/compose/ViewModelInjectionDetectorTest.kt b/compose-lint-checks/src/test/java/slack/lint/compose/ViewModelInjectionDetectorTest.kt index 2dae692f..10fccc38 100644 --- a/compose-lint-checks/src/test/java/slack/lint/compose/ViewModelInjectionDetectorTest.kt +++ b/compose-lint-checks/src/test/java/slack/lint/compose/ViewModelInjectionDetectorTest.kt @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 package slack.lint.compose +import com.android.tools.lint.checks.infrastructure.TestLintTask import com.android.tools.lint.checks.infrastructure.TestMode import com.android.tools.lint.detector.api.Detector import com.android.tools.lint.detector.api.Issue @@ -23,7 +24,8 @@ class ViewModelInjectionDetectorTest(private val viewModel: String) : BaseSlackL arrayOf("weaverViewModel"), arrayOf("hiltViewModel"), arrayOf("injectedViewModel"), - arrayOf("mavericksViewModel") + arrayOf("mavericksViewModel"), + arrayOf("tangleViewModel"), ) } } @@ -34,6 +36,11 @@ class ViewModelInjectionDetectorTest(private val viewModel: String) : BaseSlackL // This mode is irrelevant to our test and totally untestable with stringy outputs override val skipTestModes: Array = arrayOf(TestMode.SUPPRESSIBLE, TestMode.TYPE_ALIAS) + override fun lint(): TestLintTask { + return super.lint() + .configureOption(ViewModelInjectionDetector.USER_FACTORIES, "tangleViewModel") + } + @Test fun `passes when a weaverViewModel is used as a default param`() { @Language("kotlin")