forked from twitter/compose-rules
-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add LambdaParameterEventTrailing rule
- Loading branch information
Showing
9 changed files
with
218 additions
and
7 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
51 changes: 51 additions & 0 deletions
51
rules/common/src/main/kotlin/io/nlopez/compose/rules/LambdaParameterEventTrailing.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,51 @@ | ||
// Copyright 2024 Nacho Lopez | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package io.nlopez.compose.rules | ||
|
||
import io.nlopez.compose.core.ComposeKtConfig | ||
import io.nlopez.compose.core.ComposeKtVisitor | ||
import io.nlopez.compose.core.Emitter | ||
import io.nlopez.compose.core.report | ||
import io.nlopez.compose.core.util.emitsContent | ||
import io.nlopez.compose.core.util.isComposable | ||
import io.nlopez.compose.core.util.isLambda | ||
import io.nlopez.compose.core.util.modifierParameter | ||
import org.jetbrains.kotlin.psi.KtFunction | ||
|
||
class LambdaParameterEventTrailing : ComposeKtVisitor { | ||
override fun visitComposable(function: KtFunction, emitter: Emitter, config: ComposeKtConfig) { | ||
// We want this rule to only run for functions that emit content | ||
if (!function.emitsContent(config)) return | ||
|
||
// We'd also want for it to have a modifier to apply the rule, which serves two purposes: | ||
// - making sure that it's the separation between required and optional parameters | ||
// - the lambda would be able to be moved before the modifier and not be the trailing one | ||
if (function.modifierParameter(config) == null) return | ||
|
||
val trailingParam = function.valueParameters.lastOrNull() ?: return | ||
|
||
// Check if the trailing element... | ||
// - is a lambda | ||
// - is not @Composable | ||
// - doesn't have a default value | ||
// - starts with "on", meaning it's an event | ||
val typeReference = trailingParam.typeReference ?: return | ||
if (!typeReference.isLambda()) return | ||
if (typeReference.isComposable) return | ||
if (trailingParam.hasDefaultValue()) return | ||
val name = trailingParam.name ?: return | ||
if (!name.startsWith("on")) return | ||
|
||
emitter.report(trailingParam, EventLambdaIsTrailingLambda) | ||
} | ||
|
||
companion object { | ||
val EventLambdaIsTrailingLambda = """ | ||
Lambda parameters in a @Composable that are for events (e.g. onClick, onChange, etc) and are required (they don't have a default value) should not be used as the trailing parameter. | ||
Composable functions that emit content usually reserve the trailing lambda syntax for the content slot, and that can lead to an assumption that other composables can be used in that lambda. | ||
See https://mrmans0n.github.io/compose-rules/rules/#TODO for more information. | ||
""".trimIndent() | ||
} | ||
} |
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
23 changes: 23 additions & 0 deletions
23
...etekt/src/main/kotlin/io/nlopez/compose/rules/detekt/LambdaParameterEventTrailingCheck.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,23 @@ | ||
// Copyright 2024 Nacho Lopez | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package io.nlopez.compose.rules.detekt | ||
|
||
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 | ||
import io.nlopez.compose.core.ComposeKtVisitor | ||
import io.nlopez.compose.rules.DetektRule | ||
import io.nlopez.compose.rules.LambdaParameterEventTrailing | ||
|
||
class LambdaParameterEventTrailingCheck(config: Config) : | ||
DetektRule(config), | ||
ComposeKtVisitor by LambdaParameterEventTrailing() { | ||
|
||
override val issue: Issue = Issue( | ||
id = "LambdaParameterEventTrailing", | ||
severity = Severity.Style, | ||
description = LambdaParameterEventTrailing.EventLambdaIsTrailingLambda, | ||
debt = Debt.FIVE_MINS, | ||
) | ||
} |
76 changes: 76 additions & 0 deletions
76
...t/src/test/kotlin/io/nlopez/compose/rules/detekt/LambdaParameterEventTrailingCheckTest.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,76 @@ | ||
// Copyright 2024 Nacho Lopez | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package io.nlopez.compose.rules.detekt | ||
|
||
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 io.nlopez.compose.rules.LambdaParameterEventTrailing | ||
import org.intellij.lang.annotations.Language | ||
import org.junit.jupiter.api.Test | ||
|
||
class LambdaParameterEventTrailingCheckTest { | ||
|
||
private val rule = LambdaParameterEventTrailingCheck(Config.empty) | ||
|
||
@Test | ||
fun `error out when detecting a lambda being as trailing`() { | ||
@Language("kotlin") | ||
val code = | ||
""" | ||
@Composable | ||
fun Something(modifier: Modifier = Modifier, onClick: () -> Unit) { | ||
Text("Hello") | ||
} | ||
""".trimIndent() | ||
val errors = rule.lint(code) | ||
assertThat(errors) | ||
.hasStartSourceLocations( | ||
SourceLocation(2, 46), | ||
) | ||
for (error in errors) { | ||
assertThat(error).hasMessage(LambdaParameterEventTrailing.EventLambdaIsTrailingLambda) | ||
} | ||
} | ||
|
||
@Test | ||
fun `passes when a lambda is required`() { | ||
@Language("kotlin") | ||
val code = | ||
""" | ||
@Composable | ||
fun Something(onClick: () -> Unit, modifier: Modifier = Modifier) { | ||
Text("Hello") | ||
} | ||
""".trimIndent() | ||
val errors = rule.lint(code) | ||
assertThat(errors).isEmpty() | ||
} | ||
|
||
@Test | ||
fun `passes when a lambda is composable`() { | ||
@Language("kotlin") | ||
val code = | ||
""" | ||
@Composable | ||
fun Something(modifier: Modifier = Modifier, on: @Composable () -> Unit) { | ||
Text("Hello") | ||
} | ||
""".trimIndent() | ||
val errors = rule.lint(code) | ||
assertThat(errors).isEmpty() | ||
} | ||
|
||
@Test | ||
fun `passes when the function doesnt emit content`() { | ||
@Language("kotlin") | ||
val code = | ||
""" | ||
@Composable | ||
fun something(onClick: () -> Unit) {} | ||
""".trimIndent() | ||
val errors = rule.lint(code) | ||
assertThat(errors).isEmpty() | ||
} | ||
} |
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
14 changes: 14 additions & 0 deletions
14
...tlint/src/main/kotlin/io/nlopez/compose/rules/ktlint/LambdaParameterEventTrailingCheck.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,14 @@ | ||
// Copyright 2024 Nacho Lopez | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package io.nlopez.compose.rules.ktlint | ||
|
||
import io.nlopez.compose.core.ComposeKtVisitor | ||
import io.nlopez.compose.rules.KtlintRule | ||
import io.nlopez.compose.rules.LambdaParameterEventTrailing | ||
|
||
class LambdaParameterEventTrailingCheck : | ||
KtlintRule( | ||
id = "compose:lambda-param-event-trailing", | ||
editorConfigProperties = setOf(contentEmittersProperty, contentEmittersDenylist) | ||
), | ||
ComposeKtVisitor by LambdaParameterEventTrailing() |