Skip to content

Commit

Permalink
Add treatAsLambda to ParamOrder rule configuration (#170)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrmans0n authored Dec 15, 2023
1 parent 93207e0 commit fe388bf
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 11 deletions.
2 changes: 2 additions & 0 deletions docs/detekt.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ Compose:
# allowedComposableFunctionNames: .*Presenter,.*MoleculePresenter
ComposableParamOrder:
active: true
# -- You can optionally have a list of types to be treated as lambdas (e.g. typedefs or fun interfaces not picked up automatically)
# treatAsLambda: MyLambdaType
CompositionLocalAllowlist:
active: true
# -- You can optionally define a list of CompositionLocals that are allowed here
Expand Down
9 changes: 9 additions & 0 deletions docs/ktlint.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,15 @@ Most of the modifier-related rules will look for modifiers based their type: eit
compose_custom_modifiers = BananaModifier,PotatoModifier
```

### Configure types to treat as lambdas in ParamOrder check

The `param-order-check` rule will do its best to identify trailing lambdas. However, in cases where a typedef / functional interface is being used, we might want to have this rule to treat them as if they were lambdas: not reporting them if they are the last in a method signature and they don't have a default value. To give ktlint some hints, you can configure this in your `.editorconfig` file:

```editorconfig
[*.{kt,kts}]
compose_treat_as_lambda = MyLambdaType,MyOtherLambdaType
```

## Disabling a specific rule

To disable a rule you have to follow the [instructions from the ktlint documentation](https://github.com/pinterest/ktlint#how-do-i-suppress-an-errors-for-a-lineblockfile), and use the id of the rule you want to disable with the `compose` tag.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtFunctionType
import org.jetbrains.kotlin.psi.KtNullableType
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtTypeElement
import org.jetbrains.kotlin.psi.KtUserType

class ParameterOrder : ComposeKtVisitor {

Expand All @@ -31,7 +33,7 @@ class ParameterOrder : ComposeKtVisitor {
val currentOrder = function.valueParameters

// We look in the original params without defaults and see if the last one is a function.
val hasTrailingFunction = function.hasTrailingFunction
val hasTrailingFunction = with(config) { function.hasTrailingFunction }
val trailingLambda = if (hasTrailingFunction) {
listOf(function.valueParameters.last())
} else {
Expand Down Expand Up @@ -59,13 +61,18 @@ class ParameterOrder : ComposeKtVisitor {
}
}

context(ComposeKtConfig)
private val KtFunction.hasTrailingFunction: Boolean
get() =
when (val outerType = valueParameters.lastOrNull()?.typeReference?.typeElement) {
is KtFunctionType -> true
is KtNullableType -> outerType.innerType is KtFunctionType
else -> false
}
get() = valueParameters.lastOrNull()?.typeReference?.typeElement?.isLambda() == true

context(ComposeKtConfig)
private fun KtTypeElement.isLambda(treatAsLambda: Set<String> = getSet("treatAsLambda", emptySet())): Boolean =
when (this) {
is KtFunctionType -> true
is KtNullableType -> innerType?.isLambda(treatAsLambda) == true
is KtUserType -> getReferencedName() in getSet("treatAsLambda", emptySet())
else -> false
}

companion object {
fun createErrorMessage(currentOrder: List<KtParameter>, properOrder: List<KtParameter>): String =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@
// 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.TestConfig
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 ParameterOrderCheckTest {

private val rule = ParameterOrderCheck(Config.empty)
private val testConfig = TestConfig(
"treatAsLambda" to listOf("LambdaType"),
)
private val rule = ParameterOrderCheck(testConfig)

@Test
fun `no errors when ordering is correct`() {
Expand All @@ -28,6 +31,12 @@ class ParameterOrderCheckTest {
@Composable
fun MyComposable(text1: String, modifier: Modifier = Modifier, m2: Modifier = Modifier, trailing: () -> Unit) { }
@Composable
fun MyComposable(text1: String, modifier: Modifier = Modifier, m2: Modifier = Modifier, trailing: LambdaType) { }
@Composable
fun MyComposable(text1: String, modifier: Modifier = Modifier, m2: Modifier = Modifier, trailing: LambdaType?) { }
@Composable
fun MyComposable(text1: String, modifier: Modifier = Modifier, m2: Modifier = Modifier, trailing: (() -> Unit)?) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,22 @@ val customModifiers: EditorConfigProperty<String> =
}
},
)

val treatAsLambda: EditorConfigProperty<String> =
EditorConfigProperty(
type = PropertyType.LowerCasingPropertyType(
"compose_treat_as_lambda",
"A comma separated list of types that should be treated as lambdas " +
"(e.g. typedefs of lambdas, fun interfaces)",
PropertyValueParser.IDENTITY_VALUE_PARSER,
emptySet(),
),
defaultValue = "",
propertyMapper = { property, _ ->
when {
property?.isUnset == true -> ""
property?.getValueAs<String>() != null -> property.getValueAs<String>()
else -> property?.getValueAs()
}
},
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@ import io.nlopez.rules.core.ComposeKtVisitor
import io.nlopez.rules.core.ktlint.KtlintRule

class ParameterOrderCheck :
KtlintRule("compose:param-order-check"),
KtlintRule(
id = "compose:param-order-check",
editorConfigProperties = setOf(treatAsLambda),
),
ComposeKtVisitor by ParameterOrder()
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,21 @@ class ParameterOrderCheckTest {
@Composable
fun MyComposable(text1: String, modifier: Modifier = Modifier, m2: Modifier = Modifier, trailing: () -> Unit) { }
@Composable
fun MyComposable(text1: String, modifier: Modifier = Modifier, m2: Modifier = Modifier, trailing: LambdaType) { }
@Composable
fun MyComposable(text1: String, modifier: Modifier = Modifier, m2: Modifier = Modifier, trailing: LambdaType?) { }
@Composable
fun MyComposable(text1: String, modifier: Modifier = Modifier, m2: Modifier = Modifier, trailing: (() -> Unit)?) { }
@Composable
fun MyComposable(modifier: Modifier, text1: String, m2: Modifier = Modifier, trailing: (() -> Unit)?) { }
""".trimIndent()
orderingRuleAssertThat(code).hasNoLintViolations()
orderingRuleAssertThat(code)
.withEditorConfigOverride(treatAsLambda to "LambdaType")
.hasNoLintViolations()
}

@Test
Expand Down

0 comments on commit fe388bf

Please sign in to comment.