diff --git a/detekt-core/src/main/resources/default-detekt-config.yml b/detekt-core/src/main/resources/default-detekt-config.yml index 9879910ba86..5b5c7d3fd1d 100644 --- a/detekt-core/src/main/resources/default-detekt-config.yml +++ b/detekt-core/src/main/resources/default-detekt-config.yml @@ -675,6 +675,7 @@ style: active: true VarCouldBeVal: active: true + ignoreLateinitVar: false WildcardImport: active: true excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/VarCouldBeVal.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/VarCouldBeVal.kt index e602d19640f..6a98d79a3c5 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/VarCouldBeVal.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/VarCouldBeVal.kt @@ -8,8 +8,11 @@ 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.config import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault +import io.gitlab.arturbosch.detekt.api.internal.Configuration import io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution +import io.gitlab.arturbosch.detekt.rules.isLateinit import io.gitlab.arturbosch.detekt.rules.isOverride import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.lexer.KtTokens @@ -68,10 +71,13 @@ class VarCouldBeVal(config: Config = Config.empty) : Rule(config) { Debt.FIVE_MINS ) + @Configuration("Whether to ignore uninitialized lateinit vars") + private val ignoreLateinitVar: Boolean by config(defaultValue = false) + override fun visitKtFile(file: KtFile) { super.visitKtFile(file) if (bindingContext == BindingContext.EMPTY) return - val assignmentVisitor = AssignmentVisitor(bindingContext) + val assignmentVisitor = AssignmentVisitor(bindingContext, ignoreLateinitVar) file.accept(assignmentVisitor) assignmentVisitor.getNonReAssignedDeclarations().forEach { @@ -80,7 +86,10 @@ class VarCouldBeVal(config: Config = Config.empty) : Rule(config) { } @Suppress("TooManyFunctions") - private class AssignmentVisitor(private val bindingContext: BindingContext) : DetektVisitor() { + private class AssignmentVisitor( + private val bindingContext: BindingContext, + private val ignoreLateinitVar: Boolean + ) : DetektVisitor() { private val declarationCandidates = mutableSetOf() private val assignments = mutableMapOf>() @@ -197,7 +206,7 @@ class VarCouldBeVal(config: Config = Config.empty) : Rule(config) { private fun KtProperty.isDeclarationCandidate(): Boolean { return when { - !isVar || isOverride() -> false + !isVar || isOverride() || (ignoreLateinitVar && isLateinit()) -> false isLocal || isPrivate() -> true else -> { // Check for whether property belongs to an anonymous object diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/VarCouldBeValSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/VarCouldBeValSpec.kt index 8982aab7f03..17e89eb4680 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/VarCouldBeValSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/VarCouldBeValSpec.kt @@ -1,6 +1,7 @@ package io.gitlab.arturbosch.detekt.rules.style import io.gitlab.arturbosch.detekt.rules.KotlinCoreEnvironmentTest +import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.compileAndLint import io.gitlab.arturbosch.detekt.test.compileAndLintWithContext import org.assertj.core.api.Assertions.assertThat @@ -491,4 +492,24 @@ class VarCouldBeValSpec(val env: KotlinCoreEnvironment) { } } } + + @Nested + inner class `lateinit vars - #4731` { + val code = """ + public class A { + private lateinit var test: String + } + """.trimIndent() + + @Test + fun `reports uninitialized lateinit vars by default`() { + assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) + } + + @Test + fun `does not report uninitialized lateinit vars if disabled in config`() { + val subject = VarCouldBeVal(TestConfig("ignoreLateinitVar" to true)) + assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() + } + } }