From 975ea18236d39bd4f397fdbf8e7da382e84c2f28 Mon Sep 17 00:00:00 2001 From: Brais Date: Wed, 3 Aug 2022 17:55:06 +0200 Subject: [PATCH] Use reflection to disable the rules that require type solving when the BindingContext is empty --- .../api/internal/RequiresTypeResolution.kt | 2 +- detekt-core/build.gradle.kts | 1 + .../gitlab/arturbosch/detekt/core/Analyzer.kt | 9 ++++ .../arturbosch/detekt/core/AnalyzerSpec.kt | 44 +++++++++++++++++-- .../configs/config-value-type-correct.yml | 3 ++ gradle/libs.versions.toml | 3 +- 6 files changed, 57 insertions(+), 5 deletions(-) diff --git a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/internal/RequiresTypeResolution.kt b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/internal/RequiresTypeResolution.kt index 7edb1ab5d639..ba5037c4dfee 100644 --- a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/internal/RequiresTypeResolution.kt +++ b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/internal/RequiresTypeResolution.kt @@ -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 diff --git a/detekt-core/build.gradle.kts b/detekt-core/build.gradle.kts index 19e1355f421d..3756b7050bc0 100644 --- a/detekt-core/build.gradle.kts +++ b/detekt-core/build.gradle.kts @@ -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) diff --git a/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/Analyzer.kt b/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/Analyzer.kt index b5de3d7b35ea..825f34b3deb3 100644 --- a/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/Analyzer.kt +++ b/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/Analyzer.kt @@ -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 @@ -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>> @@ -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() } + } else { + sequence + } + } .partition { isCorrectable(it) } val result = HashMap>() diff --git a/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/AnalyzerSpec.kt b/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/AnalyzerSpec.kt index abc5b8668d06..184736c7293d 100644 --- a/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/AnalyzerSpec.kt +++ b/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/AnalyzerSpec.kt @@ -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()` { @@ -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") @@ -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) { @@ -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))) @@ -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() + } } } } diff --git a/detekt-core/src/test/resources/configs/config-value-type-correct.yml b/detekt-core/src/test/resources/configs/config-value-type-correct.yml index b512e9f819df..a31c24752d8d 100644 --- a/detekt-core/src/test/resources/configs/config-value-type-correct.yml +++ b/detekt-core/src/test/resources/configs/config-value-type-correct.yml @@ -2,6 +2,9 @@ style: MaxLineLength: active: true maxLineLength: 120 + RequiresTypeResolutionMaxLineLength: + active: true + maxLineLength: 120 FaultyRule: active: true FaultyRuleNoStackTrace: diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0f5e8328a2b0..b5f7f1a0b318 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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" @@ -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" }