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

Detekt - State of the Union

Detekt - State of the Union

Do you know detekt? We are The static analyzer for Kotlin, trusted by millions of developers around the globe, and completely built by the open-source community.

Our mission: spotting bugs, anti-patterns, and potential errors in your Kotlin code.

Did you know that you can extend detekt with your custom rules?
Did you know that we recently launched support for Kotlin Multiplatform?
Did you know that we're always looking for new contributors?

Whether you're new to detekt or you're a veteran, this talk is for you!
We will walk through the tool, and give you updates on the latest features we shipped and what comes next for the future.

Nicola Corti

June 09, 2021
Tweet

More Decks by Nicola Corti

Other Decks in Technology

Transcript

  1. State of the Union
    Nicola Corti
    @cortinico

    View Slide

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

    View Slide

  3. What is ?

    View Slide

  4. What is ?

    View Slide

  5. What is ?

    View Slide

  6. 220 Rules

    View Slide

  7. Rulesets

    View Slide

  8. Rulesets
    • comments AbsentOrWrongFileLicense
    CommentOverPrivateFunction
    CommentOverPrivateProperty
    DeprecatedBlockTag
    EndOfSentenceFormat
    UndocumentedPublicClass
    UndocumentedPublicFunction
    UndocumentedPublicProperty

    View Slide

  9. Rulesets
    • comments
    • complexity
    ComplexCondition
    ComplexInterface
    ComplexMethod
    LabeledExpression
    LargeClass
    LongMethod
    LongParameterList
    MethodOverloading
    NamedArguments
    NestedBlockDepth
    ReplaceSafeCallChainWithRun
    StringLiteralDuplication
    TooManyFunctions

    View Slide

  10. Rulesets
    • comments
    • complexity
    • coroutines
    GlobalCoroutineUsage
    RedundantSuspendModifier
    SleepInsteadOfDelay
    SuspendFunWith"FlowReturnType

    View Slide

  11. Rulesets
    • comments
    • complexity
    • coroutines
    • empty-blocks
    EmptyCatchBlock
    EmptyClassBlock
    EmptyDefaultConstructor
    EmptyDoWhileBlock
    EmptyElseBlock
    EmptyFinallyBlock
    EmptyForBlock
    EmptyFunctionBlock
    EmptyIfBlock
    EmptyInitBlock
    EmptyKtFile
    EmptySecondaryConstructor
    EmptyTryBlock
    EmptyWhenBlock
    EmptyWhileBlock

    View Slide

  12. Rulesets
    • comments
    • complexity
    • coroutines
    • empty-blocks
    • exceptions
    ExceptionRaisedInUnexpectedLocation
    InstanceOfCheckForException
    NotImplementedDeclaration
    ObjectExtendsThrowable
    PrintStackTrace
    RethrowCaughtException
    ReturnFromFinally
    SwallowedException
    ThrowingExceptionFromFinally
    ThrowingExceptionInMain
    ThrowingExceptionsWithoutMessage
    ThrowingNewInstanceOfSameException
    TooGenericExceptionCaught
    TooGenericExceptionThrown

    View Slide

  13. Rulesets
    • comments
    • complexity
    • coroutines
    • empty-blocks
    • exceptions
    • naming
    ClassNaming
    ConstructorParameterNaming
    EnumNaming
    ForbiddenClassName
    FunctionMaxLength
    FunctionMinLength
    FunctionNaming
    FunctionParameterNaming
    InvalidPackageDeclaration
    MatchingDeclarationName
    MemberNameEqualsClassName
    NoNameShadowing
    NonBooleanPropertyPrefixedWithIs
    ObjectPropertyNaming
    PackageNaming
    TopLevelPropertyNaming
    VariableMaxLength
    VariableMinLength
    VariableNaming

    View Slide

  14. Rulesets
    • comments
    • complexity
    • coroutines
    • empty-blocks
    • exceptions
    • naming
    • performance
    ArrayPrimitive
    ForEachOnRange
    SpreadOperator
    UnnecessaryTemporaryInstantiation

    View Slide

  15. Rulesets
    • comments
    • complexity
    • coroutines
    • empty-blocks
    • exceptions
    • naming
    • performance
    • potential-bugs
    CastToNullableType
    Deprecation
    DontDowncastCollectionTypes
    DoubleMutabilityForCollection
    DuplicateCaseInWhenExpression
    EqualsAlwaysReturnsTrueOrFalse
    EqualsWithHashCodeExist
    ExitOutsideMain
    ExplicitGarbageCollectionCall
    HasPlatformType
    IgnoredReturnValue
    ImplicitDefaultLocale
    ImplicitUnitReturnType
    InvalidRange
    IteratorHasNextCallsNextMethod
    IteratorNotThrowingNoSuchElementException
    LateinitUsage
    MapGetWithNotNullAssertionOperator
    MissingWhenCase
    NullableToStringCall
    RedundantElseInWhen
    UnconditionalJumpStatementInLoop
    UnnecessaryNotNullOperator
    UnnecessarySafeCall
    UnreachableCatchBlock
    UnreachableCode
    UnsafeCallOnNullableType
    UnsafeCast
    UnusedUnaryOperator
    UselessPostfixExpression
    WrongEqualsTypeParameter

    View Slide

  16. Rulesets
    • comments
    • complexity
    • coroutines
    • empty-blocks
    • exceptions
    • naming
    • performance
    • potential-bugs
    • style
    ClassOrdering
    CollapsibleIfStatements
    DataClassContainsFunctions
    DataClassShouldBeImmutable
    DestructuringDeclarationWithTooManyEntries
    EqualsNullCall
    EqualsOnSignatureLine
    ExplicitCollectionElementAccessMethod
    ExplicitItLambdaParameter
    ExpressionBodySyntax
    ForbiddenComment
    ForbiddenImport
    ForbiddenMethodCall
    ForbiddenPublicDataClass
    ForbiddenVoid
    FunctionOnlyReturningConstant
    LibraryCodeMustSpecifyReturnType
    LibraryEntitiesShouldNotBePublic
    LoopWithTooManyJumpStatements
    MagicNumber
    MandatoryBracesIfStatements
    MandatoryBracesLoops
    MaxLineLength
    MayBeConst
    ModifierOrder
    MultilineLambdaItParameter
    NestedClassesVisibility
    NewLineAtEndOfFile
    NoTabs
    ObjectLiteralToLambda
    OptionalAbstractKeyword
    OptionalUnit
    OptionalWhenBraces
    PreferToOverPairSyntax
    ProtectedMemberInFinalClass
    RedundantExplicitType
    RedundantHigherOrderMapUsage
    RedundantVisibilityModifierRule
    ReturnCount
    SafeCast
    SerialVersionUIDInSerializableClass
    SpacingBetweenPackageAndImports
    ThrowsCount
    TrailingWhitespace
    UnderscoresInNumericLiterals
    UnnecessaryAbstractClass
    UnnecessaryAnnotationUseSiteTarget
    UnnecessaryApply
    UnnecessaryFilter
    UnnecessaryInheritance
    UnnecessaryLet
    UnnecessaryParentheses
    UntilInsteadOfRangeTo
    UnusedImports
    UnusedPrivateClass
    UnusedPrivateMember
    UseArrayLiteralsInAnnotations
    UseCheckNotNull
    UseCheckOrError
    UseDataClass
    UseEmptyCounterpart
    UseIfEmptyOrIfBlank
    UseIfInsteadOfWhen
    UseIsNullOrEmpty
    UseOrEmpty
    UseRequire
    UseRequireNotNull
    UselessCallOnNotNull
    UtilityClassWithPublicConstructor
    VarCouldBeVal
    WildcardImport

    View Slide

  17. UnusedPrivateMember

    View Slide

  18. UnusedPrivateMember
    class Sample {
    private val aMember = "^^"
    private val anotherMember = "U.U"
    }

    View Slide

  19. UnusedPrivateMember
    class Sample {
    private val aMember = "^^"
    private val anotherMember = "U.U"
    fun main() {
    println(aMember)
    }
    }
    Private property
    `anotherMember` is unused.

    View Slide

  20. MagicNumber

    View Slide

  21. MagicNumber
    fun main() {
    }

    View Slide

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

    View Slide

  23. Documentation

    View Slide

  24. Documentation
    /**
    * This rule detects and reports usages of magic numbers in the code.
    * Prefer defining constants with clear names
    * describing what the magic number means.
    *

    View Slide

  25. Documentation
    /**
    * This rule detects and reports usages of magic numbers in the code.
    * Prefer defining constants with clear names
    * describing what the magic number means.
    *
    *
    * class User {
    *
    * fun checkName(name: String) {
    * if (name.length > 42) {
    * throw IllegalArgumentException("username is too long")
    * }
    * // ...
    * }
    * }
    *

    View Slide

  26. Documentation
    /**
    * This rule detects and reports usages of magic numbers in the code.
    * Prefer defining constants with clear names
    * describing what the magic number means.
    *
    *
    * class User {
    *
    * fun checkName(name: String) {
    * if (name.length > 42) {
    * throw IllegalArgumentException("username is too long")
    * }
    * // ...
    * }
    * }
    *

    View Slide

  27. * if (name.length > 42) {
    * throw IllegalArgumentException("username is too long")
    * }
    * // ...
    * }
    * }
    *
    *
    *
    *
    * class User {
    *
    * fun checkName(name: String) {
    * if (name.length > MAX_USERNAME_SIZE) {
    * throw IllegalArgumentException("username is too long")
    * }
    * // ...
    * }
    *
    * companion object {
    * private const val MAX_USERNAME_SIZE = 42
    * }
    * }
    *
    */

    View Slide

  28. * }
    * }
    *
    *
    *
    *
    * class User {
    *
    * fun checkName(name: String) {
    * if (name.length > MAX_USERNAME_SIZE) {
    * throw IllegalArgumentException("username is too long")
    * }
    * // ...
    * }
    *
    * companion object {
    * private const val MAX_USERNAME_SIZE = 42
    * }
    * }
    *
    */
    @Suppress("TooManyFunctions")
    @ActiveByDefault(since = "1.0.0")
    class MagicNumber(config: Config = Config.empty) : Rule(config) {

    View Slide

  29. * }
    * }
    *
    *
    *
    *
    * class User {
    *
    * fun checkName(name: String) {
    * if (name.length > MAX_USERNAME_SIZE) {
    * throw IllegalArgumentException("username is too long")
    * }
    * // ...
    * }
    *
    * companion object {
    * private const val MAX_USERNAME_SIZE = 42
    * }
    * }
    *
    */
    @Suppress("TooManyFunctions")
    @ActiveByDefault(since = "1.0.0")
    class MagicNumber(config: Config = Config.empty) : Rule(config) {

    View Slide

  30. Documentation

    View Slide

  31. Documentation

    View Slide

  32. Documentation

    View Slide

  33. Documentation

    View Slide

  34. detekt.yml

    View Slide

  35. build:
    maxIssues: 0
    excludeCorrectable: false
    weights:
    # complexity: 2
    # LongParameterList: 1
    # style: 1
    # comments: 1
    config:
    validation: true
    warningsAsErrors: false
    # when writing own rules with new properties, exclude the property path e.g.:
    'my_rule_set,."*>."*>[my_property]'
    excludes: ''
    processors:
    active: true
    exclude:
    - 'DetektProgressListener'
    # - 'KtFileCountProcessor'
    # - 'PackageCountProcessor'
    # - 'ClassCountProcessor'
    # - 'FunctionCountProcessor'
    # - 'PropertyCountProcessor'
    detekt.yml

    View Slide

  36. excludes: ['"**']
    LibraryEntitiesShouldNotBePublic:
    active: true
    excludes: ['"**']
    LoopWithTooManyJumpStatements:
    active: true
    maxJumpCount: 1
    MagicNumber:
    active: true
    excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/
    iosTest/**']
    ignoreNumbers: ['-1', '0', '1', '2']
    ignoreHashCodeFunction: true
    ignorePropertyDeclaration: false
    ignoreLocalVariableDeclaration: false
    ignoreConstantDeclaration: true
    ignoreCompanionObjectPropertyDeclaration: true
    ignoreAnnotation: false
    ignoreNamedArgument: true
    ignoreEnums: false
    ignoreRanges: false
    ignoreExtensionFunctions: true
    MandatoryBracesIfStatements:
    active: false
    MandatoryBracesLoops:
    active: false
    detekt.yml

    View Slide

  37. excludes: ['"**']
    LibraryEntitiesShouldNotBePublic:
    active: true
    excludes: ['"**']
    LoopWithTooManyJumpStatements:
    active: true
    maxJumpCount: 1
    MagicNumber:
    active: true
    excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/
    iosTest/**']
    ignoreNumbers: ['-1', '0', '1', '2']
    ignoreHashCodeFunction: true
    ignorePropertyDeclaration: false
    ignoreLocalVariableDeclaration: false
    ignoreConstantDeclaration: true
    ignoreCompanionObjectPropertyDeclaration: true
    ignoreAnnotation: false
    ignoreNamedArgument: true
    ignoreEnums: false
    ignoreRanges: false
    ignoreExtensionFunctions: true
    MandatoryBracesIfStatements:
    active: false
    MandatoryBracesLoops:
    active: false
    detekt.yml

    View Slide

  38. UnnecessaryNotNullOperator

    View Slide

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

    View Slide

  40. UnnecessaryNotNullOperator
    val zero = 0
    val answer = zero""!!.plus(42)

    View Slide

  41. UnnecessaryNotNullOperator
    val zero = 0
    val answer = zero""!!.plus(42)
    : Int

    View Slide

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

    View Slide

  43. DoubleMutabilityForCollection

    View Slide

  44. DoubleMutabilityForCollection
    fun main() {
    var hobbits = mutableListOf("Frodo", "Sam", "Merry", "Pippin")
    }
    Variable `hobbits` is declared as `var` with a mutable type
    `kotlin.collections.MutableList`.
    Consider using `val` or an immutable collection type

    View Slide

  45. ForbiddenMethodCall

    View Slide

  46. ForbiddenMethodCall
    style:
    ForbiddenMethodCall:
    active: false
    methods: ['kotlin.io.println', 'kotlin.io.print']

    View Slide

  47. ForbiddenMethodCall
    style:
    ForbiddenMethodCall:
    active: true
    methods: ['kotlin.io.println', 'kotlin.io.print']

    View Slide

  48. ForbiddenMethodCall
    class Sample {
    private val aMember = "^^"
    private val anotherMember = "U.U"
    }
    style:
    ForbiddenMethodCall:
    active: true
    methods: ['kotlin.io.println', 'kotlin.io.print']

    View Slide

  49. ForbiddenMethodCall
    class Sample {
    private val aMember = "^^"
    private val anotherMember = "U.U"
    fun main() {
    println(aMember)
    }
    }
    style:
    ForbiddenMethodCall:
    active: true
    methods: ['kotlin.io.println', 'kotlin.io.print']
    The method kotlin.io.println(String)
    has been forbidden in the Detekt config.

    View Slide

  50. Suppressing

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  54. Suppressing
    @file:Suppress(“MagicNumber”)
    fun main() {
    if (Random.nextInt() > 42) {
    println("🐋")
    }
    }

    View Slide

  55. Detekt CLI

    View Slide

  56. Custom Rules

    View Slide

  57. • Ktlint Wrapper
    • Offered as
    Custom rules
    (formatting)
    • Only rules offering
    autoCorrection
    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 Slide

  58. • Ktlint Wrapper
    • Offered as
    Custom rules
    (formatting)
    • Only rules offering
    autoCorrection
    formatting:
    android: false
    autoCorrect: true
    AnnotationOnSeparateLine:
    AnnotationSpacing:
    ArgumentListWrapping:
    ChainWrapping:
    CommentSpacing:
    EnumEntryNameCase:
    Filename:
    FinalNewline:
    ImportOrdering:

    View Slide

  59. formatting:
    active: true
    NoWildcardImports:
    active: true
    NoUnusedImports:
    active: true
    style:
    WildcardImport:
    active: true
    excludeImports: [‘java.util.*']
    UnusedImports:
    active: false

    View Slide

  60. formatting:
    active: true
    NoWildcardImports:
    active: true
    NoUnusedImports:
    active: true
    style:
    WildcardImport:
    active: true
    excludeImports: [‘java.util.*']
    UnusedImports:
    active: false

    View Slide

  61. formatting:
    active: true
    NoWildcardImports:
    active: true
    NoUnusedImports:
    active: true
    autoCorrect: true
    style:
    WildcardImport:
    active: true
    excludeImports: [‘java.util.*']
    UnusedImports:
    active: false

    View Slide

  62. Custom Rules

    View Slide

  63. Custom Rules
    class MyCustomRuleSetProvider : RuleSetProvider {
    override val ruleSetId = "my-rules"
    override fun instance(config: Config) = RuleSet(
    ruleSetId,
    listOf(MyCustomRule(config))
    )
    }
    com.example.MyCustomRuleSetProvider
    class MyCustomRule(
    config: Config = Config.empty
    ) : Rule(config) {
    override val issue = Issue(
    "//…
    )
    }

    View Slide

  64. Custom Rules

    View Slide

  65. Integrations

    View Slide

  66. View Slide

  67. View Slide

  68. SARIF - Static Analysis Result Interchange Format
    https://sarifweb.azurewebsites.net/

    View Slide

  69. View Slide

  70. View Slide

  71. View Slide

  72. View Slide

  73. View Slide

  74. Type Resolution

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  78. Rules w/ Type Resolution

    View Slide

  79. Rules w/ Type Resolution
    /**
    * Reports unnecessary not-null operator usage ("!!) that can be removed by the user.
    *
    *
    * val a = 1
    * val b = a"!!
    * "
    *
    *
    * val a = 1
    * val b = a
    * "
    "*/
    @RequiresTypeResolution
    @ActiveByDefault(since = "1.16.0")
    class UnnecessaryNotNullOperator(config: Config = Config.empty) : Rule(config) {

    View Slide

  80. Rules w/ Type Resolution
    @ActiveByDefault(since = "1.16.0")
    class UnnecessaryNotNullOperator(config: Config = Config.empty) : Rule(config) {
    override val issue: Issue = Issue(
    "UnnecessaryNotNullOperator",
    Severity.Defect,
    "Unnecessary not-null unary operator ("!!) detected.",
    Debt.FIVE_MINS
    )
    override fun visitUnaryExpression(expression: KtUnaryExpression) {
    super.visitUnaryExpression(expression)
    if (bindingContext "== BindingContext.EMPTY) return
    "// Type Resolution is enabled here
    }
    }

    View Slide

  81. Rules w/ Type Resolution
    @ActiveByDefault(since = "1.16.0")
    class UnnecessaryNotNullOperator(config: Config = Config.empty) : Rule(config) {
    override val issue: Issue = Issue(
    "UnnecessaryNotNullOperator",
    Severity.Defect,
    "Unnecessary not-null unary operator ("!!) detected.",
    Debt.FIVE_MINS
    )
    override fun visitUnaryExpression(expression: KtUnaryExpression) {
    super.visitUnaryExpression(expression)
    if (bindingContext "== BindingContext.EMPTY) return
    "// Type Resolution is enabled here
    }
    }

    View Slide

  82. Rules w/ Type Resolution

    View Slide

  83. Using Type Resolution

    View Slide

  84. Using Type Resolution

    View Slide

  85. Using Type Resolution

    View Slide

  86. Gradle

    View Slide

  87. View Slide

  88. plugins {
    id("io.gitlab.arturbosch.detekt") version "1.17.1"
    }

    View Slide

  89. plugins {
    id("io.gitlab.arturbosch.detekt") version "1.17.1"
    }
    repositories {
    mavenCentral()
    }

    View Slide

  90. plugins {
    id("io.gitlab.arturbosch.detekt") version "1.17.1"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    config = files(“config/detekt/detekt.yml”)
    }

    View Slide

  91. plugins {
    id("io.gitlab.arturbosch.detekt") version "1.17.1"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    config = files(“config/detekt/detekt.yml”)
    }
    $ ./gradlew tasks | grep detekt
    detekt
    detektBaseline
    detektGenerateConfig

    View Slide

  92. plugins {
    id("io.gitlab.arturbosch.detekt") version "1.17.1"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    config = files(“config/detekt/detekt.yml”)
    }
    $ ./gradlew tasks | grep detekt
    detekt
    detektBaseline
    detektGenerateConfig

    View Slide

  93. plugins {
    id("io.gitlab.arturbosch.detekt") version "1.17.1"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    config = files(“config/detekt/detekt.yml”)
    }
    $ ./gradlew tasks | grep detekt
    detekt
    detektBaseline
    detektGenerateConfig

    View Slide

  94. plugins {
    id("io.gitlab.arturbosch.detekt") version "1.17.1"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    config = files(“config/detekt/detekt.yml”)
    }
    $ ./gradlew tasks | grep detekt
    detekt
    detektBaseline
    detektGenerateConfig

    View Slide

  95. plugins {
    kotlin("jvm")
    id("io.gitlab.arturbosch.detekt") version "1.17.1"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    config = files(“config/detekt/detekt.yml”)
    }

    View Slide

  96. $ ./gradlew tasks | grep detekt
    detekt
    detektBaseline
    detektBaselineMain
    detektBaselineTest
    detektGenerateConfig
    detektMain
    detektTest
    plugins {
    kotlin("jvm")
    id("io.gitlab.arturbosch.detekt") version "1.17.1"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    config = files(“config/detekt/detekt.yml”)
    }

    View Slide

  97. $ ./gradlew tasks | grep detekt
    detekt
    detektBaseline
    detektBaselineMain
    detektBaselineTest
    detektGenerateConfig
    detektMain
    detektTest
    plugins {
    kotlin("jvm")
    id("io.gitlab.arturbosch.detekt") version "1.17.1"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    config = files(“config/detekt/detekt.yml”)
    }

    View Slide

  98. $ ./gradlew tasks | grep detekt
    detekt
    detektBaseline
    detektBaselineMain
    detektBaselineTest
    detektGenerateConfig
    detektMain
    detektTest
    plugins {
    kotlin("jvm")
    id("io.gitlab.arturbosch.detekt") version "1.17.1"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    config = files(“config/detekt/detekt.yml”)
    }

    View Slide

  99. View Slide

  100. plugins {
    kotlin(“android")
    id(“com.android.application”)
    id("io.gitlab.arturbosch.detekt") version "1.17.1"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    config = files(“config/detekt/detekt.yml”)
    }

    View Slide

  101. plugins {
    kotlin(“android")
    id(“com.android.application”)
    id("io.gitlab.arturbosch.detekt") version "1.17.1"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    config = files(“config/detekt/detekt.yml”)
    }
    $ ./gradlew tasks | grep detekt
    app:detekt
    app:detektBaseline
    app:detektBaselineDebug
    app:detektBaselineDebugAndroidTest
    app:detektBaselineDebugUnitTest
    app:detektBaselineMain
    app:detektBaselineRelease
    app:detektBaselineReleaseUnitTest
    app:detektBaselineTest
    app:detektDebug
    app:detektDebugAndroidTest
    app:detektDebugUnitTest
    app:detektGenerateConfig
    app:detektMain
    app:detektRelease
    app:detektReleaseUnitTest
    app:detektTest

    View Slide

  102. plugins {
    kotlin(“android")
    id(“com.android.application”)
    id("io.gitlab.arturbosch.detekt") version "1.17.1"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    config = files(“config/detekt/detekt.yml”)
    }
    $ ./gradlew tasks | grep detekt
    app:detekt
    app:detektBaseline
    app:detektBaselineDebug
    app:detektBaselineDebugAndroidTest
    app:detektBaselineDebugUnitTest
    app:detektBaselineMain
    app:detektBaselineRelease
    app:detektBaselineReleaseUnitTest
    app:detektBaselineTest
    app:detektDebug
    app:detektDebugAndroidTest
    app:detektDebugUnitTest
    app:detektGenerateConfig
    app:detektMain
    app:detektRelease
    app:detektReleaseUnitTest
    app:detektTest

    View Slide

  103. plugins {
    kotlin(“android")
    id(“com.android.application”)
    id("io.gitlab.arturbosch.detekt") version "1.17.1"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    config = files(“config/detekt/detekt.yml”)
    }
    $ ./gradlew tasks | grep detekt
    app:detekt
    app:detektBaseline
    app:detektBaselineDebug
    app:detektBaselineDebugAndroidTest
    app:detektBaselineDebugUnitTest
    app:detektBaselineMain
    app:detektBaselineRelease
    app:detektBaselineReleaseUnitTest
    app:detektBaselineTest
    app:detektDebug
    app:detektDebugAndroidTest
    app:detektDebugUnitTest
    app:detektGenerateConfig
    app:detektMain
    app:detektRelease
    app:detektReleaseUnitTest
    app:detektTest

    View Slide

  104. plugins {
    kotlin(“android")
    id(“com.android.application”)
    id("io.gitlab.arturbosch.detekt") version "1.17.1"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    config = files(“config/detekt/detekt.yml”)
    }
    $ ./gradlew tasks | grep detekt
    app:detekt
    app:detektBaseline
    app:detektBaselineDebug
    app:detektBaselineDebugAndroidTest
    app:detektBaselineDebugUnitTest
    app:detektBaselineMain
    app:detektBaselineRelease
    app:detektBaselineReleaseUnitTest
    app:detektBaselineTest
    app:detektDebug
    app:detektDebugAndroidTest
    app:detektDebugUnitTest
    app:detektGenerateConfig
    app:detektMain
    app:detektRelease
    app:detektReleaseUnitTest
    app:detektTest

    View Slide

  105. Multiplatform

    View Slide

  106. Multiplatform
    plugins {
    kotlin(“multiplatform")
    id("io.gitlab.arturbosch.detekt") version "1.17.1"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    config = files(“config/detekt/detekt.yml”)
    }

    View Slide

  107. Multiplatform
    plugins {
    kotlin(“multiplatform")
    id("io.gitlab.arturbosch.detekt") version "1.17.1"
    }
    repositories {
    mavenCentral()
    }
    detekt {
    config = files(“config/detekt/detekt.yml”)
    }
    kotlin {
    jvm()
    android()
    ios()
    js()
    }

    View Slide

  108. 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
    detektGenerateConfig
    detektIosArm64Main
    detektIosArm64Test
    detektIosX64Main
    detektIosX64Test
    detektJsMain
    detektJsTest
    detektJvmMain
    detektJvmTest
    detektMetadataCommonMain
    detektMetadataIosMain
    detektMetadataMain

    View Slide

  109. The Future

    View Slide

  110. ./gradlew check
    detekt
    (no Type Resolution)
    compileKotlin

    View Slide

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

    View Slide

  112. detekt-compiler-plugin

    View Slide

  113. detekt-compiler-plugin
    plugins {
    kotlin("jvm")
    id("io.gitlab.arturbosch.detekt") version “…”
    }
    repositories {
    mavenCentral()
    }
    detekt {
    config = files(“config/detekt/detekt.yml”)
    }

    View Slide

  114. detekt-compiler-plugin
    plugins {
    kotlin("jvm")
    id("io.github.detekt.gradle.compiler-plugin") version “…”
    }
    repositories {
    mavenCentral()
    }
    detekt {
    config = files(“config/detekt/detekt.yml”)
    }

    View Slide

  115. compileKotlin Runs detekt during the
    compilation
    ./gradlew check
    • No more double compilation
    • Types Resolution close to the
    production code
    • No more custom tasks for every
    source set

    View Slide

  116. detekt-compiler-plugin

    View Slide

  117. detekt-compiler-plugin
    $ ./gradlew assemble

    View Slide

  118. detekt-compiler-plugin
    $ ./gradlew assemble
    > Task :compileKotlin FAILED
    w: Build failed with 1 weighted issues.
    e: warnings found and -Werror specified
    w: /Users/ncorti/oss/detekt-compiler-plugin/src/main/java/Sample.kt: (3, 21):
    MagicNumber: This expression contains a magic number. Consider defining 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

    View Slide

  119. detekt-compiler-plugin
    $ ./gradlew assemble
    > Task :compileKotlin FAILED
    w: Build failed with 1 weighted issues.
    e: warnings found and -Werror specified
    w: /Users/ncorti/oss/detekt-compiler-plugin/src/main/java/Sample.kt: (3, 21):
    MagicNumber: This expression contains a magic number. Consider defining 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

    View Slide

  120. The Community
    ❤

    View Slide

  121. Where is ?
    • github.com/detekt/detekt
    • #detekt on KotlinLang
    • detekt.github.io/
    • Discussions & Github Issues

    View Slide

  122. • Search for
    • Update documentation
    • Add more tests
    • Submit new rules ideas
    • Fix false positives/false negatives
    • Help with integrations
    • Test new versions (-SNAPSHOT)
    • Help with branding
    How to ?

    View Slide

  123. Who is ?

    View Slide

  124. View Slide

  125. Thank you
    &
    Contribs are Welcome
    🙏
    Nicola Corti
    @cortinico

    View Slide