Upgrade to Pro — share decks privately, control downloads, hide ads and more …

To Detekt 2.0, and beyond!

To Detekt 2.0, and beyond!

Do you know Detekt? We are a static analyzer for Kotlin.
Our mission: spot bugs, antipatterns, and potential errors in your Kotlin code.

Detekt is helping millions of Kotlin developers around the globe to spot bugs before they reach production. What took us here is a vibrant community of users & contributors that are helping us build this ecosystem of tools.

Today, you can easily extend Detekt with your own rules and integrate it with Gradle, Maven, Bazel, IntelliJ, Github, SonarQube, and much more.

Curious to hear about some of the future we've been working on? Join us in this session as we walk through the current status of Detekt, and give you updates on what's lined up for 2.0!

Nicola Corti

November 26, 2023
Tweet

More Decks by Nicola Corti

Other Decks in Programming

Transcript

  1. KotlinConf’23
    Amsterdam
    TO DETEKT 2.0
    AND BEYOND
    Nicola Corti
    detekt Maintainer
    @cortinico

    View full-size slide

  2. Nicola Corti
    Kotlin GDE
    Detekt Maintainer
    twitter.com/cortinico
    [email protected]
    github.com/cortinico
    ncorti.com
    The Developers’ Bakery Podcast
    thebakery.dev

    View full-size slide

  3. 1.x 🚀

    View full-size slide

  4. comments complexity
    coroutines empty-blocks
    exceptions naming
    performance
    potential-bugs
    style
    libraries
    formatting
    ruleauthors

    View full-size slide

  5. style:

    active: true

    View full-size slide

  6. style:

    active: true

    MagicNumber:

    active: true

    View full-size slide

  7. style:

    active: true

    MagicNumber:

    active: true

    excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/
    jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/
    jsTest/**', '**/iosTest/**', '**/*.kts' ]

    View full-size slide

  8. style:

    active: true

    MagicNumber:

    active: true

    excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/
    jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/
    jsTest/**', '**/iosTest/**', '**/*.kts' ]

    ignoreNumbers:

    - '-1'

    - '0'

    - '1'

    - '2'

    View full-size slide

  9. style:

    active: true

    MagicNumber:

    active: true

    excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/
    jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/
    jsTest/**', '**/iosTest/**', '**/*.kts' ]

    ignoreNumbers:

    - '-1'

    - '0'

    - '1'

    - '2'

    ignoreHashCodeFunction: true

    ignorePropertyDeclaration: false

    ignoreLocalVariableDeclaration: false

    ignoreConstantDeclaration: true

    ignoreCompanionObjectPropertyDeclaration: true

    ignoreAnnotation: false

    View full-size slide

  10. fun main() {
    }
    MagicNumber

    View full-size slide

  11. fun main() {
    println("🐋")
    }
    MagicNumber

    View full-size slide

  12. fun main() {
    if (Random.nextInt() > 42) {
    println("🐋")
    }
    }
    MagicNumber
    This expression contains a magic
    number. Consider def
    i
    ning it
    to a well named constant

    View full-size slide

  13. UnnecessaryNotNullOperator
    val zero = 0

    View full-size slide

  14. UnnecessaryNotNullOperator
    val zero = 0
    val answer = zero.plus(42)

    View full-size slide

  15. UnnecessaryNotNullOperator
    val zero = 0
    val answer = zero
    ! ! .
    plus(42)
    `zero
    ! !
    ` contains an unnecessary
    not
    -
    null (
    ! !
    ) operator

    View full-size slide

  16. val zero = 0
    val answer = zero
    ! ! .
    plus(42)
    UnnecessaryNotNullOperator
    MagicNumber
    fun main() {
    if (Random.nextInt() > 42) {
    println("🐋")
    }
    }

    View full-size slide

  17. val zero = getANumber()
    val answer = zero
    ! ! .
    plus(42)
    UnnecessaryNotNullOperator
    MagicNumber
    fun main() {
    if (Random.nextInt() > 42) {
    println("🐋")
    }
    }

    View full-size slide

  18. val zero = getANumber()
    val answer = zero
    ! ! .
    plus(42)
    UnnecessaryNotNullOperator
    MagicNumber
    fun main() {
    if (Random.nextInt() > 42) {
    println("🐋")
    }
    }
    : Int?
    : Int

    View full-size slide

  19. val zero = getANumber()
    val answer = zero
    ! ! .
    plus(42)
    UnnecessaryNotNullOperator
    MagicNumber
    fun main() {
    if (Random.nextInt() > 42) {
    println("🐋")
    }
    }
    : Int?
    : Int
    Type Resolution
    UnnecessaryNotNullOperator needs to know the return type of getANumber()

    View full-size slide

  20. plugins {
    id("io.gitlab.arturbosch.detekt") version "1.22.0"
    }

    View full-size slide

  21. plugins {
    id("io.gitlab.arturbosch.detekt") version "1.22.0"
    }
    repositories {
    mavenCentral()
    }

    View full-size slide

  22. plugins {
    id("io.gitlab.arturbosch.detekt") version "1.22.0"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    conf
    i
    g = f
    i
    les("conf
    i
    g/detekt/detekt.yml")
    }

    View full-size slide

  23. plugins {
    id("io.gitlab.arturbosch.detekt") version "1.22.0"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    conf
    i
    g = f
    i
    les("conf
    i
    g/detekt/detekt.yml")
    }
    $ ./gradlew tasks | grep detekt
    detekt
    detektBaseline
    detektGenerateConf
    i
    g

    View full-size slide

  24. plugins {
    id("io.gitlab.arturbosch.detekt") version "1.22.0"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    conf
    i
    g = f
    i
    les("conf
    i
    g/detekt/detekt.yml")
    }
    $ ./gradlew tasks | grep detekt
    detekt
    detektBaseline
    detektGenerateConf
    i
    g

    View full-size slide

  25. plugins {
    id("io.gitlab.arturbosch.detekt") version "1.22.0"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    conf
    i
    g = f
    i
    les("conf
    i
    g/detekt/detekt.yml")
    }
    $ ./gradlew tasks | grep detekt
    detekt
    detektBaseline
    detektGenerateConf
    i
    g

    View full-size slide

  26. plugins {
    id("io.gitlab.arturbosch.detekt") version "1.22.0"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    conf
    i
    g = f
    i
    les("conf
    i
    g/detekt/detekt.yml")
    }
    $ ./gradlew tasks | grep detekt
    detekt
    detektBaseline
    detektGenerateConf
    i
    g

    View full-size slide

  27. plugins {
    kotlin("jvm")
    id("io.gitlab.arturbosch.detekt") version "1.22.0"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    conf
    i
    g = f
    i
    les("conf
    i
    g/detekt/detekt.yml")
    }

    View full-size slide

  28. $ ./gradlew tasks | grep detekt
    detekt
    detektBaseline
    detektBaselineMain
    detektBaselineTest
    detektGenerateConf
    i
    g
    detektMain
    detektTest
    plugins {
    kotlin("jvm")
    id("io.gitlab.arturbosch.detekt") version "1.22.0"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    conf
    i
    g = f
    i
    les("conf
    i
    g/detekt/detekt.yml")
    }

    View full-size slide

  29. $ ./gradlew tasks | grep detekt
    detekt
    detektBaseline
    detektBaselineMain
    detektBaselineTest
    detektGenerateConf
    i
    g
    detektMain
    detektTest
    plugins {
    kotlin("jvm")
    id("io.gitlab.arturbosch.detekt") version "1.22.0"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    conf
    i
    g = f
    i
    les("conf
    i
    g/detekt/detekt.yml")
    }

    View full-size slide

  30. $ ./gradlew tasks | grep detekt
    detekt
    detektBaseline
    detektBaselineMain
    detektBaselineTest
    detektGenerateConf
    i
    g
    detektMain
    detektTest
    plugins {
    kotlin("jvm")
    id("io.gitlab.arturbosch.detekt") version "1.22.0"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    conf
    i
    g = f
    i
    les("conf
    i
    g/detekt/detekt.yml")
    }

    View full-size slide

  31. Multiplatform

    View full-size slide

  32. Multiplatform
    plugins {
    kotlin("multiplatform")
    id("io.gitlab.arturbosch.detekt") version "1.22.0"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    conf
    i
    g = f
    i
    les("conf
    i
    g/detekt/detekt.yml")
    }

    View full-size slide

  33. Multiplatform
    plugins {
    kotlin("multiplatform")
    id("io.gitlab.arturbosch.detekt") version "1.22.0"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    conf
    i
    g = f
    i
    les("conf
    i
    g/detekt/detekt.yml")
    }
    kotlin {
    jvm()
    android()
    ios()
    js()
    }

    View full-size slide

  34. Multiplatform
    $ ./gradlew tasks | grep detekt
    detekt
    detektAndroidDebug
    detektAndroidDebugAndroidTest
    detektAndroidDebugUnitTest
    detektAndroidRelease
    detektAndroidReleaseUnitTest
    detektBaseline
    detektBaselineAndroidDebug
    detektBaselineAndroidDebugAndroidTest
    detektBaselineAndroidDebugUnitTest
    detektBaselineAndroidRelease
    detektBaselineAndroidReleaseUnitTest
    detektBaselineIosArm64Main
    detektBaselineIosArm64Test
    detektBaselineIosX64Main
    detektBaselineIosX64Test
    detektBaselineJsMain
    detektBaselineJsTest
    detektBaselineJvmMain
    detektBaselineJvmTest
    detektBaselineMetadataCommonMain
    detektBaselineMetadataIosMain
    detektBaselineMetadataMain
    detektGenerateConf
    i
    g
    detektIosArm64Main
    detektIosArm64Test
    detektIosX64Main
    detektIosX64Test
    detektJsMain
    detektJsTest
    detektJvmMain
    detektJvmTest
    detektMetadataCommonMain
    detektMetadataIosMain
    detektMetadataMain

    View full-size slide

  35. Much more…
    brew install detekt
    Danger JS Plugin
    github.com/AckeeCZ/danger-kotlin-detekt
    Maven Plugin
    github.com/Ozsie/detekt-maven-plugin
    Bazel Plugin
    github.com/buildfoundation/bazel_rules_detekt
    SARIF Support
    Sonarqube Plugin
    github.com/detekt/sonar-kotlin
    IntelliJ Plugin
    github.com/detekt/detekt-intellij-plugin
    Github Action Integration
    github.com/natiginfo/action-detekt-all

    View full-size slide

  36. class MyCustomRuleSetProvider : RuleSetProvider {
    override val ruleSetId = "my
    -
    rules"
    override fun instance(conf
    i
    g: Conf
    i
    g) = RuleSet(
    ruleSetId,
    listOf(MyCustomRule(conf
    i
    g))
    )
    }
    com.example.MyCustomRuleSetProvider
    class MyCustomRule(
    conf
    i
    g: Conf
    i
    g = Conf
    i
    g.empty
    ) : Rule(conf
    i
    g) {
    override val issue = Issue(
    / /
    …
    )
    }
    github.com/detekt/detekt-custom-rule-template

    View full-size slide

  37. Versioned Docs

    View full-size slide

  38. v1.0.0-RC15 v1.0.0-RC16 v1.0.0.M10 v1.0.0.M10.1
    v1.0.0.M10.3 v1.0.0.M7 v1.0.0.M7.b1 v1.0.0.M8
    v1.0.0.M8.1 v1.0.0.M9 v1.0.1 v1.1.0 v1.1.1 v1.2.0 v1.2.1 v1.2.2
    v1.3.0 v1.3.1 v1.4.0 v1.5.0 v1.5.1 v1.6.0 v1.7.0 v1.7.0-beta2
    v1.7.1 v1.7.2 v1.7.3 v1.7.4 v1.8.0 v1.9.0 v1.9.1 v1.10.0
    v1.10.0-RC1 v1.11.0 v1.11.0-RC1 v1.11.0-RC2 v1.11.1 v1.11.2
    v1.12.0 v1.12.0-RC1 v1.13.0 v1.13.1 v1.14.0 v1.14.1 v1.14.2
    v1.15.0 v1.15.0-RC1 v1.15.0-RC2 v1.16.0 v1.16.0-RC1
    v1.16.0-RC2 v1.16.0-RC3 v1.17.0 v1.17.0-RC1 v1.17.0-RC2
    v1.17.0-RC3 v1.17.1 v1.18.0 v1.18.0-RC1 v1.18.0-RC2
    v1.18.0-RC3 v1.18.1 v1.19.0 v1.19.0-RC1 v1.19.0-RC2
    v1.20.0 v1.20.0-RC1 v1.20.0-RC2 v1.21.0 v1.21.0-RC1
    v1.21.0-RC2 v1.22.0 v1.22.0-RC1 v1.22.0-RC2 v1.22.0-
    RC3 v1.23.0-RC1

    View full-size slide

  39. v1.23.0-RC1
    2.x

    View full-size slide

  40. 2.x
    1.23.x
    1.x

    View full-size slide

  41. ./gradlew build
    detekt
    (no Type Resolution)
    compileKotlin

    View full-size slide

  42. detektMain
    (w/ Type Resolution)
    Runs another compilation
    Internally to resolve types
    ./gradlew build
    • Running two compilations is bad
    for performance
    • Misaligned compiler flags
    • Harder to get the correct
    classpath every time
    (ViewBinding, etc.)
    compileKotlin

    View full-size slide

  43. compileKotlin Runs detekt during the
    compilation
    ./gradlew build
    • No more double compilation
    • Type Resolution close to the
    production code
    • No more custom tasks for every
    source set
    Compiler Plugin

    View full-size slide

  44. Compiler Plugin
    plugins {
    kotlin("jvm")
    id("io.gitlab.arturbosch.detekt") version “…”
    }
    repositories {
    mavenCentral()
    }

    View full-size slide

  45. plugins {
    kotlin("jvm")
    id("io.github.detekt.gradle.compiler
    -
    plugin") version "…"
    }
    repositories {
    mavenCentral()
    }
    Compiler Plugin

    View full-size slide

  46. plugins {
    kotlin("jvm")
    id("io.github.detekt.gradle.compiler
    -
    plugin") version "…"
    }
    repositories {
    mavenCentral()
    }
    Compiler Plugin

    View full-size slide

  47. Compiler Plugin

    View full-size slide

  48. $ ./gradlew assemble
    Compiler Plugin

    View full-size slide

  49. $ ./gradlew assemble
    > Task :compileKotlin FAILED
    w: Build failed with 1 weighted issues.
    e: warnings found and -Werror specif
    i
    ed
    w: /Users/ncorti/oss/detekt
    -
    compiler
    -
    plugin/src/main/java/Sample.kt: (3, 21)
    :
    MagicNumber: This expression contains a magic number. Consider def
    i
    ning it to a
    well named constant.
    FAILURE
    :
    Build failed with an exception.
    * What went wrong:
    Execution failed for task ':compileKotlin'.
    > Compilation error. See log for more details
    BUILD FAILED in 1s
    1 actionable task: 1 executed
    Compiler Plugin

    View full-size slide

  50. $ ./gradlew assemble
    > Task :compileKotlin FAILED
    w: Build failed with 1 weighted issues.
    e: warnings found and -Werror specif
    i
    ed
    w: /Users/ncorti/oss/detekt
    -
    compiler
    -
    plugin/src/main/java/Sample.kt: (3, 21)
    :
    MagicNumber: This expression contains a magic number. Consider def
    i
    ning it to a
    well named constant.
    FAILURE
    :
    Build failed with an exception.
    * What went wrong:
    Execution failed for task ':compileKotlin'.
    > Compilation error. See log for more details
    BUILD FAILED in 1s
    1 actionable task: 1 executed
    Compiler Plugin

    View full-size slide

  51. Multiplatform
    $ ./gradlew tasks | grep detekt
    detekt
    detektAndroidDebug
    detektAndroidDebugAndroidTest
    detektAndroidDebugUnitTest
    detektAndroidRelease
    detektAndroidReleaseUnitTest
    detektBaseline
    detektBaselineAndroidDebug
    detektBaselineAndroidDebugAndroidTest
    detektBaselineAndroidDebugUnitTest
    detektBaselineAndroidRelease
    detektBaselineAndroidReleaseUnitTest
    detektBaselineIosArm64Main
    detektBaselineIosArm64Test
    detektBaselineIosX64Main
    detektBaselineIosX64Test
    detektBaselineJsMain
    detektBaselineJsTest
    detektBaselineJvmMain
    detektBaselineJvmTest
    detektBaselineMetadataCommonMain
    detektBaselineMetadataIosMain
    detektBaselineMetadataMain
    detektGenerateConf
    i
    g
    detektIosArm64Main
    detektIosArm64Test
    detektIosX64Main
    detektIosX64Test
    detektJsMain
    detektJsTest
    detektJvmMain
    detektJvmTest
    detektMetadataCommonMain
    detektMetadataIosMain
    detektMetadataMain

    View full-size slide

  52. Multiplatform
    $ ./gradlew tasks | grep detekt
    detekt
    detektAndroidDebug
    detektAndroidDebugAndroidTest
    detektAndroidDebugUnitTest
    detektAndroidRelease
    detektAndroidReleaseUnitTest
    detektBaseline
    detektBaselineAndroidDebug
    detektBaselineAndroidDebugAndroidTest
    detektBaselineAndroidDebugUnitTest
    detektBaselineAndroidRelease
    detektBaselineAndroidReleaseUnitTest
    detektBaselineIosArm64Main
    detektBaselineIosArm64Test
    detektBaselineIosX64Main
    detektBaselineIosX64Test
    detektBaselineJsMain
    detektBaselineJsTest
    detektBaselineJvmMain
    detektBaselineJvmTest
    detektBaselineMetadataCommonMain
    detektBaselineMetadataIosMain
    detektBaselineMetadataMain
    detektGenerateConf
    i
    g
    detektIosArm64Main
    detektIosArm64Test
    detektIosX64Main
    detektIosX64Test
    detektJsMain
    detektJsTest
    detektJvmMain
    detektJvmTest
    detektMetadataCommonMain
    detektMetadataIosMain
    detektMetadataMain

    View full-size slide

  53. New Rules
    • UnnecessaryInnerClass
    • CanBeNonNullable
    • NullCheckOnMutableProperty
    • SuspendFunWithCoroutineScopeReceiver
    • ElseCaseInsteadOfExhaustiveWhen
    • TrailingComma - From KtLint
    • UnnecessaryParenthesesBeforeTrailingLambda - From KtLint
    • BlockCommentInitialStarAlignment - From KtLint
    • CommentWrapping - From KtLint
    • DiscouragedCommentLocation - From KtLint
    • FunKeywordSpacing - From KtLint
    • FunctionTypeReferenceSpacing - From KtLint
    • KdocWrapping - From KtLint
    • Modif
    i
    erListSpacing - From KtLint
    • TypeArgumentListSpacing - From KtLint
    • Wrapping - From KtLint
    • BracesOnIfStatements
    • CastNullableToNonNullableType
    • DoubleNegativeLambda
    • ForbiddenAnnotation
    • StringShouldBeRawString
    • SuspendFunSwallowedCancellation
    • UnusedParameter
    • UnusedPrivateProperty

    View full-size slide

  54. ge.detekt.dev

    View full-size slide

  55. ge.detekt.dev

    View full-size slide

  56. Tool
    Alignment

    View full-size slide

  57. New Rulesets
    libraries:

    active: true

    ForbiddenPublicDataClass:

    active: true

    ignorePackages:

    - '*.internal'

    - '*.internal.*'

    LibraryCodeMustSpecifyReturnType:

    active: true

    LibraryEntitiesShouldNotBePublic:

    active: true

    ruleauthors:

    active: true

    UseEntityAtName:

    active: true

    ViolatesTypeResolutionRequirements:

    active: true

    View full-size slide

  58. New Rulesets
    libraries:

    active: true

    ForbiddenPublicDataClass:

    active: true

    ignorePackages:

    - '*.internal'

    - '*.internal.*'

    LibraryCodeMustSpecifyReturnType:

    active: true

    LibraryEntitiesShouldNotBePublic:

    active: true

    ruleauthors:

    active: true

    UseEntityAtName:

    active: true

    ViolatesTypeResolutionRequirements:

    active: true

    View full-size slide

  59. 2.x
    1.23.x
    2.x

    View full-size slide

  60. main
    1.23
    2.x
    1.x
    • 2.x
    • New Rules & New Features
    • Breaking Changes
    • 1.x
    • Tool Version Bumps
    • False positive/negative fixes

    View full-size slide

  61. dev.detekt.*
    plugins {
    kotlin("jvm")
    id("io.gitlab.arturbosch.detekt") version "1.22.0"
    }
    dependencies {
    implementation("io.gitlab.arturbosch.detekt:detekt
    -
    api:1.22.0")
    }

    View full-size slide

  62. dev.detekt.*
    plugins {
    kotlin("jvm")
    id("dev.detekt") version "1.22.0"
    }
    dependencies {
    implementation("dev.detekt:detekt
    -
    api:1.22.0")
    }

    View full-size slide

  63. Spring Cleaning
    complexity>ComplexMethod=Rule is renamed to `CyclomaticComplexMethod` to distinguish between Cyclomatic Complexit

    View full-size slide

  64. Spring Cleaning
    complexity>ComplexMethod=Rule is renamed to `CyclomaticComplexMethod` to distinguish between Cyclomatic Complexit
    complexity>LongParameterList>threshold=Use `functionThreshold` and `constructorThreshold` instead

    empty-blocks>EmptyFunctionBlock>ignoreOverriddenFunctions=Use `ignoreOverridden` instead

    formatting>Indentation>continuationIndentSize=`continuationIndentSize` is ignored by KtLint and will have no effe
    formatting>ParameterListWrapping>indentSize=`indentSize` is ignored by KtLint and will have no effect

    formatting>TrailingComma=Rule is split between `TrailingCommaOnCallSite` and `TrailingCommaOnDeclarationSite` now
    naming>BooleanPropertyNaming>ignoreOverridden=This configuration is ignored and will be removed in the future

    naming>ConstructorParameterNaming>ignoreOverridden=This configuration is ignored and will be removed in the futur
    naming>FunctionNaming>ignoreOverridden=This configuration is ignored and will be removed in the future

    naming>FunctionParameterNaming>ignoreOverridden=This configuration is ignored and will be removed in the future

    naming>FunctionParameterNaming>ignoreOverriddenFunctions=Use `ignoreOverridden` instead

    naming>MemberNameEqualsClassName>ignoreOverriddenFunction=Use `ignoreOverridden` instead

    naming>VariableNaming>ignoreOverridden=This configuration is ignored and will be removed in the future

    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

    style>ForbiddenPublicDataClass=Rule migrated to `libraries` ruleset plugin

    style>FunctionOnlyReturningConstant>excludeAnnotatedFunction=Use `ignoreAnnotated` instead

    style>LibraryCodeMustSpecifyReturnType=Rule migrated to `libraries` ruleset plugin

    style>LibraryEntitiesShouldNotBePublic=Rule migrated to `libraries` ruleset plugin

    View full-size slide

  65. Spring Cleaning
    complexity>ComplexMethod=Rule is renamed to `CyclomaticComplexMethod` to distinguish between Cyclomatic Complexit
    complexity>LongParameterList>threshold=Use `functionThreshold` and `constructorThreshold` instead

    empty-blocks>EmptyFunctionBlock>ignoreOverriddenFunctions=Use `ignoreOverridden` instead

    formatting>Indentation>continuationIndentSize=`continuationIndentSize` is ignored by KtLint and will have no effe
    formatting>ParameterListWrapping>indentSize=`indentSize` is ignored by KtLint and will have no effect

    formatting>TrailingComma=Rule is split between `TrailingCommaOnCallSite` and `TrailingCommaOnDeclarationSite` now
    naming>BooleanPropertyNaming>ignoreOverridden=This configuration is ignored and will be removed in the future

    naming>ConstructorParameterNaming>ignoreOverridden=This configuration is ignored and will be removed in the futur
    naming>FunctionNaming>ignoreOverridden=This configuration is ignored and will be removed in the future

    naming>FunctionParameterNaming>ignoreOverridden=This configuration is ignored and will be removed in the future

    naming>FunctionParameterNaming>ignoreOverriddenFunctions=Use `ignoreOverridden` instead

    naming>MemberNameEqualsClassName>ignoreOverriddenFunction=Use `ignoreOverridden` instead

    naming>VariableNaming>ignoreOverridden=This configuration is ignored and will be removed in the future

    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

    style>ForbiddenPublicDataClass=Rule migrated to `libraries` ruleset plugin

    style>FunctionOnlyReturningConstant>excludeAnnotatedFunction=Use `ignoreAnnotated` instead

    style>LibraryCodeMustSpecifyReturnType=Rule migrated to `libraries` ruleset plugin

    style>LibraryEntitiesShouldNotBePublic=Rule migrated to `libraries` ruleset plugin

    View full-size slide

  66. Spring Cleaning
    style>UnderscoresInNumericLiterals>acceptableDecimalLength=Use `acceptableLength` instead

    style>UnnecessaryAbstractClass>excludeAnnotatedClasses=Use `ignoreAnnotated` instead

    style>UseDataClass>excludeAnnotatedClasses=Use `ignoreAnnotated` instead

    • FunctionMaxLength

    • MayBeConst

    • SpacingBetweenPackageAndImports

    View full-size slide

  67. Spring Cleaning
    style>UnderscoresInNumericLiterals>acceptableDecimalLength=Use `acceptableLength` instead

    style>UnnecessaryAbstractClass>excludeAnnotatedClasses=Use `ignoreAnnotated` instead

    style>UseDataClass>excludeAnnotatedClasses=Use `ignoreAnnotated` instead

    • FunctionMaxLength
    - >
    FunctionNameMaxLength

    • MayBeConst
    ->
    MayBeConstant

    • SpacingBetweenPackageAndImports
    ->
    ?

    View full-size slide

  68. ./gradlew detekt

    ./gradlew detektMain

    View full-size slide

  69. ./gradlew detekt

    ./gradlew detektMain
    ./gradlew detektFull

    ./gradlew detektLite
    usesTypeResolution.set(true)
    Option 1
    Option 2

    View full-size slide

  70. Gradle API Revamp
    @CacheableTask

    abstract class Detekt : SourceTask() {

    @get:Input

    @get:Optional

    internal abstract val jvmTargetProp: Property

    var jvmTarget: String

    @Internal

    get() = jvmTargetProp.get()

    set(value) = jvmTargetProp.set(value)

    View full-size slide

  71. Gradle API Revamp
    @CacheableTask

    abstract class Detekt : SourceTask() {

    @get:Input

    @get:Optional

    internal abstract val jvmTargetProp: Property

    var jvmTarget: String

    @Internal

    get() = jvmTargetProp.get()

    set(value) = jvmTargetProp.set(value)

    Actually fixed in Gradle 8.1 😱

    View full-size slide

  72. formatting:

    android: false

    autoCorrect: true

    AnnotationOnSeparateLine:

    AnnotationSpacing:

    ArgumentListWrapping:

    ChainWrapping:

    CommentSpacing:

    EnumEntryNameCase:

    Filename:

    FinalNewline:

    ImportOrdering:

    Indentation:

    MaximumLineLength:

    ModifierOrdering:

    MultiLineIfElse:

    NoBlankLineBeforeRbrace:

    NoConsecutiveBlankLines:

    NoEmptyClassBody:

    NoEmptyFirstLineInMethodBlock:

    NoLineBreakAfterElse:

    NoLineBreakBeforeAssignment:

    NoMultipleSpaces:

    NoSemicolons:

    NoTrailingSpaces:

    NoUnitReturn:

    NoUnusedImports:

    NoWildcardImports:

    PackageName:

    ParameterListWrapping:

    SpacingAroundAngleBrackets:

    SpacingAroundColon:

    SpacingAroundComma:

    SpacingAroundCurly:

    SpacingAroundDot:

    SpacingAroundDoubleColon:

    SpacingAroundKeyword:

    SpacingAroundOperators:

    SpacingAroundParens:

    SpacingAroundRangeOperator:

    SpacingAroundUnaryOperator:

    SpacingBetweenDeclarationsWithAnnotations:

    SpacingBetweenDeclarationsWithComments:

    StringTemplate:

    View full-size slide

  73. • Ktlint Wrapper
    • Offered as
    Custom rules
    (formatting)
    formatting:

    android: false

    autoCorrect: true

    AnnotationOnSeparateLine:

    AnnotationSpacing:

    ArgumentListWrapping:

    ChainWrapping:

    CommentSpacing:

    EnumEntryNameCase:

    Filename:

    FinalNewline:

    ImportOrdering:

    Indentation:

    View full-size slide

  74. formatting:

    active: true

    NoWildcardImports:

    active: true

    NoUnusedImports:

    active: true
    style:

    WildcardImport:

    active: true

    excludeImports: [‘java.util.*']

    UnusedImports:

    active: false

    View full-size slide

  75. formatting:

    active: true

    NoWildcardImports:

    active: true

    NoUnusedImports:

    active: true
    style:

    WildcardImport:

    active: true

    excludeImports: [‘java.util.*']

    UnusedImports:

    active: false

    View full-size slide

  76. style:

    WildcardImport:

    active: true

    excludeImports: [‘java.util.*']

    UnusedImports:

    active: false
    formatting:

    active: true

    NoWildcardImports:

    active: true

    NoUnusedImports:

    active: true

    View full-size slide

  77. formatting:

    active: true

    NoWildcardImports:

    active: true

    NoUnusedImports:

    active: true
    style:

    WildcardImport:

    active: true

    excludeImports: [‘java.util.*']

    UnusedImports:

    active: false
    Should we remove it?

    View full-size slide

  78. bit.ly/detekt-remove-formatting
    Should we remove it?

    View full-size slide

  79. autoCorrect: false

    View full-size slide

  80. Severity
    /**

    * Rules can classified into different severity grades. Maintainer can choose

    * a grade which is most harmful to their projects.

    *

    * Note: This will be replaced by [SeverityLevel] in future versions of detekt.

    */


    enum class Severity {

    View full-size slide

  81. Severity
    /**

    * Rules can classified into different severity grades. Maintainer can choose

    * a grade which is most harmful to their projects.

    *

    * Note: This will be replaced by [SeverityLevel] in future versions of detekt.

    */


    enum class Severity {

    /**

    * Represents clean coding violations which may lead to maintainability issues.

    */


    CodeSmell,

    /**

    * Inspections in this category detect violations of code syntax styles.

    */


    Style,

    /**

    * Corresponds to issues that do not prevent the code from working,

    * but may nevertheless represent coding inefficiencies.

    */


    Warning,

    /**

    * Corresponds to coding mistakes which could lead to unwanted behavior.

    */


    Defect,

    View full-size slide

  82. Severity
    /**

    * Rules can classified into different severity grades. Maintainer can choose

    * a grade which is most harmful to their projects.

    *

    * Note: This will be replaced by [SeverityLevel] in future versions of detekt.

    */


    enum class Severity {

    /**

    * Represents clean coding violations which may lead to maintainability issues.

    */


    CodeSmell,

    /**

    * Inspections in this category detect violations of code syntax styles.

    */


    Style,

    /**

    * Corresponds to issues that do not prevent the code from working,

    * but may nevertheless represent coding inefficiencies.

    */


    Warning,

    /**

    * Corresponds to coding mistakes which could lead to unwanted behavior.

    */


    Defect,

    View full-size slide

  83. Severity
    /**

    * Rules can classified into different severity grades. Maintainer can choose

    * a grade which is most harmful to their projects.

    *

    * Note: This will be replaced by [SeverityLevel] in future versions of detekt.

    */


    enum class Severity {

    /**

    * Represents clean coding violations which may lead to maintainability issues.

    */


    CodeSmell,

    /**

    * Inspections in this category detect violations of code syntax styles.

    */


    Style,

    /**

    * Corresponds to issues that do not prevent the code from working,

    * but may nevertheless represent coding inefficiencies.

    */


    Warning,

    /**

    * Corresponds to coding mistakes which could lead to unwanted behavior.

    */


    Defect,

    View full-size slide

  84. @Suppressing

    View full-size slide

  85. @Suppressing
    fun main() {
    if (Random.nextInt() > 42) {
    println("🐋")
    }
    }

    View full-size slide

  86. @Suppressing
    fun main() {
    @Suppress("MagicNumber")
    if (Random.nextInt() > 42) {
    println("🐋")
    }
    }

    View full-size slide

  87. @Suppressing
    @Suppress("MagicNumber")
    fun main() {
    if (Random.nextInt() > 42) {
    println("🐋")
    }
    }

    View full-size slide

  88. @Suppressing
    @f
    i
    le:Suppress("MagicNumber")
    fun main() {
    if (Random.nextInt() > 42) {
    println("🐋")
    }
    }

    View full-size slide

  89. @Suppressing
    @f
    i
    le:Suppress("MagicNumber")
    val THE_ANSWER = 42

    fun main() {
    if (Random.nextInt() > THE_ANSWER) {
    println("🐋")
    }
    }
    Not relevant anymore (and harmful)

    View full-size slide

  90. naming:

    ClassNaming:

    active: true

    classPattern: '[A-Z][a-zA-Z0-9]*'

    View full-size slide

  91. naming:

    ClassNaming:

    active: true

    classPattern: '[A-Z][a-zA-Z0-9]*'

    FunctionNaming:

    active: true

    functionPattern: '[a-z][a-zA-Z0-9]*'

    View full-size slide

  92. naming:

    ClassNaming:

    active: true

    classPattern: '[A-Z][a-zA-Z0-9]*(Test|Spec)'

    FunctionNaming:

    active: true

    functionPattern: '[a-z][a-zA-Z0-9]*'

    View full-size slide

  93. Multiple Config Instantiation
    naming:

    ClassNaming:

    active: true

    classPattern: '[A-Z][a-zA-Z0-9]*'

    testClassPattern: '[A-Z][a-zA-Z0-9]*(Test|Spec)'

    FunctionNaming:

    active: true

    functionPattern: '[a-z][a-zA-Z0-9]*'

    testFunctionPattern: '[A-Z][a-zA-Z0-9 _=+,!@#$^&()-]*'

    🤔

    View full-size slide

  94. Baseline &
    Report
    merging

    View full-size slide

  95. 3.x
    1.23.x
    2.x
    3.x

    View full-size slide

  96. Help us migrate

    View full-size slide

  97. Help us migrate
    AbsentOrWrongFileLicense

    AlsoCouldBeApply

    ArrayPrimitive

    AvoidReferentialEquality

    View full-size slide

  98. Help us migrate
    UseCheckOrError

    UseDataClass

    UseEmptyCounterpart

    UseIfEmptyOrIfBlank

    UseIfInsteadOfWhen

    UseIsNullOrEmpty

    UseOrEmpty

    UseRequire

    UseRequireNotNull

    UseSumOfInsteadOfFlatMapSize

    UselessCallOnNotNull

    UselessPostfixExpression

    UtilityClassWithPublicConstructor

    VarCouldBeVal

    VariableMaxLength

    VariableMinLength

    VariableNaming

    WildcardImport

    WrongEqualsTypeParameter

    View full-size slide

  99. Join Detekt! bit.ly/detekt-20

    View full-size slide

  100. detekt vs Detekt

    View full-size slide

  101. Thank you,
    and don’t
    forget to vote
    KotlinConf’23
    Amsterdam
    Nicola Corti
    detekt Maintainer
    @cortinico

    View full-size slide

  102. BracesOnIfStatements
    if (a) {

    b

    } else c

    //
    * `always`: forces braces on all `if` and `else`

    //
    branches in the whole codebase.

    //
    * `consistent`: ensures that braces are consistent

    //
    within each `if`-`else if`-`else` chain.

    //
    If there's a brace on one of the branches, all

    //
    branches should have it.

    //
    * `necessary`: forces no braces on any `if` and `else`

    //
    branches in the whole codebase except where

    //
    necessary for multi-statement branches.

    //
    * `never`: forces no braces on any `if` and `else`

    //
    branches in the whole codebase.

    View full-size slide

  103. BracesOnWhenStatements
    when (a) {

    1
    ->
    { f1() }

    2
    ->
    f2()

    }
    Extra braces exist on this
    branch, remove them.

    View full-size slide

  104. fun foo(bar: Any?) {

    val x = bar as String

    }

    CastNullableToNonNullableType
    Use separate `null` assertion and type cast like
    ((bar
    ? :
    error("null assertion message")) as String)
    instead of 'bar as String'

    View full-size slide

  105. DoubleNegativeLambda
    fun Int.evenOrNull() =

    takeUnless { it % 2
    !=
    0 }

    fun Int.evenOrNull() =

    takeIf { it % 2
    ==
    0 }

    DON’T
    DO

    View full-size slide

  106. PropertyUsedBeforeDeclaration
    class Example {

    val number

    get() = if (isValid) true else false

    val list = listOf(number)

    val isValid = true

    init {

    println(list)
    /
    /
    [false]

    }

    }

    class Example {

    val isValid = true

    val number

    get() = if (isValid) true else false

    val list = listOf(number)

    init {

    println(list)
    /
    /
    [true]

    }

    }

    DON’T DO

    View full-size slide

  107. suspend fun foo() {

    runCatching {

    delay(500)

    }

    }

    SuspendFunSwallowedCancellation
    The `runCatching` has suspend call inside. You should
    either use specif
    i
    c `try
    -
    catch` only catching exception
    that you are expecting or rethrow the
    `CancellationException` if already caught

    View full-size slide

  108. UseLet
    if (x
    !=
    null) { x.inc() } else null

    if (x
    ==
    null) null else y

    x
    ?.
    let { it.inc() }

    x
    ?.
    let { y }

    DON’T
    DO

    View full-size slide

  109. UnnecessaryBracesAroundTrailingLambda
    fun main() {

    repeat(10, {

    println(it)

    })

    }
    fun main() {

    repeat(10) {

    println(it)

    }

    }
    DON’T DO

    View full-size slide