Skip to content

Commit

Permalink
Merge remote-tracking branch 'detekt/main' into chao/kt47
Browse files Browse the repository at this point in the history
  • Loading branch information
chao2zhang committed Oct 7, 2022
2 parents 60791ad + 7027c27 commit 68d4159
Show file tree
Hide file tree
Showing 34 changed files with 870 additions and 207 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/stale.yaml
Expand Up @@ -10,7 +10,7 @@ jobs:
issues: write
pull-requests: write
steps:
- uses: actions/stale@99b6c709598e2b0d0841cd037aaf1ba07a4410bd # tag=v5
- uses: actions/stale@3de2653986ebd134983c79fe2be5d45cc3d9f4e1 # tag=v6
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 90
Expand Down
2 changes: 1 addition & 1 deletion bots/package.json
Expand Up @@ -7,6 +7,6 @@
"main": "index.js",
"license": "MIT",
"devDependencies": {
"danger": "11.1.2"
"danger": "11.1.4"
}
}
396 changes: 254 additions & 142 deletions bots/yarn.lock

Large diffs are not rendered by default.

11 changes: 4 additions & 7 deletions detekt-core/src/main/resources/default-detekt-config.yml
Expand Up @@ -419,8 +419,6 @@ potential-bugs:
- 'java.util.HashSet'
- 'java.util.LinkedHashMap'
- 'java.util.HashMap'
DuplicateCaseInWhenExpression:
active: true
ElseCaseInsteadOfExhaustiveWhen:
active: false
EqualsAlwaysReturnsTrueOrFalse:
Expand Down Expand Up @@ -466,17 +464,14 @@ potential-bugs:
MissingPackageDeclaration:
active: false
excludes: ['**/*.kts']
MissingWhenCase:
active: true
allowElseExpression: true
NullCheckOnMutableProperty:
active: false
NullableToStringCall:
active: false
RedundantElseInWhen:
active: true
UnconditionalJumpStatementInLoop:
active: false
UnnecessaryNotNullCheck:
active: false
UnnecessaryNotNullOperator:
active: true
UnnecessarySafeCall:
Expand All @@ -499,6 +494,8 @@ potential-bugs:

style:
active: true
AlsoCouldBeApply:
active: false
CanBeNonNullable:
active: false
CascadingCallWrapping:
Expand Down
5 changes: 3 additions & 2 deletions detekt-core/src/main/resources/deprecation.properties
@@ -1,7 +1,10 @@
complexity>LongParameterList>threshold=Use `functionThreshold` and `constructorThreshold` instead
empty-blocks>EmptyFunctionBlock>ignoreOverriddenFunctions=Use `ignoreOverridden` instead
potential-bugs>DuplicateCaseInWhenExpression=Rule deprecated as compiler performs this check by default
potential-bugs>IgnoredReturnValue>restrictToAnnotatedMethods=Use `restrictToConfig` instead
potential-bugs>LateinitUsage>excludeAnnotatedProperties=Use `ignoreAnnotated` instead
potential-bugs>MissingWhenCase=Rule deprecated as compiler performs this check by default
potential-bugs>RedundantElseInWhen=Rule deprecated as compiler performs this check by default
naming>FunctionParameterNaming>ignoreOverriddenFunctions=Use `ignoreOverridden` instead
naming>MemberNameEqualsClassName>ignoreOverriddenFunction=Use `ignoreOverridden` instead
style>FunctionOnlyReturningConstant>excludeAnnotatedFunction=Use `ignoreAnnotated` instead
Expand All @@ -10,5 +13,3 @@ style>UnnecessaryAbstractClass>excludeAnnotatedClasses=Use `ignoreAnnotated` ins
style>UseDataClass>excludeAnnotatedClasses=Use `ignoreAnnotated` instead
formatting>Indentation>continuationIndentSize=`continuationIndentSize` is ignored by KtLint and will have no effect
formatting>ParameterListWrapping>indentSize=`indentSize` is ignored by KtLint and will have no effect
formatting>TrailingComma>allowTrailingComma=Use `TrailingCommaOnDeclarationSite` instead
formatting>TrailingComma>allowTrailingCommaOnCallSite=Use `TrailingCommaOnCallSite` instead
2 changes: 1 addition & 1 deletion detekt-generator/build.gradle.kts
Expand Up @@ -35,7 +35,7 @@ val ruleModules = rootProject.subprojects
.map { "${rootProject.rootDir}/$it/src/main/kotlin" }

val generateDocumentation by tasks.registering(JavaExec::class) {
dependsOn(tasks.assemble, ":detekt-api:dokkaHtml")
dependsOn(tasks.assemble, ":detekt-api:dokkaHtml", tasks.shadowJar, ":detekt-rules-ruleauthors:sourcesJar")
description = "Generates detekt documentation and the default config.yml based on Rule KDoc"
group = "documentation"

Expand Down
Expand Up @@ -13,5 +13,8 @@ data class Rule(
val configuration: List<Configuration> = emptyList(),
val autoCorrect: Boolean = false,
var inMultiRule: String? = null,
val requiresTypeResolution: Boolean = false
)
val requiresTypeResolution: Boolean = false,
val deprecationMessage: String? = null
) {
fun isDeprecated() = deprecationMessage != null
}
Expand Up @@ -5,6 +5,7 @@ import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault
import io.gitlab.arturbosch.detekt.api.internal.DefaultRuleSetProvider
import io.gitlab.arturbosch.detekt.generator.collection.exception.InvalidDocumentationException
import io.gitlab.arturbosch.detekt.rules.isOverride
import org.jetbrains.kotlin.psi.KtAnnotatedExpression
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtFile
Expand Down Expand Up @@ -128,6 +129,7 @@ class RuleSetProviderVisitor : DetektVisitor() {
val ruleArgumentNames = (ruleListExpression as? KtCallExpression)
?.valueArguments
?.mapNotNull { it.getArgumentExpression() }
?.map { if (it is KtAnnotatedExpression) it.lastChild as KtCallExpression else it }
?.mapNotNull { it.referenceExpression()?.text }
.orEmpty()

Expand Down
Expand Up @@ -35,6 +35,7 @@ internal class RuleVisitor : DetektVisitor() {
private var parent = ""
private val configurationCollector = ConfigurationCollector()
private val classesMap = mutableMapOf<String, Boolean>()
private var deprecationMessage: String? = null

fun getRule(): Rule {
if (documentationCollector.description.isEmpty()) {
Expand All @@ -55,6 +56,7 @@ internal class RuleVisitor : DetektVisitor() {
parent = parent,
configuration = configurationByAnnotation,
autoCorrect = autoCorrect,
deprecationMessage = deprecationMessage,
requiresTypeResolution = requiresTypeResolution
)
}
Expand Down Expand Up @@ -105,6 +107,7 @@ internal class RuleVisitor : DetektVisitor() {

autoCorrect = classOrObject.isAnnotatedWith(AutoCorrectable::class)
requiresTypeResolution = classOrObject.isAnnotatedWith(RequiresTypeResolution::class)
deprecationMessage = classOrObject.firstAnnotationParameterOrNull(Deprecated::class)

documentationCollector.setClass(classOrObject)
}
Expand Down
Expand Up @@ -10,6 +10,9 @@ object DeprecatedPrinter : DocumentationPrinter<List<RuleSetPage>> {
val builder = StringBuilder()
item.forEach { ruleSet ->
ruleSet.rules.forEach { rule ->
if (rule.isDeprecated()) {
builder.appendLine(writeRuleProperty(ruleSet, rule))
}
rule.configuration.forEach { configuration ->
if (configuration.isDeprecated()) {
builder.appendLine(writeProperty(ruleSet, rule, configuration))
Expand All @@ -25,3 +28,8 @@ private fun writeProperty(ruleSet: RuleSetPage, rule: Rule, configuration: Confi
@Suppress("UnsafeCallOnNullableType")
return "${ruleSet.ruleSet.name}>${rule.name}>${configuration.name}=${configuration.deprecated!!}"
}

private fun writeRuleProperty(ruleSet: RuleSetPage, rule: Rule): String {
@Suppress("UnsafeCallOnNullableType")
return "${ruleSet.ruleSet.name}>${rule.name}=${rule.deprecationMessage!!}"
}
Expand Up @@ -3,6 +3,7 @@ package io.gitlab.arturbosch.detekt.generator.printer
import io.github.detekt.utils.MarkdownContent
import io.github.detekt.utils.bold
import io.github.detekt.utils.codeBlock
import io.github.detekt.utils.crossOut
import io.github.detekt.utils.h3
import io.github.detekt.utils.h4
import io.github.detekt.utils.markdown
Expand All @@ -14,7 +15,12 @@ internal object RulePrinter : DocumentationPrinter<Rule> {

override fun print(item: Rule): String {
return markdown {
h3 { item.name }
if (item.isDeprecated()) {
h3 { crossOut { item.name } }
paragraph { escapeHtml(item.deprecationMessage.orEmpty()) }
} else {
h3 { item.name }
}

if (item.description.isNotEmpty()) {
paragraph { escapeHtml(item.description) }
Expand Down
Expand Up @@ -30,6 +30,8 @@ internal fun YamlNode.printRuleSet(ruleSet: RuleSetProvider, rules: List<Rule>)
}

internal fun YamlNode.printRule(rule: Rule) {
if (rule.isDeprecated()) return

node(rule.name) {
keyValue { Config.ACTIVE_KEY to "${rule.defaultActivationStatus.active}" }
if (rule.autoCorrect) {
Expand Down
Expand Up @@ -77,6 +77,9 @@ class ConfigAssert(
private fun getRuleClassesInPackage(): List<Class<out Rule>> {
return Reflections(packageName)
.getSubTypesOf(Rule::class.java)
.filter { !Modifier.isAbstract(it.modifiers) }
.filter { rule ->
!Modifier.isAbstract(rule.modifiers) &&
rule.annotations.none { it.annotationClass == Deprecated::class }
}
}
}
Expand Up @@ -12,7 +12,8 @@ class DeprecatedPrinterSpec {
val expectedMarkdownString = """
style>MagicNumber>conf2=use conf1 instead
style>MagicNumber>conf4=use conf3 instead
style>DuplicateCaseInWhenExpression=is deprecated
""".trimIndent()
assertThat(markdownString).isEqualTo(expectedMarkdownString)
}
Expand Down
Expand Up @@ -102,5 +102,17 @@ internal fun createRules(): List<Rule> {
autoCorrect = true,
requiresTypeResolution = true
)
return listOf(rule1, rule2, rule3)
val rule4 = Rule(
name = "DuplicateCaseInWhenExpression",
description = "Duplicated `case` statements in a `when` expression detected.",
nonCompliantCodeExample = "fun stuff(): Unit {}",
compliantCodeExample = "fun stuff() {}",
defaultActivationStatus = Active(since = "1.16.0"),
severity = "",
debt = "5m",
aliases = null,
parent = "",
deprecationMessage = "is deprecated"
)
return listOf(rule1, rule2, rule3, rule4)
}
22 changes: 22 additions & 0 deletions detekt-generator/src/test/resources/RuleSet.md
Expand Up @@ -75,3 +75,25 @@ fun stuff(): Unit {}
```kotlin
fun stuff() {}
```

### ~~DuplicateCaseInWhenExpression~~

is deprecated

Duplicated `case` statements in a `when` expression detected.

**Active by default**: Yes - Since v1.16.0

**Debt**: 5m

#### Noncompliant Code:

```kotlin
fun stuff(): Unit {}
```

#### Compliant Code:

```kotlin
fun stuff() {}
```
@@ -1,52 +1,53 @@
@file:Suppress("DEPRECATION")

package io.gitlab.arturbosch.detekt.internal

import io.gitlab.arturbosch.detekt.DetektPlugin
import io.gitlab.arturbosch.detekt.extensions.DetektExtension
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.gradle.api.internal.HasConvention
import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.tasks.SourceSet
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation

internal class DetektJvm(private val project: Project) {
fun registerTasks(extension: DetektExtension) {
project.convention.getPlugin(JavaPluginConvention::class.java).sourceSets.all { sourceSet ->
project.registerJvmDetektTask(extension, sourceSet)
project.registerJvmCreateBaselineTask(extension, sourceSet)
project.extensions.getByType(KotlinJvmProjectExtension::class.java).target.compilations.all { compilation ->
val inputSource = compilation.kotlinSourceSets
.map { it.kotlin.sourceDirectories }
.fold(project.files() as FileCollection) { collection, next -> collection.plus(next) }
project.registerJvmDetektTask(compilation, extension, inputSource)
project.registerJvmCreateBaselineTask(compilation, extension, inputSource)
}
}

private fun Project.registerJvmDetektTask(extension: DetektExtension, sourceSet: SourceSet) {
val kotlinSourceSet = (sourceSet as HasConvention).convention.plugins["kotlin"] as? KotlinSourceSet
?: throw GradleException("Kotlin source set not found. Please report on detekt's issue tracker")
registerDetektTask(DetektPlugin.DETEKT_TASK_NAME + sourceSet.name.capitalize(), extension) {
source = kotlinSourceSet.kotlin
classpath.setFrom(sourceSet.compileClasspath.existingFiles(), sourceSet.output.classesDirs.existingFiles())
private fun Project.registerJvmDetektTask(
compilation: KotlinCompilation<KotlinCommonOptions>,
extension: DetektExtension,
inputSource: FileCollection
) {
registerDetektTask(DetektPlugin.DETEKT_TASK_NAME + compilation.name.capitalize(), extension) {
setSource(inputSource)
classpath.setFrom(inputSource, compilation.compileDependencyFiles)
// If a baseline file is configured as input file, it must exist to be configured, otherwise the task fails.
// We try to find the configured baseline or alternatively a specific variant matching this task.
extension.baseline?.existingVariantOrBaseFile(sourceSet.name)?.let { baselineFile ->
baseline.convention(layout.file(project.provider { baselineFile }))
extension.baseline?.existingVariantOrBaseFile(compilation.name)?.let { baselineFile ->
baseline.convention(layout.file(provider { baselineFile }))
}
setReportOutputConventions(reports, extension, sourceSet.name)
description = "EXPERIMENTAL: Run detekt analysis for ${sourceSet.name} classes with type resolution"
setReportOutputConventions(reports, extension, compilation.name)
description = "EXPERIMENTAL: Run detekt analysis for ${compilation.name} classes with type resolution"
}
}

private fun Project.registerJvmCreateBaselineTask(extension: DetektExtension, sourceSet: SourceSet) {
val kotlinSourceSet = (sourceSet as HasConvention).convention.plugins["kotlin"] as? KotlinSourceSet
?: throw GradleException("Kotlin source set not found. Please report on detekt's issue tracker")
registerCreateBaselineTask(DetektPlugin.BASELINE_TASK_NAME + sourceSet.name.capitalize(), extension) {
source = kotlinSourceSet.kotlin
classpath.setFrom(sourceSet.compileClasspath.existingFiles(), sourceSet.output.classesDirs.existingFiles())
val variantBaselineFile = extension.baseline?.addVariantName(sourceSet.name)
baseline.convention(project.layout.file(project.provider { variantBaselineFile }))
description = "EXPERIMENTAL: Creates detekt baseline for ${sourceSet.name} classes with type resolution"
private fun Project.registerJvmCreateBaselineTask(
compilation: KotlinCompilation<KotlinCommonOptions>,
extension: DetektExtension,
inputSource: FileCollection
) {
registerCreateBaselineTask(DetektPlugin.BASELINE_TASK_NAME + compilation.name.capitalize(), extension) {
setSource(inputSource)
classpath.setFrom(inputSource, compilation.compileDependencyFiles)
val variantBaselineFile = extension.baseline?.addVariantName(compilation.name)
baseline.convention(layout.file(provider { variantBaselineFile }))
description = "EXPERIMENTAL: Creates detekt baseline for ${compilation.name} classes with type resolution"
}
}

private fun FileCollection.existingFiles() = filter { it.exists() }
}
Expand Up @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.psi.KtWhenExpression
* </compliant>
*/
@ActiveByDefault(since = "1.0.0")
@Deprecated("Rule deprecated as compiler performs this check by default")
class DuplicateCaseInWhenExpression(config: Config) : Rule(config) {

override val issue = Issue(
Expand Down
Expand Up @@ -68,6 +68,7 @@ import org.jetbrains.kotlin.resolve.calls.util.getType
*/
@ActiveByDefault(since = "1.2.0")
@RequiresTypeResolution
@Deprecated("Rule deprecated as compiler performs this check by default")
class MissingWhenCase(config: Config = Config.empty) : Rule(config) {

override val issue: Issue = Issue(
Expand Down
Expand Up @@ -20,7 +20,7 @@ class PotentialBugProvider : DefaultRuleSetProvider {
Deprecation(config),
DontDowncastCollectionTypes(config),
DoubleMutabilityForCollection(config),
DuplicateCaseInWhenExpression(config),
@Suppress("DEPRECATION") DuplicateCaseInWhenExpression(config),
ElseCaseInsteadOfExhaustiveWhen(config),
EqualsAlwaysReturnsTrueOrFalse(config),
EqualsWithHashCodeExist(config),
Expand All @@ -34,11 +34,12 @@ class PotentialBugProvider : DefaultRuleSetProvider {
LateinitUsage(config),
MapGetWithNotNullAssertionOperator(config),
MissingPackageDeclaration(config),
MissingWhenCase(config),
@Suppress("DEPRECATION") MissingWhenCase(config),
NullCheckOnMutableProperty(config),
RedundantElseInWhen(config),
@Suppress("DEPRECATION") RedundantElseInWhen(config),
UnconditionalJumpStatementInLoop(config),
UnnecessaryNotNullOperator(config),
UnnecessaryNotNullCheck(config),
UnnecessarySafeCall(config),
UnreachableCode(config),
UnsafeCallOnNullableType(config),
Expand Down
Expand Up @@ -59,6 +59,7 @@ import org.jetbrains.kotlin.psi.KtWhenExpression
*/
@RequiresTypeResolution
@ActiveByDefault(since = "1.2.0")
@Deprecated("Rule deprecated as compiler performs this check by default")
class RedundantElseInWhen(config: Config = Config.empty) : Rule(config) {

override val issue: Issue = Issue(
Expand Down

0 comments on commit 68d4159

Please sign in to comment.