Slide 1

Slide 1 text

State of the Union Nicola Corti @cortinico

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

What is ?

Slide 4

Slide 4 text

What is ?

Slide 5

Slide 5 text

What is ?

Slide 6

Slide 6 text

220 Rules

Slide 7

Slide 7 text

Rulesets

Slide 8

Slide 8 text

Rulesets • comments AbsentOrWrongFileLicense CommentOverPrivateFunction CommentOverPrivateProperty DeprecatedBlockTag EndOfSentenceFormat UndocumentedPublicClass UndocumentedPublicFunction UndocumentedPublicProperty

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

UnusedPrivateMember

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

MagicNumber

Slide 21

Slide 21 text

MagicNumber fun main() { }

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

Documentation

Slide 24

Slide 24 text

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. *

Slide 25

Slide 25 text

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") * } * // ... * } * } *

Slide 26

Slide 26 text

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") * } * // ... * } * } *

Slide 27

Slide 27 text

* 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 * } * } * */

Slide 28

Slide 28 text

* } * } * * * * * 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) {

Slide 29

Slide 29 text

* } * } * * * * * 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) {

Slide 30

Slide 30 text

Documentation

Slide 31

Slide 31 text

Documentation

Slide 32

Slide 32 text

Documentation

Slide 33

Slide 33 text

Documentation

Slide 34

Slide 34 text

detekt.yml

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

UnnecessaryNotNullOperator

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

DoubleMutabilityForCollection

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

ForbiddenMethodCall

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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.

Slide 50

Slide 50 text

Suppressing

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

Detekt CLI

Slide 56

Slide 56 text

Custom Rules

Slide 57

Slide 57 text

• 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:

Slide 58

Slide 58 text

• 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:

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

Custom Rules

Slide 63

Slide 63 text

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( "//… ) }

Slide 64

Slide 64 text

Custom Rules

Slide 65

Slide 65 text

Integrations

Slide 66

Slide 66 text

No content

Slide 67

Slide 67 text

No content

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

No content

Slide 70

Slide 70 text

No content

Slide 71

Slide 71 text

No content

Slide 72

Slide 72 text

No content

Slide 73

Slide 73 text

No content

Slide 74

Slide 74 text

Type Resolution

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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()

Slide 78

Slide 78 text

Rules w/ Type Resolution

Slide 79

Slide 79 text

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) {

Slide 80

Slide 80 text

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 } }

Slide 81

Slide 81 text

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 } }

Slide 82

Slide 82 text

Rules w/ Type Resolution

Slide 83

Slide 83 text

Using Type Resolution

Slide 84

Slide 84 text

Using Type Resolution

Slide 85

Slide 85 text

Using Type Resolution

Slide 86

Slide 86 text

Gradle

Slide 87

Slide 87 text

No content

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

$ ./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”) }

Slide 97

Slide 97 text

$ ./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”) }

Slide 98

Slide 98 text

$ ./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”) }

Slide 99

Slide 99 text

No content

Slide 100

Slide 100 text

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”) }

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

Multiplatform

Slide 106

Slide 106 text

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

Slide 107

Slide 107 text

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() }

Slide 108

Slide 108 text

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

Slide 109

Slide 109 text

The Future

Slide 110

Slide 110 text

./gradlew check detekt (no Type Resolution) compileKotlin

Slide 111

Slide 111 text

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.)

Slide 112

Slide 112 text

detekt-compiler-plugin

Slide 113

Slide 113 text

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

Slide 114

Slide 114 text

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

Slide 115

Slide 115 text

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

Slide 116

Slide 116 text

detekt-compiler-plugin

Slide 117

Slide 117 text

detekt-compiler-plugin $ ./gradlew assemble

Slide 118

Slide 118 text

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

Slide 119

Slide 119 text

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

Slide 120

Slide 120 text

The Community ❤

Slide 121

Slide 121 text

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

Slide 122

Slide 122 text

• 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 ?

Slide 123

Slide 123 text

Who is ?

Slide 124

Slide 124 text

No content

Slide 125

Slide 125 text

Thank you & Contribs are Welcome 🙏 Nicola Corti @cortinico