Skip to content

Commit

Permalink
Use reflection to disable the rules that require type solving when th…
Browse files Browse the repository at this point in the history
…e BindingContext is empty
  • Loading branch information
BraisGabin committed Aug 3, 2022
1 parent b5f2981 commit 975ea18
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 5 deletions.
Expand Up @@ -4,5 +4,5 @@ package io.gitlab.arturbosch.detekt.api.internal
* Annotated [io.gitlab.arturbosch.detekt.api.Rule] requires type resolution to work.
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
@Retention(AnnotationRetention.RUNTIME)
annotation class RequiresTypeResolution
1 change: 1 addition & 0 deletions detekt-core/build.gradle.kts
Expand Up @@ -7,6 +7,7 @@ dependencies {
api(projects.detektParser)
api(projects.detektTooling)
implementation(libs.snakeyaml)
implementation(libs.kotlin.reflection)
implementation(projects.detektMetrics)
implementation(projects.detektPsiUtils)
implementation(projects.detektReportHtml)
Expand Down
Expand Up @@ -11,6 +11,7 @@ import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.RuleSetId
import io.gitlab.arturbosch.detekt.api.RuleSetProvider
import io.gitlab.arturbosch.detekt.api.internal.CompilerResources
import io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution
import io.gitlab.arturbosch.detekt.api.internal.whichDetekt
import io.gitlab.arturbosch.detekt.api.internal.whichJava
import io.gitlab.arturbosch.detekt.api.internal.whichOS
Expand All @@ -25,6 +26,7 @@ import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactoryImpl
import kotlin.reflect.full.hasAnnotation

private typealias FindingsResult = List<Map<RuleSetId, List<Finding>>>

Expand Down Expand Up @@ -113,6 +115,13 @@ internal class Analyzer(

val (correctableRules, otherRules) = activeRuleSetsToRuleSetConfigs
.flatMap { (ruleSet, _) -> ruleSet.rules.asSequence() }
.let { sequence ->
if (bindingContext == BindingContext.EMPTY) {
sequence.filterNot { rule -> rule::class.hasAnnotation<RequiresTypeResolution>() }
} else {
sequence
}
}
.partition { isCorrectable(it) }

val result = HashMap<RuleSetId, MutableList<Finding>>()
Expand Down
Expand Up @@ -10,15 +10,20 @@ import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.RuleSet
import io.gitlab.arturbosch.detekt.api.RuleSetProvider
import io.gitlab.arturbosch.detekt.api.Severity
import io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution
import io.gitlab.arturbosch.detekt.rules.KotlinCoreEnvironmentTest
import io.gitlab.arturbosch.detekt.test.getContextForPaths
import io.gitlab.arturbosch.detekt.test.yamlConfig
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.psi.KtFile
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import java.util.concurrent.CompletionException

class AnalyzerSpec {
@KotlinCoreEnvironmentTest
class AnalyzerSpec(val env: KotlinCoreEnvironment) {

@Nested
inner class `exceptions during analyze()` {
Expand Down Expand Up @@ -75,6 +80,17 @@ class AnalyzerSpec {
assertThat(settings.use { analyzer.run(listOf(compileForTest(testFile))) }.values.flatten()).hasSize(1)
}

@Test
fun `with findings and context binding`() {
val testFile = path.resolve("Test.kt")
val settings = createProcessingSettings(testFile, yamlConfig("configs/config-value-type-correct.yml"))
val analyzer = Analyzer(settings, listOf(StyleRuleSetProvider(30)), emptyList())
val ktFile = compileForTest(testFile)
val bindingContext = env.getContextForPaths(listOf(ktFile))

assertThat(settings.use { analyzer.run(listOf(ktFile), bindingContext) }.values.flatten()).hasSize(2)
}

@Test
fun `with findings but ignored`() {
val testFile = path.resolve("Test.kt")
Expand Down Expand Up @@ -119,7 +135,13 @@ class AnalyzerSpec {

private class StyleRuleSetProvider(private val threshold: Int? = null) : RuleSetProvider {
override val ruleSetId: String = "style"
override fun instance(config: Config) = RuleSet(ruleSetId, listOf(MaxLineLength(config, threshold)))
override fun instance(config: Config) = RuleSet(
ruleSetId,
listOf(
MaxLineLength(config, threshold),
RequiresTypeResolutionMaxLineLength(config, threshold),
)
)
}

private class MaxLineLength(config: Config, threshold: Int?) : Rule(config) {
Expand All @@ -135,6 +157,20 @@ private class MaxLineLength(config: Config, threshold: Int?) : Rule(config) {
}
}

@RequiresTypeResolution
private class RequiresTypeResolutionMaxLineLength(config: Config, threshold: Int?) : Rule(config) {
override val issue = Issue(this::class.java.simpleName, Severity.Style, "", Debt.FIVE_MINS)
private val lengthThreshold: Int = threshold ?: valueOrDefault("maxLineLength", 100)
override fun visitKtFile(file: KtFile) {
super.visitKtFile(file)
for (line in file.text.lineSequence()) {
if (line.length > lengthThreshold) {
report(CodeSmell(issue, Entity.atPackageOrFirstDecl(file), issue.description))
}
}
}
}

private class FaultyRuleSetProvider : RuleSetProvider {
override val ruleSetId: String = "style"
override fun instance(config: Config) = RuleSet(ruleSetId, listOf(FaultyRule(config)))
Expand All @@ -156,7 +192,9 @@ private class FaultyRuleNoStackTrace(config: Config) : Rule(config) {
override val issue = Issue(this::class.java.simpleName, Severity.Style, "", Debt.FIVE_MINS)
override fun visitKtFile(file: KtFile) {
throw object : IllegalStateException("Deliberately triggered error without stack trace.") {
init { stackTrace = emptyArray() }
init {
stackTrace = emptyArray()
}
}
}
}
Expand Up @@ -2,6 +2,9 @@ style:
MaxLineLength:
active: true
maxLineLength: 120
RequiresTypeResolutionMaxLineLength:
active: true
maxLineLength: 120
FaultyRule:
active: true
FaultyRuleNoStackTrace:
Expand Down
3 changes: 2 additions & 1 deletion gradle/libs.versions.toml
Expand Up @@ -17,6 +17,7 @@ kotlin-gradlePluginApi = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin-a
kotlin-scriptUtil = { module = "org.jetbrains.kotlin:kotlin-script-util", version.ref = "kotlin" }
kotlin-scriptingCompilerEmbeddable = { module = "org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable", version.ref = "kotlin" }
kotlin-stdlibJdk8 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
kotlin-reflection = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }

kotlinx-html = "org.jetbrains.kotlinx:kotlinx-html-jvm:0.8.0"
kotlinx-coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4"
Expand All @@ -26,7 +27,7 @@ android-gradle = "com.android.tools.build:gradle:7.2.1"
ktlint-core = { module = "com.pinterest.ktlint:ktlint-core", version.ref = "ktlint" }
ktlint-rulesetStandard = { module = "com.pinterest.ktlint:ktlint-ruleset-standard", version.ref = "ktlint" }
ktlint-rulesetExperimental = { module = "com.pinterest.ktlint:ktlint-ruleset-experimental", version.ref = "ktlint" }
slf4j-nop = { module = "org.slf4j:slf4j-nop", version = "1.7.36" }
slf4j-nop = { module = "org.slf4j:slf4j-nop", version = "1.7.36" }

spek-dsl = { module = "org.spekframework.spek2:spek-dsl-jvm", version = "2.0.18" }

Expand Down

0 comments on commit 975ea18

Please sign in to comment.