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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  8. 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 full-size slide

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

    View full-size slide

  10. 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 full-size slide

  11. 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 full-size slide

  12. UnusedPrivateMember

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  15. MagicNumber
    fun main() {
    }

    View full-size slide

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

    View full-size slide

  17. Documentation

    View full-size slide

  18. 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 full-size slide

  19. 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 full-size slide

  20. 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 full-size slide

  21. * 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 full-size slide

  22. * }
    * }
    *
    *
    *
    *
    * 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 full-size slide

  23. * }
    * }
    *
    *
    *
    *
    * 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 full-size slide

  24. Documentation

    View full-size slide

  25. Documentation

    View full-size slide

  26. Documentation

    View full-size slide

  27. Documentation

    View full-size slide

  28. 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 full-size slide

  29. 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 full-size slide

  30. 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 full-size slide

  31. UnnecessaryNotNullOperator

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  36. DoubleMutabilityForCollection

    View full-size slide

  37. 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 full-size slide

  38. ForbiddenMethodCall

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  42. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  47. Custom Rules

    View full-size slide

  48. • 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 full-size slide

  49. • 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  53. Custom Rules

    View full-size slide

  54. 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 full-size slide

  55. Custom Rules

    View full-size slide

  56. Integrations

    View full-size slide

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

    View full-size slide

  58. Type Resolution

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  61. 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 full-size slide

  62. Rules w/ Type Resolution

    View full-size slide

  63. 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 full-size slide

  64. 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 full-size slide

  65. 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 full-size slide

  66. Rules w/ Type Resolution

    View full-size slide

  67. Using Type Resolution

    View full-size slide

  68. Using Type Resolution

    View full-size slide

  69. Using Type Resolution

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  73. 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 full-size slide

  74. 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 full-size slide

  75. 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 full-size slide

  76. 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 full-size slide

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

    View full-size slide

  78. $ ./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 full-size slide

  79. $ ./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 full-size slide

  80. $ ./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 full-size slide

  81. 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 full-size slide

  82. 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 full-size slide

  83. 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 full-size slide

  84. 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 full-size slide

  85. 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 full-size slide

  86. Multiplatform

    View full-size slide

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

    View full-size slide

  88. 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 full-size slide

  89. 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 full-size slide

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

    View full-size slide

  91. 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 full-size slide

  92. detekt-compiler-plugin

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  95. 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 full-size slide

  96. detekt-compiler-plugin

    View full-size slide

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

    View full-size slide

  98. 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 full-size slide

  99. 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 full-size slide

  100. The Community

    View full-size slide

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

    View full-size slide

  102. • 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 full-size slide

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

    View full-size slide