-
Notifications
You must be signed in to change notification settings - Fork 94
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add UnstableCollections rule The Compose compiler cannot infer stability for kotlin collection interfaces (List/Set/Map), so any composable with one of those (which is pretty likely) as a parameter will be deemed unstable by the compiler. This rule detects those cases and prompt the user to use the Kotlinx Immutable Collections alternative instead, or an `@Immutable` wrapper. * Remove space
- Loading branch information
Showing
10 changed files
with
239 additions
and
4 deletions.
There are no files selected for viewing
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
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
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
39 changes: 39 additions & 0 deletions
39
rules/common/src/main/kotlin/com/twitter/compose/rules/ComposeUnstableCollections.kt
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// Copyright 2022 Twitter, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package com.twitter.compose.rules | ||
|
||
import com.twitter.rules.core.ComposeKtVisitor | ||
import com.twitter.rules.core.Emitter | ||
import com.twitter.rules.core.report | ||
import com.twitter.rules.core.util.isTypeUnstableCollection | ||
import org.jetbrains.kotlin.psi.KtFunction | ||
import java.util.* | ||
|
||
class ComposeUnstableCollections : ComposeKtVisitor { | ||
|
||
override fun visitComposable(function: KtFunction, autoCorrect: Boolean, emitter: Emitter) { | ||
for (param in function.valueParameters.filter { it.isTypeUnstableCollection }) { | ||
val variableName = param.nameAsSafeName.asString() | ||
val type = param.typeReference?.text ?: "List/Set/Map" | ||
val message = createErrorMessage( | ||
type = type, | ||
rawType = type.replace(DiamondRegex, ""), | ||
variable = variableName | ||
) | ||
emitter.report(param.typeReference ?: param, message) | ||
} | ||
} | ||
|
||
companion object { | ||
private val DiamondRegex by lazy(LazyThreadSafetyMode.NONE) { Regex("<.*>\\??") } | ||
private val String.capitalized: String | ||
get() = replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() } | ||
|
||
fun createErrorMessage(type: String, rawType: String, variable: String) = """ | ||
The Compose Compiler cannot infer the stability of a parameter if a $type is used in it, even if the item type is stable. | ||
You should use Kotlinx Immutable Collections instead: `$variable: Immutable$type` or create an `@Immutable` wrapper for this class: `@Immutable data class ${variable.capitalized}}$rawType(val items: $type)` | ||
See https://twitter.github.io/compose-rules/rules/#avoid-using-unstable-collections for more information. | ||
""".trimIndent() | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
...etekt/src/main/kotlin/com/twitter/compose/rules/detekt/ComposeUnstableCollectionsCheck.kt
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Copyright 2022 Twitter, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package com.twitter.compose.rules.detekt | ||
|
||
import com.twitter.compose.rules.ComposeUnstableCollections | ||
import com.twitter.rules.core.ComposeKtVisitor | ||
import com.twitter.rules.core.detekt.TwitterDetektRule | ||
import io.gitlab.arturbosch.detekt.api.Config | ||
import io.gitlab.arturbosch.detekt.api.Debt | ||
import io.gitlab.arturbosch.detekt.api.Issue | ||
import io.gitlab.arturbosch.detekt.api.Severity | ||
|
||
class ComposeUnstableCollectionsCheck(config: Config) : | ||
TwitterDetektRule(config), | ||
ComposeKtVisitor by ComposeUnstableCollections() { | ||
override val issue: Issue = Issue( | ||
id = "UnstableCollections", | ||
severity = Severity.Defect, | ||
description = """ | ||
The Compose Compiler cannot infer the stability of a parameter if a List/Set/Map is used in it, even if the item type is stable. | ||
You should use Kotlinx Immutable Collections instead, or create an `@Immutable` wrapper for this class. | ||
See https://twitter.github.io/compose-rules/rules/#avoid-using-unstable-collections for more information. | ||
""".trimIndent(), | ||
debt = Debt.TWENTY_MINS | ||
) | ||
} |
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
54 changes: 54 additions & 0 deletions
54
...t/src/test/kotlin/com/twitter/compose/rules/detekt/ComposeUnstableCollectionsCheckTest.kt
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// Copyright 2022 Twitter, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package com.twitter.compose.rules.detekt | ||
|
||
import com.twitter.compose.rules.ComposeUnstableCollections.Companion.createErrorMessage | ||
import io.gitlab.arturbosch.detekt.api.Config | ||
import io.gitlab.arturbosch.detekt.api.SourceLocation | ||
import io.gitlab.arturbosch.detekt.test.assertThat | ||
import io.gitlab.arturbosch.detekt.test.lint | ||
import org.intellij.lang.annotations.Language | ||
import org.junit.jupiter.api.Test | ||
|
||
class ComposeUnstableCollectionsCheckTest { | ||
|
||
private val rule = ComposeUnstableCollectionsCheck(Config.empty) | ||
|
||
@Test | ||
fun `errors when a Composable has a List Set Map parameter`() { | ||
@Language("kotlin") | ||
val code = | ||
""" | ||
@Composable | ||
fun Something(a: List<String>) {} | ||
@Composable | ||
fun Something(a: Set<String>) {} | ||
@Composable | ||
fun Something(a: Map<String, Int>) {} | ||
""".trimIndent() | ||
val errors = rule.lint(code) | ||
assertThat(errors) | ||
.hasSourceLocations( | ||
SourceLocation(2, 18), | ||
SourceLocation(4, 18), | ||
SourceLocation(6, 18) | ||
) | ||
assertThat(errors[0]).hasMessage(createErrorMessage("List<String>", "List", "a")) | ||
assertThat(errors[1]).hasMessage(createErrorMessage("Set<String>", "Set", "a")) | ||
assertThat(errors[2]).hasMessage(createErrorMessage("Map<String, Int>", "Map", "a")) | ||
} | ||
|
||
@Test | ||
fun `no errors when a Composable has valid parameters`() { | ||
@Language("kotlin") | ||
val code = | ||
""" | ||
@Composable | ||
fun Something(a: ImmutableList<String>, b: ImmutableSet<String>, c: ImmutableMap<String, Int>) {} | ||
@Composable | ||
fun Something(a: StringList, b: StringSet, c: StringToIntMap) {} | ||
""".trimIndent() | ||
val errors = rule.lint(code) | ||
assertThat(errors).isEmpty() | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
...tlint/src/main/kotlin/com/twitter/compose/rules/ktlint/ComposeUnstableCollectionsCheck.kt
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// Copyright 2022 Twitter, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package com.twitter.compose.rules.ktlint | ||
|
||
import com.twitter.compose.rules.ComposeUnstableCollections | ||
import com.twitter.rules.core.ComposeKtVisitor | ||
import com.twitter.rules.core.ktlint.TwitterKtlintRule | ||
|
||
class ComposeUnstableCollectionsCheck : | ||
TwitterKtlintRule("twitter-compose:unstable-collections"), | ||
ComposeKtVisitor by ComposeUnstableCollections() |
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
58 changes: 58 additions & 0 deletions
58
...t/src/test/kotlin/com/twitter/compose/rules/ktlint/ComposeUnstableCollectionsCheckTest.kt
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// Copyright 2022 Twitter, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package com.twitter.compose.rules.ktlint | ||
|
||
import com.pinterest.ktlint.test.KtLintAssertThat.Companion.assertThatRule | ||
import com.pinterest.ktlint.test.LintViolation | ||
import com.twitter.compose.rules.ComposeUnstableCollections.Companion.createErrorMessage | ||
import org.intellij.lang.annotations.Language | ||
import org.junit.jupiter.api.Test | ||
|
||
class ComposeUnstableCollectionsCheckTest { | ||
|
||
private val ruleAssertThat = assertThatRule { ComposeUnstableCollectionsCheck() } | ||
|
||
@Test | ||
fun `errors when a Composable has a List Set Map parameter`() { | ||
@Language("kotlin") | ||
val code = | ||
""" | ||
@Composable | ||
fun Something(a: List<String>) {} | ||
@Composable | ||
fun Something(a: Set<String>) {} | ||
@Composable | ||
fun Something(a: Map<String, Int>) {} | ||
""".trimIndent() | ||
ruleAssertThat(code).hasLintViolationsWithoutAutoCorrect( | ||
LintViolation( | ||
line = 2, | ||
col = 18, | ||
detail = createErrorMessage("List<String>", "List", "a") | ||
), | ||
LintViolation( | ||
line = 4, | ||
col = 18, | ||
detail = createErrorMessage("Set<String>", "Set", "a") | ||
), | ||
LintViolation( | ||
line = 6, | ||
col = 18, | ||
detail = createErrorMessage("Map<String, Int>", "Map", "a") | ||
) | ||
) | ||
} | ||
|
||
@Test | ||
fun `no errors when a Composable has valid parameters`() { | ||
@Language("kotlin") | ||
val code = | ||
""" | ||
@Composable | ||
fun Something(a: ImmutableList<String>, b: ImmutableSet<String>, c: ImmutableMap<String, Int>) {} | ||
@Composable | ||
fun Something(a: StringList, b: StringSet, c: StringToIntMap) {} | ||
""".trimIndent() | ||
ruleAssertThat(code).hasNoLintViolations() | ||
} | ||
} |