Skip to content

Commit

Permalink
Update ViewModelInjectionDetector to support a lint option (#53)
Browse files Browse the repository at this point in the history
* Update ViewModelInjectionDetector to support a lint option

* Update compose-lint-checks/src/main/java/slack/lint/compose/ViewModelInjectionDetector.kt

Co-authored-by: Zac Sweers <pandanomic@gmail.com>

* Simplify filtering allowed and known ViewModel factory functions

* Avoid unnecessarily creating a Set on each property checked

* Use more accurate name for user added viewModel factory list

---------

Co-authored-by: Zac Sweers <pandanomic@gmail.com>
  • Loading branch information
WhosNickDoglio and ZacSweers authored Feb 18, 2023
1 parent 4073fe0 commit 00b3562
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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<ViewModelInjectionDetector>()
)
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<ViewModelInjectionDetector>()
)
.setOptions(listOf(USER_FACTORIES))

private val KnownViewModelFactories by lazy {
setOf(
Expand All @@ -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<KtProperty>()
.flatMap { property ->
property
.findDirectChildrenByClass<KtCallExpression>()
.filter { KnownViewModelFactories.contains(it.calleeExpression?.text) }
.filter { it.calleeExpression?.text in allFactoryNames }
.map { property to it.calleeExpression!!.text }
}
.forEach { (property, viewModelFactoryName) ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -23,7 +24,8 @@ class ViewModelInjectionDetectorTest(private val viewModel: String) : BaseSlackL
arrayOf("weaverViewModel"),
arrayOf("hiltViewModel"),
arrayOf("injectedViewModel"),
arrayOf("mavericksViewModel")
arrayOf("mavericksViewModel"),
arrayOf("tangleViewModel"),
)
}
}
Expand All @@ -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<TestMode> = 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")
Expand Down

0 comments on commit 00b3562

Please sign in to comment.