generated from detekt/detekt-custom-rule-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6096461
commit 54710a8
Showing
4 changed files
with
119 additions
and
0 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
86 changes: 86 additions & 0 deletions
86
src/main/kotlin/com/github/ivy/explicit/rule/DataClassTypedIDsRule.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,86 @@ | ||
package com.github.ivy.explicit.rule | ||
|
||
import io.gitlab.arturbosch.detekt.api.* | ||
import io.gitlab.arturbosch.detekt.rules.isOverride | ||
import org.jetbrains.kotlin.psi.KtClass | ||
import org.jetbrains.kotlin.psi.KtParameter | ||
|
||
class DataClassTypedIDsRule(config: Config) : Rule(config) { | ||
companion object { | ||
private val ExcludedClassNameEndings by lazy { | ||
setOf("Dto", "Entity") | ||
} | ||
|
||
private val ExcludedAnnotations by lazy { | ||
setOf("Entity", "Serializable") | ||
} | ||
|
||
private val IdFieldEndings by lazy { | ||
setOf("Id", "ID") | ||
} | ||
} | ||
|
||
override val issue = Issue( | ||
id = "DataClassTypedIDs", | ||
severity = Severity.Maintainability, | ||
description = "Domain data models should use type-safe `value class` ids. " + | ||
"Typed-IDs provide compile-time safety and prevent mixing IDs of different entities.", | ||
debt = Debt.TWENTY_MINS | ||
) | ||
|
||
override fun visitClass(klass: KtClass) { | ||
super.visitClass(klass) | ||
if (klass.isData() && !klass.isIgnoredClass()) { | ||
klass.getPrimaryConstructorParameterList() | ||
?.parameters | ||
?.filter { param -> | ||
!param.isOverride() && param.seemsLikeID() | ||
} | ||
?.forEach { parameter -> | ||
report( | ||
CodeSmell( | ||
issue, | ||
Entity.from(parameter), | ||
message = failureMessage(klass, parameter) | ||
) | ||
) | ||
} | ||
} | ||
} | ||
|
||
private fun KtClass.isIgnoredClass(): Boolean { | ||
name?.let { klasName -> | ||
val isIgnored = ExcludedClassNameEndings.any { | ||
klasName.endsWith(it, ignoreCase = true) | ||
} | ||
if (isIgnored) return true | ||
} | ||
|
||
return annotationEntries.any { | ||
val annotationName = it.shortName?.asString() | ||
annotationName in ExcludedAnnotations | ||
} | ||
} | ||
|
||
private fun KtParameter.seemsLikeID(): Boolean { | ||
val paramType = typeReference?.text | ||
if (paramType == "UUID") return true | ||
|
||
name?.let { paramName -> | ||
val endsLikeID = IdFieldEndings.any { | ||
paramName.endsWith(it, ignoreCase = false) | ||
} | ||
if (endsLikeID) return true | ||
} | ||
|
||
return false | ||
} | ||
|
||
private fun failureMessage(klass: KtClass, parameter: KtParameter) = buildString { | ||
val paramType = parameter.typeReference?.text | ||
append("Data class '${klass.name}' should use type-safe IDs ") | ||
append("instead of $paramType for property '${parameter.name}'. ") | ||
append("Typed-IDs like `value class SomeId(val id: UUID)` provide ") | ||
append("compile-time safety and prevent mixing IDs of different entities.") | ||
} | ||
} |
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 |
---|---|---|
|
@@ -3,3 +3,5 @@ IvyExplicit: | |
active: true | ||
DataClassDefaultValues: | ||
active: true | ||
DataClassTypedIDs: | ||
active: true |
29 changes: 29 additions & 0 deletions
29
src/test/kotlin/com/github/ivy/explicit/rule/DataClassTypedIDsRuleRuleTest.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,29 @@ | ||
package com.github.ivy.explicit.rule | ||
|
||
import io.gitlab.arturbosch.detekt.api.Config | ||
import io.gitlab.arturbosch.detekt.rules.KotlinCoreEnvironmentTest | ||
import io.gitlab.arturbosch.detekt.test.compileAndLintWithContext | ||
import io.kotest.matchers.collections.shouldHaveSize | ||
import io.kotest.matchers.shouldBe | ||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment | ||
import org.junit.jupiter.api.Test | ||
|
||
@KotlinCoreEnvironmentTest | ||
internal class DataClassTypedIDsRuleRuleTest(private val env: KotlinCoreEnvironment) { | ||
|
||
@Test | ||
fun `reports data class having UUID as id`() { | ||
val code = """ | ||
data class A( | ||
val id: UUID, | ||
val name: String, | ||
) | ||
""" | ||
val findings = DataClassTypedIDsRule(Config.empty).compileAndLintWithContext(env, code) | ||
findings shouldHaveSize 1 | ||
val message = findings.first().message | ||
message shouldBe """ | ||
Data class 'A' should use type-safe IDs instead of UUID for property 'id'. Typed-IDs like `value class SomeId(val id: UUID)` provide compile-time safety and prevent mixing IDs of different entities. | ||
""".trimIndent() | ||
} | ||
} |