diff --git a/README.md b/README.md index 0a39acd..4d3f219 100644 --- a/README.md +++ b/README.md @@ -20,3 +20,5 @@ dependencies { ``` If you want to configure the rules individually look at the [config.yml](https://github.com/meisterplan/detekt-rules/blob/main/src/main/resources/config/config.yml) and set the same flags in your local detekt config. + +For `CopyOnDataClassWithNonPublicConstructor`, it is required to [run detekt with type resolution](https://detekt.dev/docs/gettingstarted/type-resolution#enabling-on-a-jvm-project). diff --git a/src/main/kotlin/com/meisterplan/detektrules/CopyOnDataClassWithNonPublicConstructor.kt b/src/main/kotlin/com/meisterplan/detektrules/CopyOnDataClassWithNonPublicConstructor.kt index 7e47829..eb74893 100644 --- a/src/main/kotlin/com/meisterplan/detektrules/CopyOnDataClassWithNonPublicConstructor.kt +++ b/src/main/kotlin/com/meisterplan/detektrules/CopyOnDataClassWithNonPublicConstructor.kt @@ -7,16 +7,18 @@ import io.gitlab.arturbosch.detekt.api.Entity import io.gitlab.arturbosch.detekt.api.Issue import io.gitlab.arturbosch.detekt.api.Rule import io.gitlab.arturbosch.detekt.api.Severity +import io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution import io.gitlab.arturbosch.detekt.rules.fqNameOrNull import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.psi.KtCallExpression -import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtNameReferenceExpression import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassMemberScope +@RequiresTypeResolution class CopyOnDataClassWithNonPublicConstructor(config: Config) : Rule(config) { + override val issue = Issue( javaClass.simpleName, Severity.CodeSmell, @@ -37,9 +39,14 @@ class CopyOnDataClassWithNonPublicConstructor(config: Config) : Rule(config) { val typeMemberScope = type?.memberScope if (typeMemberScope is LazyClassMemberScope && typeMemberScope.getPrimaryConstructor()?.visibility != DescriptorVisibilities.PUBLIC - ) - report(CodeSmell(issue, Entity.from(expression), "Non-public constructed data class ${calleeExpression.getReceiverExpression()?.text ?: ""}" - + " of type ${type.fqNameOrNull()} should not bypass constructor by calling copy().")) + ) { + report( + CodeSmell( + issue, Entity.from(expression), "Non-public constructed data class ${calleeExpression.getReceiverExpression()?.text ?: ""}" + + " of type ${type.fqNameOrNull()} should not bypass constructor by calling copy()." + ) + ) + } } } diff --git a/src/test/kotlin/com/meisterplan/detektrules/CopyOnDataClassWithNonPublicConstructorTest.kt b/src/test/kotlin/com/meisterplan/detektrules/CopyOnDataClassWithNonPublicConstructorTest.kt index 4b350a7..cf77a59 100644 --- a/src/test/kotlin/com/meisterplan/detektrules/CopyOnDataClassWithNonPublicConstructorTest.kt +++ b/src/test/kotlin/com/meisterplan/detektrules/CopyOnDataClassWithNonPublicConstructorTest.kt @@ -14,9 +14,13 @@ internal class CopyOnDataClassWithNonPublicConstructorTest(private val env: Kotl fun `reports private data copy`() { val code = """ @Suppress("DataClassPrivateConstructor") - data class A private constructor(val i: Int) - - val x = A(3) + data class A private constructor(val i: Int) { + companion object { + fun create(i: Int) = A(i) + } + } + + val x = A.create(3) val y = x.copy(i = 4) """ val findings = CopyOnDataClassWithNonPublicConstructor(Config.empty).compileAndLintWithContext(env, code)