diff --git a/README.md b/README.md index d042adec0ad..ca7f24123fb 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ Gradle 6.7.1+ is the minimum requirement. However, the recommended versions toge | Detekt Version | Gradle | Kotlin | AGP | Java Target Level | JDK Max Version | | -------------- | ------- | -------- | ------- | ----------------- | --------------- | -| `1.21.0` | `7.5` | `1.6.21` | `7.2.1` | `1.8` | `17` | +| `1.22.0` | `7.5.1` | `1.7.21` | `7.3.1` | `1.8` | `17` | The list of [recommended versions for previous detekt version is listed here](https://detekt.dev/compatibility.html). diff --git a/build-logic/src/main/kotlin/Versions.kt b/build-logic/src/main/kotlin/Versions.kt index f66e36b45b6..db6dccb2f36 100644 --- a/build-logic/src/main/kotlin/Versions.kt +++ b/build-logic/src/main/kotlin/Versions.kt @@ -1,6 +1,6 @@ object Versions { - const val DETEKT: String = "1.22.0-RC3" + const val DETEKT: String = "1.22.0" const val SNAPSHOT_NAME: String = "main" const val JVM_TARGET: String = "1.8" diff --git a/website/docs/introduction/compatibility.md b/website/docs/introduction/compatibility.md index 4c66e50c96e..634df0b9652 100644 --- a/website/docs/introduction/compatibility.md +++ b/website/docs/introduction/compatibility.md @@ -28,7 +28,7 @@ Consider **aligning** your Gradle plugin versions with the one listed below, as | Detekt Version | Gradle Version | Kotlin Version | AGP Version | Java Target Level | JDK Version | |----------------|----------------|----------------|-------------|-------------------|-------------| -| `1.22.0` | `7.5.1` | `1.7.20` | `7.3.1` | `1.8` | `17` | +| `1.22.0` | `7.5.1` | `1.7.21` | `7.3.1` | `1.8` | `17` | | `1.21.0` | `7.5` | `1.6.21` | `7.2.1` | `1.8` | `17` | | `1.20.0` | `7.4.2` | `1.6.20` | `7.1.3` | `1.8` | `17` | | `1.19.0` | `7.3.0` | `1.5.31` | `4.2.2` | `1.8` | `17` | diff --git a/website/src/pages/changelog.md b/website/src/pages/changelog.md index dc721ed93eb..862523db951 100644 --- a/website/src/pages/changelog.md +++ b/website/src/pages/changelog.md @@ -6,7 +6,7 @@ keywords: [changelog, release-notes, migration] # Changelog and Migration Guide -#### 1.22.0-RC3 - 2022-11-06 +#### 1.22.0 - 2022-11-20 ##### Notable Changes @@ -47,7 +47,7 @@ keywords: [changelog, release-notes, migration] - `WildcardImport` is now running also on tests by default - [#5121](https://github.com/detekt/detekt/pull/5121) - `ForbiddenImport` allows now to specify a reason for every forbidden import - [#4909](https://github.com/detekt/detekt/pull/4909) - `IgnoredReturnValue`: option `restrictToAnnotatedMethods` is now deprecated in favor of `restrictToConfig` - [#4922](https://github.com/detekt/detekt/pull/4922) -- This version of Detekt is built with Gradle `v7.5.1`, AGP `7.3.1`, Kotlin `1.7.20` and KtLint `0.47.1` (see [#5363](https://github.com/detekt/detekt/pull/5363) [#5189](https://github.com/detekt/detekt/pull/5189) [#5411](https://github.com/detekt/detekt/pull/5411) [#5312](https://github.com/detekt/detekt/pull/5312) +- This version of Detekt is built with Gradle `v7.5.1`, AGP `7.3.1`, Kotlin `1.7.21` and KtLint `0.47.1` (see [#5363](https://github.com/detekt/detekt/pull/5363) [#5189](https://github.com/detekt/detekt/pull/5189) [#5411](https://github.com/detekt/detekt/pull/5411) [#5312](https://github.com/detekt/detekt/pull/5312) [#5519](https://github.com/detekt/detekt/pull/5519)) - The minimum supported Gradle version is now `v6.7.1` - [#4964](https://github.com/detekt/detekt/pull/4964) ##### Migration @@ -132,6 +132,13 @@ formatting: ##### Changelog +- ReturnCount: correctly count assignment expressions with elvis return as guard clauses - [#5539](https://github.com/detekt/detekt/pull/5539) +- UnnecessaryPartOfBinaryExpression: fix false positive with pair creation - [#5516](https://github.com/detekt/detekt/pull/5516) +- False positive at `UnnecessaryPartOfBinaryExpression` - [#5514](https://github.com/detekt/detekt/issues/5514) +- Update documentation for TrailingComma rules - [#5513](https://github.com/detekt/detekt/pull/5513) +- `TrimMultilineRawString` false-positive on annotation parameters - [#5476](https://github.com/detekt/detekt/issues/5476) +- Detekt 1.22.0-RC1 -> 1.22.0-RC2 breaks UnreachableCode - [#5435](https://github.com/detekt/detekt/issues/5435) +- Detekt 1.22.0-RC1 -> 1.22.0-RC2 breaks ignoreAnnotated - [#5427](https://github.com/detekt/detekt/issues/5427) - Fix issues introduced by #5152 - [#5508](https://github.com/detekt/detekt/pull/5508) - MultilineLambdaItParameter: fix false positive for one-line statements with a lambda argument - [#5505](https://github.com/detekt/detekt/pull/5505) - UseArrayLiteralsInAnnotations: fix false negative with primitive array factory calls - [#5482](https://github.com/detekt/detekt/pull/5482) @@ -394,6 +401,7 @@ Because of this, some users might have to **recreate their baseline** as the loc ##### Dependency Updates +- Update plugin binaryCompatibilityValidator to v0.12.0 - [#5456](https://github.com/detekt/detekt/pull/5456) - Update dependency gradle to v7.5 - [#5074](https://github.com/detekt/detekt/pull/5074) - Update plugin binaryCompatibilityValidator to v0.11.0 - [#5069](https://github.com/detekt/detekt/pull/5069) - Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-core to v1.6.3 - [#4976](https://github.com/detekt/detekt/pull/4976) diff --git a/website/src/remark/detektVersionReplace.js b/website/src/remark/detektVersionReplace.js index 30d6b6bb419..eef1d622144 100644 --- a/website/src/remark/detektVersionReplace.js +++ b/website/src/remark/detektVersionReplace.js @@ -3,7 +3,7 @@ const visit = require("unist-util-visit"); // Remark plugin that is replacing the [detekt_version] with the latest // released version. Please note that this field is updated automatically // by the `applyDocVersion` task. -const detektVersion = "1.22.0-RC3"; +const detektVersion = "1.22.0"; const plugin = (options) => { const transformer = async (ast) => { diff --git a/website/versioned_docs/version-1.22.0/gettingstarted/_category_.json b/website/versioned_docs/version-1.22.0/gettingstarted/_category_.json new file mode 100644 index 00000000000..c1998c5e4c8 --- /dev/null +++ b/website/versioned_docs/version-1.22.0/gettingstarted/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Getting Started", + "position": 3 +} diff --git a/website/versioned_docs/version-1.22.0/gettingstarted/_cli-generator-options.md b/website/versioned_docs/version-1.22.0/gettingstarted/_cli-generator-options.md new file mode 100644 index 00000000000..4b49776c0a9 --- /dev/null +++ b/website/versioned_docs/version-1.22.0/gettingstarted/_cli-generator-options.md @@ -0,0 +1,10 @@ +``` +Usage: java -jar detekt-generator-[detekt_version]-all.jar [options] + Options: + --generate-custom-rule-config, -gcrc + Generate custom rules configuration files. The files will be + placed under 'resources' folder of each rule respectively + (e.g. 'custom-rule/src/main/resources/config/config.yml'). + --input, -i + Input paths to rules to analyze. Multiple paths are separated by comma. +``` diff --git a/website/versioned_docs/version-1.22.0/gettingstarted/_cli-options.md b/website/versioned_docs/version-1.22.0/gettingstarted/_cli-options.md new file mode 100644 index 00000000000..ec8fee495a7 --- /dev/null +++ b/website/versioned_docs/version-1.22.0/gettingstarted/_cli-options.md @@ -0,0 +1,89 @@ +``` +Usage: detekt [options] + Options: + --all-rules + Activates all available (even unstable) rules. + Default: false + --auto-correct, -ac + Allow rules to auto correct code if they support it. The default rule + sets do NOT support auto correcting and won't change any line in the + users code base. However custom rules can be written to support auto + correcting. The additional 'formatting' rule set, added with + '--plugins', does support it and needs this flag. + Default: false + --base-path, -bp + Specifies a directory as the base path.Currently it impacts all file + paths in the formatted reports. File paths in console output and txt + report are not affected and remain as absolute paths. + --baseline, -b + If a baseline xml file is passed in, only new code smells not in the + baseline are printed in the console. + --build-upon-default-config + Preconfigures detekt with a bunch of rules and some opinionated defaults + for you. Allows additional provided configurations to override the + defaults. + Default: false + --classpath, -cp + EXPERIMENTAL: Paths where to find user class files and depending jar + files. Used for type resolution. + --config, -c + Path to the config file (path/to/config.yml). Multiple configuration + files can be specified with ',' or ';' as separator. + --config-resource, -cr + Path to the config resource on detekt's classpath (path/to/config.yml). + --create-baseline, -cb + Treats current analysis findings as a smell baseline for future detekt + runs. + Default: false + --debug + Prints extra information about configurations and extensions. + Default: false + --disable-default-rulesets, -dd + Disables default rule sets. + Default: false + --excludes, -ex + Globbing patterns describing paths to exclude from the analysis. + --generate-config, -gc + Export default config. Path can be specified with --config option + (default path: default-detekt-config.yml) + Default: false + --help, -h + Shows the usage. + --includes, -in + Globbing patterns describing paths to include in the analysis. Useful in + combination with 'excludes' patterns. + --input, -i + Input paths to analyze. Multiple paths are separated by comma. If not + specified the current working directory is used. + --jdk-home + EXPERIMENTAL: Use a custom JDK home directory to include into the + classpath + --jvm-target + EXPERIMENTAL: Target version of the generated JVM bytecode that was + generated during compilation and is now being used for type resolution + (1.6, 1.8, 9, 10, 11, 12, 13, 14, 15, 16 or 17) + Default: 1.8 + --language-version + EXPERIMENTAL: Compatibility mode for Kotlin language version X.Y, + reports errors for all language features that came out later + Possible Values: [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9] + --max-issues + Return exit code 0 only when found issues count does not exceed + specified issues count. + --parallel + Enables parallel compilation and analysis of source files. Do some + benchmarks first before enabling this flag. Heuristics show performance + benefits starting from 2000 lines of Kotlin code. + Default: false + --plugins, -p + Extra paths to plugin jars separated by ',' or ';'. + --report, -r + Generates a report for given 'report-id' and stores it on given 'path'. + Entry should consist of: [report-id:path]. Available 'report-id' values: + 'txt', 'xml', 'html', 'md', 'sarif'. These can also be used in + combination with each other e.g. '-r txt:reports/detekt.txt -r + xml:reports/detekt.xml' + --version + Prints the detekt CLI version. + Default: false +``` diff --git a/website/versioned_docs/version-1.22.0/gettingstarted/cli.mdx b/website/versioned_docs/version-1.22.0/gettingstarted/cli.mdx new file mode 100644 index 00000000000..981f0eedcb1 --- /dev/null +++ b/website/versioned_docs/version-1.22.0/gettingstarted/cli.mdx @@ -0,0 +1,61 @@ +--- +title: "Run detekt using Command Line Interface" +keywords: [cli] +sidebar: +permalink: cli.html +folder: gettingstarted +summary: +sidebar_position: 1 +--- + +import CliOptions from "./_cli-options.md"; +import CliGeneratorOptions from "./_cli-generator-options.md"; + +## Install the cli + +There are different ways to install the Command Line Interface (CLI): + +### MacOS, with [Homebrew](https://brew.sh/): + +```sh +brew install detekt +detekt [options] +``` + +### Windows, with [Scoop](https://scoop.sh/) + +```powershell +scoop install detekt +detekt [options] +``` + +### Any OS: + +```sh +curl -sSLO https://github.com/detekt/detekt/releases/download/v[detekt_version]/detekt-cli-[detekt_version].zip +unzip detekt-cli-[detekt_version].zip +./detekt-cli-[detekt_version]/bin/detekt-cli --help +``` + +## Use the cli + +detekt will exit with one of the following exit codes: + +| Exit code | Description | +| --------- | ------------------------------------------------------------------------------ | +| 0 | detekt ran normally and maxIssues count was not reached in BuildFailureReport. | +| 1 | An unexpected error occurred | +| 2 | MaxIssues count was reached in BuildFailureReport. | +| 3 | Invalid detekt configuration file detected. | + +The following parameters are shown when `--help` is entered. + + + +## Use the cli to generate configuration for custom rules + + + +```sh +java -jar detekt-generator-[detekt_version]-all.jar -gcrc -i /path/to/rule1, /path/to/rule2 +``` diff --git a/website/versioned_docs/version-1.22.0/gettingstarted/git-pre-commit-hook.md b/website/versioned_docs/version-1.22.0/gettingstarted/git-pre-commit-hook.md new file mode 100644 index 00000000000..23bfabd5fba --- /dev/null +++ b/website/versioned_docs/version-1.22.0/gettingstarted/git-pre-commit-hook.md @@ -0,0 +1,82 @@ +--- +title: "Run detekt using a Git pre-commit hook" +keywords: [detekt, static, analysis, code, kotlin] +sidebar: +permalink: git-pre-commit-hook.html +folder: gettingstarted +summary: +sidebar_position: 6 +--- + +Detekt can be integrated into your development workflow by using a Git pre-commit hook. +For that reason Git supports to run custom scripts automatically, when a specific action occurs. +The mentioned pre-commit hook can be setup locally on your dev-machine. +The following client-side detekt hook is triggered by a commit operation, and checks all files via the gradle task. + +```bash +#!/usr/bin/env bash +echo "Running detekt check..." +OUTPUT="/tmp/detekt-$(date +%s)" +./gradlew detekt > $OUTPUT +EXIT_CODE=$? +if [ $EXIT_CODE -ne 0 ]; then + cat $OUTPUT + rm $OUTPUT + echo "***********************************************" + echo " Detekt failed " + echo " Please fix the above issues before committing " + echo "***********************************************" + exit $EXIT_CODE +fi +rm $OUTPUT +``` + +The shell script can be installed by copying the content over to `<>/.git/hooks/pre-commit`. +This pre-commit hook needs to be executable, so you may need to change the permission (`chmod +x pre-commit`). +More information about Git hooks and how to install them can be found in +[Atlassian's tutorial](https://www.atlassian.com/git/tutorials/git-hooks). + +A special thanks goes to Mohit Sarveiya for providing this shell script. +You can watch his excellent talk about **Static Code Analysis For Kotlin** on +[YouTube](https://www.youtube.com/watch?v=LT6m5_LO2DQ). + +## Only run on staged files + +It is also possible to use [the CLI](/docs/gettingstarted/cli) to create a hook that only runs on staged files. This has the advantage of speedier execution, by running on fewer files and avoiding the warm-up time of the gradle daemon. + +Please note, however, that a handful of checks requiring [type resolution](/docs/gettingstarted/type-resolution) will not work correctly with this approach. If you do adopt a partial hook, it is recommended that you still implement a full `detekt` check as part of your CI pipeline. + +This example has been put together using [pre-commit](https://pre-commit.com/), but the same principle can be applied to any kind of hook. + +Hook definition in pre-commit: + +```yml +- id: detekt + name: detekt check + description: Runs `detekt` on modified .kt files. + language: script + entry: detekt.sh + files: \.kt +``` + +Script `detekt.sh`: + +```bash +#!/bin/bash + +echo "Running detekt check..." +fileArray=($@) +detektInput=$(IFS=,;printf "%s" "${fileArray[*]}") +echo "Input files: $detektInput" + +OUTPUT=$(detekt --input "$detektInput" 2>&1) +EXIT_CODE=$? +if [ $EXIT_CODE -ne 0 ]; then + echo $OUTPUT + echo "***********************************************" + echo " Detekt failed " + echo " Please fix the above issues before committing " + echo "***********************************************" + exit $EXIT_CODE +fi +``` diff --git a/website/versioned_docs/version-1.22.0/gettingstarted/gradle.mdx b/website/versioned_docs/version-1.22.0/gettingstarted/gradle.mdx new file mode 100644 index 00000000000..71f906f9537 --- /dev/null +++ b/website/versioned_docs/version-1.22.0/gettingstarted/gradle.mdx @@ -0,0 +1,492 @@ +--- +title: "Run detekt using the Detekt Gradle Plugin" +keywords: [detekt, static, analysis, code, kotlin] +sidebar: +permalink: gradle.html +folder: gettingstarted +summary: +sidebar_position: 2 +--- + +Detekt requires **Gradle 6.7.1** or higher. We, however, recommend using the version of Gradle that is [listed in this table](/docs/introduction/compatibility). + +## Available plugin tasks + +The detekt Gradle plugin will generate multiple tasks: + +- `detekt` - Runs a detekt analysis and complexity report on your source files. Configure the analysis inside the +`detekt` closure. + - By default, the standard rule set without any ignore list is executed on sources files located + in `src/main/java`, `src/test/java`, `src/main/kotlin` and `src/test/kotlin`. + - Reports are automatically generated in xml, + html, txt, md, and sarif format and can be found in `build/reports/detekt/detekt.[xml|html|txt|md|sarif]` respectively. + - Please note that the `detekt` task is automatically run when executing `gradle check`. + - You may specify Gradle task CLI option for auto correction, such as `gradle detekt --auto-correct`. +- `detektGenerateConfig` - Generates a default detekt configuration file into your project directory. +- `detektBaseline` - Similar to `detekt`, but creates a code smell baseline. Further detekt runs will only feature new smells not in this list. + +In addition to these standard tasks, the plugin will also generate a set of experimental tasks that have +[type resolution](type-resolution.md) enabled. This happens for both, pure JVM projects and Android projects that have +the Android Gradle Plugin applied: + +- `detektMain` - Similar to `detekt`, but runs only on the `main` source set + (Android: all production source sets) +- `detektTest` - Similar to `detekt`, but runs only on the `test` source set + (Android: all JVM and Android Test source sets) +- `detektBaselineMain` - Similar to `detektBaseline`, but creates a baseline only for the `main` source set + (Android: multiple baselines for all production source sets) +- `detektBaselineTest` - Similar to `detektBaseline`, but creates a baseline only for the `test` source set + (Android: multiple baselines for all JVM and Android Test source sets) +- Android-only: `detekt` - Similar to `detekt`, but runs only on the specific (test) build variant +- Android-only: `detektBaseline` - Similar to `detektBaseline`, but creates a baseline only for the + specific (test) build variant + +Baseline files that are generated for these specific source sets / build variants contain the name of the source set / +the name of the build variant in their name, unless otherwise configured, such as `detekt-main.xml` or +`detekt-productionDebug.xml`. + +If both, a `detekt-main.xml` and a `detekt.xml` baseline file exists in place, the more specific one - `detekt-main.xml` - +takes precedence when the `detektMain` task is executed, likewise for Android variant-specific baseline files. + +_NOTE:_ When analyzing Android projects that make use of specific code generators, such as Data Binding, Kotlin synthetic +view accessors or else, you might see warnings output while Detekt runs. This is due to the inability to gather the +complete compile classpath from the Android Gradle Plugin ([upstream ticket](https://issuetracker.google.com/issues/158777988)) +and can safely be ignored. + +Use the Groovy or Kotlin DSL of Gradle to apply the detekt Gradle Plugin. You can further configure the Plugin +using the detekt closure as described [here](#closure). + +### Configuration + +Using the plugins DSL: + +#### Groovy DSL + +```groovy +plugins { + id "io.gitlab.arturbosch.detekt" version "[detekt_version]" +} + +repositories { + mavenCentral() +} +``` + +#### Kotlin DSL + +```kotlin +plugins { + id("io.gitlab.arturbosch.detekt").version("[detekt_version]") +} + +repositories { + mavenCentral() +} +``` + +Using legacy plugin application (`buildscript{}`): + +#### Groovy DSL + +```groovy +buildscript { + repositories { + gradlePluginPortal() + } + dependencies { + classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:[detekt_version]" + } +} + +apply plugin: "io.gitlab.arturbosch.detekt" + +repositories { + mavenCentral() +} +``` + +#### Kotlin DSL + +```kotlin +buildscript { + repositories { + gradlePluginPortal() + } + dependencies { + classpath("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:[detekt_version]") + } +} + +apply(plugin = "io.gitlab.arturbosch.detekt") + +repositories { + mavenCentral() +} +``` + +### Configuration for Android projects + +When using Android make sure to have detekt configured in the app/module level `build.gradle` file. + +You can configure the plugin in the same way as indicated above. + +#### Groovy DSL + +```groovy +buildscript { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } + dependencies { + classpath "com.android.tools.build:gradle:" + } +} + +plugins { + id "com.android.application" + id "org.jetbrains.kotlin.android" version "" + id "io.gitlab.arturbosch.detekt" version "[detekt_version]" +} + +repositories { + mavenCentral() +} +``` + +#### Kotlin DSL + +```kotlin +buildscript { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } + dependencies { + classpath("com.android.tools.build:gradle:") + } +} + +plugins { + id("com.android.application") + kotlin("android") version "" + id("io.gitlab.arturbosch.detekt") version "[detekt_version]" +} + +repositories { + mavenCentral() +} +``` + +### Options for detekt configuration closure + +#### Groovy DSL + +```groovy +detekt { + // Version of Detekt that will be used. When unspecified the latest detekt + // version found will be used. Override to stay on the same version. + toolVersion = "[detekt_version]" + + // The directories where detekt looks for source files. + // Defaults to `files("src/main/java", "src/test/java", "src/main/kotlin", "src/test/kotlin")`. + source = files( + "src/main/kotlin", + "gensrc/main/kotlin" + ) + + // Builds the AST in parallel. Rules are always executed in parallel. + // Can lead to speedups in larger projects. `false` by default. + parallel = false + + // Define the detekt configuration(s) you want to use. + // Defaults to the default detekt configuration. + config = files("path/to/config.yml") + + // Applies the config files on top of detekt's default config file. `false` by default. + buildUponDefaultConfig = false + + // Turns on all the rules. `false` by default. + allRules = false + + // Specifying a baseline file. All findings stored in this file in subsequent runs of detekt. + baseline = file("path/to/baseline.xml") + + // Disables all default detekt rulesets and will only run detekt with custom rules + // defined in plugins passed in with `detektPlugins` configuration. `false` by default. + disableDefaultRuleSets = false + + // Adds debug output during task execution. `false` by default. + debug = false + + // If set to `true` the build does not fail when the + // maxIssues count was reached. Defaults to `false`. + ignoreFailures = false + + // Android: Don't create tasks for the specified build types (e.g. "release") + ignoredBuildTypes = ["release"] + + // Android: Don't create tasks for the specified build flavor (e.g. "production") + ignoredFlavors = ["production"] + + // Android: Don't create tasks for the specified build variants (e.g. "productionRelease") + ignoredVariants = ["productionRelease"] + + // Specify the base path for file paths in the formatted reports. + // If not set, all file paths reported will be absolute file path. + basePath = projectDir +} +``` + +#### Kotlin DSL + +```kotlin +detekt { + // Version of Detekt that will be used. When unspecified the latest detekt + // version found will be used. Override to stay on the same version. + toolVersion = "[detekt_version]" + + // The directories where detekt looks for source files. + // Defaults to `files("src/main/java", "src/test/java", "src/main/kotlin", "src/test/kotlin")`. + source = files("src/main/java", "src/main/kotlin") + + // Builds the AST in parallel. Rules are always executed in parallel. + // Can lead to speedups in larger projects. `false` by default. + parallel = false + + // Define the detekt configuration(s) you want to use. + // Defaults to the default detekt configuration. + config = files("path/to/config.yml") + + // Applies the config files on top of detekt's default config file. `false` by default. + buildUponDefaultConfig = false + + // Turns on all the rules. `false` by default. + allRules = false + + // Specifying a baseline file. All findings stored in this file in subsequent runs of detekt. + baseline = file("path/to/baseline.xml") + + // Disables all default detekt rulesets and will only run detekt with custom rules + // defined in plugins passed in with `detektPlugins` configuration. `false` by default. + disableDefaultRuleSets = false + + // Adds debug output during task execution. `false` by default. + debug = false + + // If set to `true` the build does not fail when the + // maxIssues count was reached. Defaults to `false`. + ignoreFailures = false + + // Android: Don't create tasks for the specified build types (e.g. "release") + ignoredBuildTypes = listOf("release") + + // Android: Don't create tasks for the specified build flavor (e.g. "production") + ignoredFlavors = listOf("production") + + // Android: Don't create tasks for the specified build variants (e.g. "productionRelease") + ignoredVariants = listOf("productionRelease") + + // Specify the base path for file paths in the formatted reports. + // If not set, all file paths reported will be absolute file path. + basePath = projectDir +} +``` + +### Reports + +Report output can be customized for each task. The DSL is the same in both Groovy and Kotlin: + +```kotlin +tasks.named("detekt").configure { + reports { + // Enable/Disable XML report (default: true) + xml.required.set(true) + xml.outputLocation.set(file("build/reports/detekt.xml")) + // Enable/Disable HTML report (default: true) + html.required.set(true) + html.outputLocation.set(file("build/reports/detekt.html")) + // Enable/Disable TXT report (default: true) + txt.required.set(true) + txt.outputLocation.set(file("build/reports/detekt.txt")) + // Enable/Disable SARIF report (default: false) + sarif.required.set(true) + sarif.outputLocation.set(file("build/reports/detekt.sarif")) + // Enable/Disable MD report (default: false) + md.required.set(true) + md.outputLocation.set(file("build/reports/detekt.md")) + custom { + // The simple class name of your custom report. + reportId = "CustomJsonReport" + outputLocation.set(file("build/reports/detekt.json")) + } + } +} +``` + +### Using Type Resolution + +Type resolution is experimental and works only for [predefined tasks listed above](#available-plugin-tasks) +or when implementing a custom detekt task with the `classpath` and `jvmTarget` properties present. + +`jdkHome` is also available as an input. When this is unset, analysis is performed using the JDK classes of the JDK that +Gradle is running with (shown by the `./gradlew --version` command). This can be an issue if the Gradle JDK and the +project JDK doesn't match e.g. if Gradle runs under Java 8 but the project uses classes only available in Java 9 or +higher. Setting `jdkHome` to the Java 9 JDK path will allow for more correct analysis. + +`jdkHome` and `jvmTarget` are set automatically when applying a toolchain using either +[`java`](https://docs.gradle.org/current/userguide/toolchains.html) or +[`kotlin`](https://kotlinlang.org/docs/gradle.html#gradle-java-toolchains-support). + +More information on type resolution are available on the [type resolution](type-resolution.md) page. + +#### Groovy DSL + +```groovy +tasks.withType(io.gitlab.arturbosch.detekt.Detekt).configureEach { + jvmTarget = '1.8' + jdkHome.set(file('path/to/jdkHome')) +} +tasks.withType(io.gitlab.arturbosch.detekt.DetektCreateBaselineTask).configureEach { + jvmTarget = '1.8' + jdkHome.set(file('path/to/jdkHome')) +} +``` + +#### Kotlin DSL + +```kotlin +tasks.withType().configureEach { + this.jvmTarget = "1.8" + jdkHome.set(file("path/to/jdkHome")) +} +tasks.withType().configureEach { + this.jvmTarget = "1.8" + jdkHome.set(file("path/to/jdkHome")) +} +``` + +### Leveraging Gradle's SourceTask - Excluding and including source files + +A detekt task extends the Gradle `SourceTask` to be only scheduled when watched source files are changed. +It also allows to match files that should be excluded from the analysis. +To do this introduce a query on detekt tasks and define include and exclude patterns outside the detekt closure: + +#### Groovy DSL + +```groovy +detekt { + ... +} + +tasks.withType(io.gitlab.arturbosch.detekt.Detekt).configureEach { + // include("**/special/package/**") // only analyze a sub package inside src/main/kotlin + exclude("**/special/package/internal/**") // but exclude our legacy internal package +} + +tasks.withType(io.gitlab.arturbosch.detekt.DetektCreateBaselineTask).configureEach { + // include("**/special/package/**") // only analyze a sub package inside src/main/kotlin + exclude("**/special/package/internal/**") // but exclude our legacy internal package +} +``` + +#### Kotlin DSL + +```kotlin +detekt { + ... +} + +tasks.withType().configureEach { + // include("**/special/package/**") // only analyze a sub package inside src/main/kotlin + exclude("**/special/package/internal/**") // but exclude our legacy internal package +} + +tasks.withType().configureEach { + // include("**/special/package/**") // only analyze a sub package inside src/main/kotlin + exclude("**/special/package/internal/**") // but exclude our legacy internal package +} +``` + +### Defining custom detekt task + +Custom tasks for alternative configurations or different source sets can be defined by creating a custom task that +uses the type `Detekt`. + +#### Groovy DSL + +```groovy +tasks.register(name: myDetekt, type: io.gitlab.arturbosch.detekt.Detekt) { + description = "Runs a custom detekt build." + setSource(files("src/main/kotlin", "src/test/kotlin")) + config.setFrom(files("$rootDir/config.yml")) + debug = true + reports { + xml { + destination = file("build/reports/mydetekt.xml") + } + html.destination = file("build/reports/mydetekt.html") + } + include '**/*.kt' + include '**/*.kts' + exclude 'resources/' + exclude 'build/' +} +``` + +#### Kotlin DSL + +```kotlin +tasks.register("myDetekt") { + description = "Runs a custom detekt build." + setSource(files("src/main/kotlin", "src/test/kotlin")) + config.setFrom(files("$rootDir/config.yml")) + debug = true + reports { + xml { + destination = file("build/reports/mydetekt.xml") + } + html.destination = file("build/reports/mydetekt.html") + } + include("**/*.kt") + include("**/*.kts") + exclude("resources/") + exclude("build/") +} +``` + +### Disabling detekt from the check task + +Detekt tasks by default are verification tasks. They get executed whenever the Gradle check task gets executed. +This aligns with the behavior of other code analysis plugins for Gradle. + +If you are adding detekt to an already long running project you may want to increase the code quality incrementally and therefore +exclude detekt from the check task. + +#### Groovy DSL + +```groovy +check.configure { + dependsOn = dependsOn.findAll { it.name != 'detekt' } +} +``` + +#### Kotlin DSL + +```kotlin +tasks.named("check").configure { + this.setDependsOn(this.dependsOn.filterNot { + it is TaskProvider<*> && it.name == "detekt" + }) +} +``` + +Instead of disabling detekt for the check task, you may want to increase the build failure threshold in the [configuration file](/docs/introduction/configurations). + +## Integrating detekt inside your IntelliJ IDEA + +detekt comes with an [IntelliJ Plugin](https://plugins.jetbrains.com/plugin/10761-detekt) that you can install directly from the IDE. The plugin offers warning highlight directly inside the IDE as well as support for code formatting. + +The source code of the plugin is available here: [detekt/detekt-intellij-plugin](https://github.com/detekt/detekt-intellij-plugin) diff --git a/website/versioned_docs/version-1.22.0/gettingstarted/gradletask.md b/website/versioned_docs/version-1.22.0/gettingstarted/gradletask.md new file mode 100644 index 00000000000..6fd265f63a3 --- /dev/null +++ b/website/versioned_docs/version-1.22.0/gettingstarted/gradletask.md @@ -0,0 +1,72 @@ +--- +title: "Run detekt using Gradle Task" +keywords: [gradle, task] +sidebar: +permalink: gradletask.html +folder: gettingstarted +summary: +sidebar_position: 3 +--- + +1. Add following lines to your build.gradle file. +2. Run `gradle detekt` + +###### Groovy DSL +```groovy +repositories { + mavenCentral() +} + +configurations { + detekt +} + +def detektTask = tasks.register("detekt", JavaExec) { + main = "io.gitlab.arturbosch.detekt.cli.Main" + classpath = configurations.detekt + + def input = "$projectDir" + def config = "$projectDir/detekt.yml" + def exclude = ".*/build/.*,.*/resources/.*" + def params = [ '-i', input, '-c', config, '-ex', exclude] + + args(params) +} + +dependencies { + detekt 'io.gitlab.arturbosch.detekt:detekt-cli:[detekt_version]' +} + +// Remove this line if you don't want to run detekt on every build +check.dependsOn detektTask +``` + +###### Kotlin DSL +```kotlin +repositories { + mavenCentral() +} + +val detekt by configurations.creating + +val detektTask = tasks.register("detekt") { + main = "io.gitlab.arturbosch.detekt.cli.Main" + classpath = detekt + + val input = projectDir + val config = "$projectDir/detekt.yml" + val exclude = ".*/build/.*,.*/resources/.*" + val params = listOf("-i", input, "-c", config, "-ex", exclude) + + args(params) +} + +dependencies { + detekt("io.gitlab.arturbosch.detekt:detekt-cli:[detekt_version]) +} + +// Remove this block if you don't want to run detekt on every build +tasks.check { + dependsOn(detektTask) +} +``` diff --git a/website/versioned_docs/version-1.22.0/gettingstarted/mavenanttask.md b/website/versioned_docs/version-1.22.0/gettingstarted/mavenanttask.md new file mode 100644 index 00000000000..b832a9bb7c7 --- /dev/null +++ b/website/versioned_docs/version-1.22.0/gettingstarted/mavenanttask.md @@ -0,0 +1,57 @@ +--- +title: "Run detekt using Maven Ant Task" +keywords: [maven, anttask] +sidebar: +permalink: mavenanttask.html +folder: gettingstarted +summary: +sidebar_position: 4 +--- + +1. Add following lines to your pom.xml. +2. Run `mvn verify` (when using the verify phase as we are doing here) + +```xml + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.8 + + + + detekt + verify + + + + + + + + + + + + + + + run + + + + + io.gitlab.arturbosch.detekt + detekt-cli + [detekt_version] + + + + + +``` diff --git a/website/versioned_docs/version-1.22.0/gettingstarted/type-resolution.md b/website/versioned_docs/version-1.22.0/gettingstarted/type-resolution.md new file mode 100644 index 00000000000..86d3c5cfbe1 --- /dev/null +++ b/website/versioned_docs/version-1.22.0/gettingstarted/type-resolution.md @@ -0,0 +1,154 @@ +--- +title: "Using Type Resolution" +keywords: [detekt, static, analysis, code, kotlin] +sidebar: +permalink: type-resolution.html +folder: gettingstarted +summary: +sidebar_position: 5 +--- + +This page describes how to use detekt's **type resolution** feature. + +:::caution + +Please note that type resolution is still an **experimental feature** of Detekt. We expect it to be stable with the upcoming release of Detekt (2.x) + +::: + +## What is type resolution + +Type resolution is a feature that allows Detekt to perform more **advanced** static analysis on your Kotlin source code. + +Normally, Detekt doesn't have access to the types and symbols that are available to the compiler during the compilation. This restricts the inspection capability. +By enabling type resolution, you provide to Detekt all the information to understand types and symbols in your code needed to perform more accurate analysis. This extends Detekt's inspection capability to ones of the Kotlin **compiler**. + +### An example + +Detekt has a rule called [MagicNumber](/docs/rules/style#magicnumber) to detect usages of magic numbers in your code. + +In the following code: + +```kotlin +val user = getUserById(42)?.toString() +``` + +Detekt is able to report the usage of the number `42` as a magic number, **without** type resolution. All the information needed to run this inspection is already available in the source code. + +Similarly, Detekt has another rule called [UnnecessarySafeCall](/docs/rules/potential-bugs#unnecessarysafecall) to detect unnecessary usages of safe call operators (`?.`). + +In the previous example, Detekt is able to determine if the safe call in `getUserById(42)?.toString()` is required **only with** type resolution. + +This is because Detekt needs to know what is the **return type** of `getUserById()` in order to correctly perform the inspection. If the return type is a nullable type, then the code is valid. If the return type is a non-nullable type, Detekt will report an `UnnecessarySafeCall` as the `?.` is actually not needed. + +With type resolution, Detekt has access to all the symbols and types of your codebase. Type resolution can be enabled by providing the **classpath** that is used during compilation. This will give Detekt access to all the code used to compile your project (both first and third party code) and will allow more advanced analysis. + +## Is my rule using type resolution? + +If you're running Detekt **without** type resolution, all the rules that require type resolution **will not run**. + +All the rules that require type resolution are annotated with [`@RequiresTypeResolution`](https://github.com/detekt/detekt/search?q=%40RequiresTypeResolution). + +Moreover, their official documentation in the Detekt website will mention _Requires Type Resolution_ ([like here](/docs/rules/potential-bugs#unnecessarysafecall)). + +:::caution + +Please note that we do have some rules that have mixed behavior whether type resolution is enabled or not. Those rules are listed here: [#2994](https://github.com/detekt/detekt/issues/2994) + +::: + +Before opening an issue that you're rule is not working, please verify, whether your rule requires type resolution and check if you have type resolution enabled. + +Issues and proposals for rules that require type resolution are labelled with [needs type and symbol solving](https://github.com/detekt/detekt/labels/needs%20type%20and%20symbol%20solving) on the Issue tracker. + +## Enabling on a JVM project + +The easiest way to use type resolution is to use the Detekt Gradle plugin. On a JVM project, the following tasks will be created: + +- `detekt` - Runs detekt WITHOUT type resolution +- `detektMain` - Runs detekt with type resolution on the `main` source set +- `detektTest` - Runs detekt with type resolution on the `test` source set + +Moreover, you can use `detektBaselineMain` and `detektBaselineTest` to create baselines starting from runs of Detekt with type resolution enabled. + +Alternatively, you can create a **custom detekt task**, making sure to specify the `classpath` and `jvmTarget` properties correctly. See the [Run detekt using the Detekt Gradle Plugin](/docs/gettingstarted/gradle) and the [Run detekt using Gradle Task](/docs/gettingstarted/gradletask) for further readings on this. + +## Enabling on an Android project + +Other than the aforementioned tasks for JVM projects, you can use the following Android-specific gradle tasks: + +- `detekt` - Runs detekt with type resolution on the specific build variant +- `detektBaseline` - Creates a detekt baselines starting from a run of Detekt with type resolution enabled on the specific build variant. + +Alternatively, you can create a **custom detekt task**, making sure to specify the `classpath` and `jvmTarget` properties correctly. +Doing this on Android is more complicated due to build types/flavors (see [#2259](https://github.com/detekt/detekt/issues/2259) for further context). +Therefore, we recommend using the `detekt` tasks offered by the Gradle plugins. + +In case of build related issues, you may try `detekt.android.disabled=true` in `gradle.properties` to prevent detekt +Gradle plugins from configuring Android-specific gradle tasks. + +## Enabling on Detekt CLI + +If you're using [Detekt via CLI](/docs/gettingstarted/cli), type resolution will be enabled only if you provide the `--classpath` and +`--jvm-target` flags. See the list of [CLI options](/docs/gettingstarted/cli#use-the-cli) for details. + +## Writing a rule that uses type resolution + +If you're [writing a custom rule](/docs/introduction/extensions) or if you're willing to write a rule to contribute to Detekt, you might want to leverage type resolution. + +Rules that are using type resolution, access the [bindingContext](https://github.com/JetBrains/kotlin/blob/master/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java) from the `BaseRule` class ([source](https://github.com/detekt/detekt/blob/cd659ce8737fb177caf140f46f73a1a86b22be56/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/internal/BaseRule.kt#L30)). + +By default, the `bindingContext` is initialized as `BindingContext.EMPTY`. This is the **default value** that the rule receives if type resolution is **not enabled**. + +Therefore, is generally advised to annotate your `Rule` with `@RequiresTypeResolution` to ensure that your rule doesn't run if you don't have a proper `BindingContext`. + +If your rule is annotated with `@RequiresTypeResolution` you are free to use it to resolve types and get access to all the information needed for your rules. As a rule of thumb, we recommend to get inspiration from other rules on how they're using the `bindingContext`. + +## Testing a rule that uses type resolution + +To test a rule that uses type resolution, you can use the [`lintWithContext`](https://github.com/detekt/detekt/blob/d3546ff0d539d57e7a502dacbf66e91587fff098/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/RuleExtensions.kt#L40-L44) and [`compileAndLintWithContext`](https://github.com/detekt/detekt/blob/cd659ce8737fb177caf140f46f73a1a86b22be56/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/RuleExtensions.kt#L63-L72) extension functions. + +If you're using JUnit 5 for testing, you can use the `@KotlinCoreEnvironmentTest` annotation on your test class, and +accept a parameter of type `KotlinCoreEnvironment` in the class constructor. You can then access the environment by +referencing the parameter specified in the constructor: + +```kotlin +@KotlinCoreEnvironmentTest +class MyRuleSpec(private val env: KotlinCoreEnvironment) { + @Test + fun `reports cast that cannot succeed`() { + val code = """/* The code you want to test */""" + assertThat(MyRuleSpec().compileAndLintWithContext(env, code)).hasSize(1) + } +} +``` + +If you're using Spek for testing, you can create a `setupKotlinEnvironment()` util function, and get access to the +`KotlinCoreEnvironment` by simply calling `val env: KotlinCoreEnvironment by memoized()`: + +```kotlin +fun org.spekframework.spek2.dsl.Root.setupKotlinEnvironment(additionalJavaSourceRootPath: Path? = null) { + val wrapper by memoized( + CachingMode.SCOPE, + { createEnvironment(additionalJavaSourceRootPaths = listOfNotNull(additionalJavaSourceRootPath?.toFile())) }, + { it.dispose() } + ) + + // `env` name is used for delegation + @Suppress("UNUSED_VARIABLE") + val env: KotlinCoreEnvironment by memoized(CachingMode.EACH_GROUP) { wrapper.env } +} + +class MyRuleTest : Spek({ + setupKotlinEnvironment() + + val env: KotlinCoreEnvironment by memoized() + + it("reports cast that cannot succeed") { + val code = """/* The code you want to test */""" + assertThat(MyRuleSpec().compileAndLintWithContext(env, code)).hasSize(1) + } +}) +``` + +If you're using another testing framework (e.g. JUnit 4), you can use the [`createEnvironment()`](https://github.com/detekt/detekt/blob/cd659ce8737fb177caf140f46f73a1a86b22be56/detekt-test-utils/src/main/kotlin/io/github/detekt/test/utils/KotlinCoreEnvironmentWrapper.kt#L26-L31) method from `detekt-test-utils`. diff --git a/website/versioned_docs/version-1.22.0/intro.mdx b/website/versioned_docs/version-1.22.0/intro.mdx new file mode 100644 index 00000000000..630ee21624c --- /dev/null +++ b/website/versioned_docs/version-1.22.0/intro.mdx @@ -0,0 +1,98 @@ +--- +title: "Welcome" +keywords: [detekt, static, analysis, code, kotlin] +sidebar_position: 1 +summary: +--- + +![detekt logo](/img/logo.svg "detekt logo") +![detekt in action](/img/tutorial/detekt_in_action.png "detekt in action") + +### Features + +- Code smell analysis for your [Kotlin projects](https://kotlinlang.org/). +- Highly configurable rule sets. +- Code Smell baseline and suppression for legacy projects. +- Suppression of findings with `@Suppress` annotations. +- Support for different report formats: html, markdown, [SARIF](https://sarifweb.azurewebsites.net/) and xml (checkstyle). Is it not enough? You can extend detekt and create your own reports. +- Extensibility by enabling incorporation of personal rule sets, `FileProcessListener's` and `OutputReport's`. +- Complexity reports based on lines of code, cyclomatic complexity and number of code smells. +- First party integration with Gradle with our [Gradle plugin](#with-gradle). +- A community of [third party plugins](https://github.com/topics/detekt-plugin) that adds more rules and features to detekt. + +### Quick Start with Gradle + +Apply the following configuration to your Gradle project build file: + +```kotlin +plugins { + id("io.gitlab.arturbosch.detekt").version("[detekt_version]") +} + +repositories { + mavenCentral() +} +``` + +The format is very similar if you use the Gradle Groovy DSL. You can find what is the **latest version of detekt** in +the [release notes](/docs/introduction/changelog). + +Once you have set up detekt in your project, simply run `gradle detekt`. + +To change the default behaviour of detekt rules, first generate yourself a detekt configuration file by running the +`detektGenerateConfig` task and applying any changes to the generated file. + +Don't forget to reference the newly generated config inside the `detekt { }` closure. Optionally, it is possible to +slim down the configuration file to only the changes from the default configuration, by applying the +`buildUponDefaultConfig` option: + +```kotlin +detekt { + toolVersion = "[detekt_version]" + config = files("config/detekt/detekt.yml") + buildUponDefaultConfig = true +} +``` + +To enable/disable detekt reports use the `withType` method to set defaults for all detekt tasks at once: + +```kotlin +// Kotlin DSL +tasks.withType().configureEach { + reports { + xml.required.set(true) + html.required.set(true) + txt.required.set(true) + sarif.required.set(true) + md.required.set(true) + } +} +``` + +```groovy +// Groovy DSL +tasks.withType(Detekt).configureEach { + reports { + xml.required.set(true) + html.required.set(true) + txt.required.set(true) + sarif.required.set(true) + md.required.set(true) + } +} +``` + +See [reporting](/docs/introduction/reporting) docs for more details on configuring reports. + +### Adding more rule sets + +detekt itself provides a wrapper over [ktlint](https://github.com/pinterest/ktlint) as the `formatting` rule set +which can be easily added to the gradle configuration: + +```gradle +dependencies { + detektPlugins "io.gitlab.arturbosch.detekt:detekt-formatting:[detekt_version]" +} +``` + +Likewise custom [extensions](/docs/introduction/extensions) can be added to detekt. diff --git a/website/versioned_docs/version-1.22.0/introduction/_category_.json b/website/versioned_docs/version-1.22.0/introduction/_category_.json new file mode 100644 index 00000000000..34d319ff6a9 --- /dev/null +++ b/website/versioned_docs/version-1.22.0/introduction/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Introduction", + "position": 2 +} diff --git a/website/versioned_docs/version-1.22.0/introduction/baseline.md b/website/versioned_docs/version-1.22.0/introduction/baseline.md new file mode 100644 index 00000000000..add7a082a2f --- /dev/null +++ b/website/versioned_docs/version-1.22.0/introduction/baseline.md @@ -0,0 +1,92 @@ +--- +id: baseline +title: "Code Smell Baseline" +keywords: [baseline, suppressing, smells] +sidebar_position: 7 +--- + +With the cli option `--baseline` or the detekt-gradle-plugin closure-property `baseline` you can specify a file which is used to generate a `baseline.xml`. +It is a file where ignored code smells are defined. + +The intention of `CurrentIssues` is that only new code smells are printed on further analysis. +The `ManuallySuppressedIssues` can be used to write down false positive detections (instead of suppressing them and pollute your code base). + +The `ID` node has the following structure: `:`. +When adding a custom issue to the xml file, make sure the `RuleID` should be self-explaining. +The `Codesmell_Signature` is not printed to the console but can be retrieved from the **txt** output file when using +the `--report txt:path/to/report` cli flag. + +```xml + + + CatchRuntimeException:Junk.kt$e: RuntimeException + + + NestedBlockDepth:Indentation.kt$Indentation$override fun procedure(node: ASTNode) + TooManyFunctions:LargeClass.kt$io.gitlab.arturbosch.detekt.rules.complexity.LargeClass.kt + ComplexMethod:DetektExtension.kt$DetektExtension$fun convertToArguments(): MutableList<String> + + +``` + +#### Gradle + +If you are using the gradle-plugin run the `detektBaseline` task to generate yourself a `baseline.xml`. +This will create one baseline file per Gradle module. +As this might not be the desired behavior for a multi module project, think about implementing +a custom meta baseline task: + +###### Groovy DSL +```groovy +subprojects { + detekt { + // ... + baseline = file("${rootProject.projectDir}/config/baseline.xml") + // ... + } +} + +task detektProjectBaseline(type: io.gitlab.arturbosch.detekt.DetektCreateBaselineTask) { + description = "Overrides current baseline." + ignoreFailures.set(true) + parallel.set(true) + buildUponDefaultConfig.set(true) + setSource(files(rootDir)) + config.setFrom(files("$rootDir/config/detekt/detekt.yml")) + baseline.set(file("$rootDir/config/detekt/baseline.xml")) + include("**/*.kt") + include("**/*.kts") + exclude("**/resources/**") + exclude("**/build/**") +} +``` + +###### Kotlin DSL +```kotlin +subprojects { + detekt { + // ... + baseline = file("${rootProject.projectDir}/config/baseline.xml") + // ... + } +} + +val detektProjectBaseline by tasks.registering(DetektCreateBaselineTask::class) { + description = "Overrides current baseline." + buildUponDefaultConfig.set(true) + ignoreFailures.set(true) + parallel.set(true) + setSource(files(rootDir)) + config.setFrom(files("$rootDir/config/detekt/detekt.yml")) + baseline.set(file("$rootDir/config/detekt/baseline.xml")) + include("**/*.kt") + include("**/*.kts") + exclude("**/resources/**") + exclude("**/build/**") +} +``` + +#### FAQ + +Be aware that auto formatting cannot be combined with the `baseline`. +The signatures for a `;` for example would be too ambiguous. diff --git a/website/versioned_docs/version-1.22.0/introduction/compatibility.md b/website/versioned_docs/version-1.22.0/introduction/compatibility.md new file mode 100644 index 00000000000..634df0b9652 --- /dev/null +++ b/website/versioned_docs/version-1.22.0/introduction/compatibility.md @@ -0,0 +1,43 @@ +--- +id: compatibility +title: "Compatibility Table" +keywords: [detekt, kotlin, gradle, compatibility, android] +summary: This page lists the version of the Gradle plugins have been used to build Detekt. +sidebar_position: 11 +--- + +## Detekt Support Commitment + +Detekt is developed by open-source contributors as a volunteer effort. +Due to our limited resources, we commit to support only the **latest stable versions** and related RC versions. + +When opening Issues and Discussions, consider first updating to the latest version and align your tool versions +with the one listed below. This allows us to offer you better support. + +## Tool Versions + +When shipping the Detekt Gradle Plugin, we depend on both the **Kotlin Gradle Plugin** and the **Android Gradle Plugin**. + +Those dependencies are applied as `compileOnly` ([see here](https://github.com/detekt/detekt/blob/75622d3ba88b0ae0357aec5f2d82a55aa6c6d157/detekt-gradle-plugin/build.gradle.kts#L17-L18)) to allow our users to pick the version of the Gradle plugin they prefer and don't impose the one we use inside detekt. + +We try to provide **backward compatibility** when possible, although that's not always trivial (especially with AGP or across minor versions of Kotlin). + +The following table lists the version of the Gradle plugin we used to compile the Detekt Gradle plugin. The `Java Target Level` entry specifies the level of the generated bytecode (i.e. the `-target` flag used when generating bytecode). The `JDK Version` represents the highest version of the JDK we test our code against. + +Consider **aligning** your Gradle plugin versions with the one listed below, as we can offer better support on Issues and Discussions for the listed versions of those tools. + +| Detekt Version | Gradle Version | Kotlin Version | AGP Version | Java Target Level | JDK Version | +|----------------|----------------|----------------|-------------|-------------------|-------------| +| `1.22.0` | `7.5.1` | `1.7.21` | `7.3.1` | `1.8` | `17` | +| `1.21.0` | `7.5` | `1.6.21` | `7.2.1` | `1.8` | `17` | +| `1.20.0` | `7.4.2` | `1.6.20` | `7.1.3` | `1.8` | `17` | +| `1.19.0` | `7.3.0` | `1.5.31` | `4.2.2` | `1.8` | `17` | +| `1.18.0` | `7.0.1` | `1.5.21` | `4.2.0` | `1.8` | `16` | +| `1.17.0` | `7.0.1` | `1.4.32` | `4.2.0` | `1.8` | `15` | +| `1.16.0` | `6.8.0` | `1.4.21` | `4.1.2` | `1.8` | `15` | +| `1.15.0` | `6.8.0` | `1.4.10` | `4.0.1` | `1.8` | `15` | +| `1.14.2` | `6.7.0` | `1.4.10` | `4.0.1` | `1.8` | `14` | +| `1.14.0` | `6.7-rc-2` | `1.4.10` | `4.0.1` | `1.8` | `14` | +| `1.13.1` | `6.6.1` | `1.4.0` | `4.0.1` | `1.8` | `14` | + +_(older versions are omitted for brevity)_ diff --git a/website/versioned_docs/version-1.22.0/introduction/compose.md b/website/versioned_docs/version-1.22.0/introduction/compose.md new file mode 100644 index 00000000000..3d2b315123e --- /dev/null +++ b/website/versioned_docs/version-1.22.0/introduction/compose.md @@ -0,0 +1,102 @@ +--- +id: compose +title: "Configuration for Compose" +keywords: [compose, config, configuration, jetpack-compose, rules] +summary: This page describes each reporting format and explains how to leverage them. +sidebar_position: 5 +--- + +Relevant rule sets and their configuration options for Compose styles & usage. The following are being used as reference for Compose usage: +- [Compose API Guidelines](https://github.com/androidx/androidx/blob/androidx-main/compose/docs/compose-api-guidelines.md) +- [Compose source](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose) + +### FunctionNaming + +See [FunctionNaming](/docs/rules/naming#functionnaming). + +`@Composable` functions that return `Unit` are named using `PascalCase`. Detekt may see this as a violation: + +```kotlin +@Composable +fun FooButton(text: String, onClick: () -> Unit) { // Violation for FooButton() +``` + +#### Configurations: +Choose _either_ of the following options: + +* Augment default `functionPattern` to `'[a-zA-Z][a-zA-Z0-9]*'` (default is: `'[a-z][a-zA-Z0-9]*'`) +* Set `ignoreAnnotated` to `['Composable']` + +### TopLevelPropertyNaming + +See [TopLevelPropertyNaming](/docs/rules/naming#toplevelpropertynaming). + +Compose guidelines prescribe `CamelCase` for top-level constants. + +##### Default Style: + +```kotlin +private val FOO_PADDING = 16.dp +``` + +##### Compose Style: + +```kotlin +private val FooPadding = 16.dp +``` + +#### Configurations: + +* Set `constantPattern` to `'[A-Z][A-Za-z0-9]*'` (default is: `'[A-Z][_A-Z0-9]*'`) + + +### LongParameterList + +See [LongParameterList](/docs/rules/complexity#longparameterlist). + +Composables may boast more than the typical number of function arguments (albeit mostly with default values). For example, see [OutlinedTextField](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt;l=133?q=OutlinedTextFieldLayout&ss=androidx%2Fplatform%2Fframeworks%2Fsupport:compose%2F). + +#### Configurations: + +* Set `functionThreshold` to a higher value +* Additionally, can set `ignoreDefaultParameters = true` + +### MagicNumber + +See [MagicNumber](/docs/rules/style#magicnumber). + +Class/companion object/top-level properties that declare objects such as `Color(0xFFEA6D7E)` may be considered violations if they don't specify the named parameter (i.e. `Color(color = 0xFFEA6D7E)`). + +``` kotlin +val color1 = Color(0xFFEA6D7E) // Violation + +class Foo { + val color2 = Color(0xFFEA6D7E) // Violation + + companion object { + val color3 = Color(0xFFEA6D7E) // No violation if ignoreCompanionObjectPropertyDeclaration = true by default + } +} +``` + +#### Configurations: + +* Set `ignorePropertyDeclaration = true`, `ignoreCompanionObjectPropertyDeclaration = true` (default) + +### UnusedPrivateMember + +See [UnusedPrivateMember](/docs/rules/style#unusedprivatemember). + +Detekt may see composable preview functions, i.e. those marked with `@Preview`, as unused. + +``` kotlin +@Preview +@Composable +private fun FooLazyColumnPreview() { // Violation for FooLazyColumnPreview() + FooLazyColumn() +} +``` + +#### Configurations: + +* Set `ignoreAnnotated` to `['Preview']` diff --git a/website/versioned_docs/version-1.22.0/introduction/configurations.md b/website/versioned_docs/version-1.22.0/introduction/configurations.md new file mode 100644 index 00000000000..f2fd226b221 --- /dev/null +++ b/website/versioned_docs/version-1.22.0/introduction/configurations.md @@ -0,0 +1,191 @@ +--- +id: configurations +title: "Detekt Configuration File" +keywords: [config, configuration, yaml] +permalink: configurations.html +sidebar_position: 3 +--- + +_detekt_ uses a [YAML style configuration](https://yaml.org/spec/1.2/spec.html) file for various things: + +- rule set and rule properties +- build failure +- console reports +- output reports +- processors + +See the [default-detekt-config.yml](https://github.com/detekt/detekt/blob/main/detekt-core/src/main/resources/default-detekt-config.yml) +file for all defined configuration options and their default values. + +_Note:_ When using a custom config file, the default values are ignored unless you also set the `--build-upon-default-config` flag. + +## Config validation + +If config validation is enabled, _detekt_ will verify that your configuration file is structured correctly and all first party rule sets, rules and configuration options are valid and not marked as deprecated. + +```yaml +config: + validation: true + warningsAsErrors: false + excludes: '' +``` + +Invalid or deprecated rules and configuration options are by default printed as warnings unless `warningsAsErrors` is set to `true`. + +_Note:_ Custom rules sets are excluded from config validation by default. + +If you have extended _detekt_ and rely on a custom properties, you will need to exclude those from config validation by adding their paths to the `excludes` attribute. Multiple values are separated by comma and `.*` can be used as a wildcard (e.g. `propA,build>.*>propB`). + +## Rule sets and rules + +_detekt_ allows easily to just pick the rules you want and configure them the way you like. +For example if you want to allow up to 20 functions inside a Kotlin file instead of the default threshold, write: + +```yaml +complexity: + TooManyFunctions: + thresholdInFiles: 20 +``` + +To read about all supported rule sets and rules, use the side navigation `Rule Sets`. + +### Path Filters / Excludes / Includes + +Fine grained path filters can be defined for each rule or rule set through globbing patterns. +This gives the user more freedom in analyzing only specific files +and rule authors the ability to write *library only* rules. + +```yaml +complexity: + TooManyFunctions: + ... + excludes: ['**/internal/**'] + includes: ['**/internal/util/NeedsToBeChecked.kt'] +``` + +In case you want to apply the same filters for different rules, you can use +[YAML anchors and aliases](https://yaml.org/spec/1.2/spec.html#id2785586) to reapply previously defined paths. + +```yaml +naming: + ClassNaming: + ... + excludes: &testFolders + - '**/test/**' + - '**/androidTest/**' + ConstructorParameterNaming: + ... + excludes: *testFolders +``` + +## Build failure + +_Detekt_ supports the option to fail your build if a threshold of code smell issues is met. + +For this the following code must be inside the detekt config: + +```yaml +build: + maxIssues: 10 # break the build if more than ten weighted issues are found + weights: + complexity: 2 # every rule of the complexity rule set should count as if two issues were found... + LongParameterList: 1 # ...with the exception of the LongParameterList rule. + comments: 0 # comment rules are just a nice to know?! +``` + +Every rule and rule set can be attached with an integer value which is the weight of the finding. +For example: If you have 5 findings of the category _complexity_, then your failThreshold of 10 is reached as +5 x 2 = 10. + +Weights are respected in the following priority order: +- The specified weight for a rule +- The specified weight for a rule set +- By default, the weight is 1. + +## Console Reports + +Uncomment the reports you don't care about. + +```yaml +console-reports: + active: true + exclude: + # - 'ProjectStatisticsReport' + # - 'ComplexityReport' + # - 'NotificationReport' + # - 'FindingsReport' + # - 'FileBasedFindingsReport' + # - 'LiteFindingsReport' +``` + +**ProjectStatisticsReport** contains metrics and statistics concerning the analyzed project sorted by priority. + +**ComplexityReport** contains metrics concerning the analyzed code. +For instance the source lines of code and the McCabe complexity are calculated. + +**NotificationReport** contains notifications reported by the detekt analyzer similar to push notifications. +It's simply a way of alerting users to information that they have opted-in to. + +**FindingsReport** contains all rule violations in a list format grouped by ruleset. + +**FileBasedFindingsReport** is similar to the FindingsReport shown above. +The rule violations are grouped by file location. + +## Output Reports + +Uncomment the reports you don't care about. The detailed description can be found in [reporting](reporting.md). + +```yaml +output-reports: + active: true + exclude: + # - 'HtmlOutputReport' + # - 'TxtOutputReport' + # - 'XmlOutputReport' + # - 'SarifOutputReport' + # - 'MdOutputReport' +``` + + +## Processors + +Count processors are used to calculate project metrics. +For example, when all count processors are enabled, a detekt html report might look like this: + +![Processor metrics in html report](/img/tutorial/processor_metrics_in_html_report.png) + +The `'DetektProgressListener'` processor shows a progress indicator in stdout while a detekt process is running. + +Uncomment the processors you don't care about. + +```yaml +processors: + active: true + exclude: + - 'DetektProgressListener' + # - 'KtFileCountProcessor' + # - 'PackageCountProcessor' + # - 'ClassCountProcessor' + # - 'FunctionCountProcessor' + # - 'PropertyCountProcessor' + # - 'ProjectComplexityProcessor' + # - 'ProjectCognitiveComplexityProcessor' + # - 'ProjectLLOCProcessor' + # - 'ProjectCLOCProcessor' + # - 'ProjectLOCProcessor' + # - 'ProjectSLOCProcessor' + # - 'LicenseHeaderLoaderExtension' +``` + +## Config JSON Schema + +A JSON Schema for the config file is available on: [json.schemastore.org/detekt](https://json.schemastore.org/detekt). + +You can configure your IDE (e.g. IntelliJ or Android Studio have built-in support) +to use that schema to give you **autocompletion** capabilities on your config file. +More details on the IntelliJ support are available +[on this page](https://www.jetbrains.com/help/ruby/yaml.html#remote_json). + +![JSON Schema validator on IntelliJ](/img/tutorial/json_schema_validator_intellij.png) + +The JSON Schema is currently not automatically generated. It can be updated manually [on this repository](https://github.com/SchemaStore/schemastore). diff --git a/website/versioned_docs/version-1.22.0/introduction/extensions.md b/website/versioned_docs/version-1.22.0/introduction/extensions.md new file mode 100644 index 00000000000..4e8097b6b26 --- /dev/null +++ b/website/versioned_docs/version-1.22.0/introduction/extensions.md @@ -0,0 +1,255 @@ +--- +id: extensions +title: "Extending detekt" +keywords: [extensions, rulesets] +sidebar_position: 9 +--- + +The following page describes how to extend detekt and how to customize it to your domain-specific needs. +The associated **code samples** to this guide can be found in the package [detekt/detekt-sample-extensions](https://github.com/detekt/detekt/tree/main/detekt-sample-extensions). + +#### Custom RuleSets + +_detekt_ uses the `ServiceLoader` pattern to collect all instances of `RuleSetProvider` interfaces. +So it is possible to define rules/rule sets and enhance _detekt_ with your own flavor. + +:::caution Attention + +You need a `resources/META-INF/services/io.gitlab.arturbosch.detekt.api.RuleSetProvider` file which +has as content the fully qualified name of your `RuleSetProvider` e.g. `io.gitlab.arturbosch.detekt.sample.extensions.SampleProvider`. + +::: + +You can use our [GitHub template](https://github.com/detekt/detekt-custom-rule-template) to have a basic scaffolding to +develop your own custom rules. Another option is to clone the provided [detekt/detekt-sample-extensions](https://github.com/detekt/detekt/tree/main/detekt-sample-extensions) project. + +Own rules have to extend the abstract _Rule_ class and override the `visitXXX()`-functions from the AST. +A `RuleSetProvider` must be implemented, which declares a `RuleSet` in the `instance()`-function. +To leverage the configuration mechanism of detekt you must pass the Config object from your rule set provider to your rule. +An `Issue` property defines what ID, severity and message should be printed on the console or on any other output format. + +Example of a custom rule: +```kotlin +class TooManyFunctions(config: Config) : Rule(config) { + + override val issue = Issue(javaClass.simpleName, + Severity.CodeSmell, + "This rule reports a file with an excessive function count.", + Debt.TWENTY_MINS) + + private val threshold = 10 + private var amount: Int = 0 + + override fun visitKtFile(file: KtFile) { + super.visitKtFile(file) + if (amount > threshold) { + report(CodeSmell(issue, Entity.from(file), + "Too many functions can make the maintainability of a file costlier") + } + amount = 0 + } + + override fun visitNamedFunction(function: KtNamedFunction) { + super.visitNamedFunction(function) + amount++ + } +} +``` + +Example of a much preciser rule in terms of more specific CodeSmell constructor and Rule attributes: +```kotlin +class TooManyFunctions2(config: Config) : Rule(config) { + + override val issue = Issue( + javaClass.simpleName, + Severity.CodeSmell, + "This rule reports a file with an excessive function count.", + Debt.TWENTY_MINS + ) + + private val threshold: Int by config(defaultValue = 10) + private var amount: Int = 0 + + override fun visitKtFile(file: KtFile) { + super.visitKtFile(file) + if (amount > threshold) { + report(ThresholdedCodeSmell(issue, + entity = Entity.from(file), + metric = Metric(type = "SIZE", value = amount, threshold = threshold), + message = "The file ${file.name} has $amount function declarations. " + + "Threshold is specified with $threshold.", + references = emptyList()) + ) + } + amount = 0 + } + + override fun visitNamedFunction(function: KtNamedFunction) { + super.visitNamedFunction(function) + amount++ + } +} +``` + +If you want your rule to be configurable, write down your properties inside the detekt.yml file. +Please note that this will only take effect, if the `Config` object is passed on by the `RuleSetProvider` +to the rule itself. + +```yaml +MyRuleSet: + TooManyFunctions2: + active: true + threshold: 5 + OtherRule: + active: false +``` + +By specifying the rule set and rule ids, _detekt_ will use the sub configuration of `TooManyFunctions2`: + +```val threshold = valueOrDefault("threshold", THRESHOLD)``` + +:::note + +As of version 1.2.0 detekt now verifies if all configured properties actually exist in a configuration created by `--generate-config`. +This means that by default detekt does not know about your new properties. +Therefore we need to mention them in the configuration under `config>excludes`. + +::: + +```yaml +config: + validation: true + # 1. exclude rule set 'sample' and all its nested members + # 2. exclude every property in every rule under the rule set 'sample' + excludes: "sample.*,sample>.*>.*" +``` + +##### Testing your rules + +To test your rules, add the dependency on `detekt-test` to your project: `testCompile "io.gitlab.arturbosch.detekt:detekt-test:$version"`. + +The easiest way to detect issues with your newly created rule is to use the `lint` extension function: +- `Rule.lint(StringContent/Path/KtFile): List` + +If you need to reuse the Kotlin file for performance reasons within similar test cases, please use one of these functions: +- `compileContentForTest(content: String): KtFile` +- `compileForTest(path: Path): KtFile` + +#### Custom Processors + +Custom processors can be used for example to implement additional project metrics. + +When for whatever reason you want to count all loop statements inside your code base, you could write something like: + +```kotlin +class NumberOfLoopsProcessor : FileProcessListener { + + override fun onProcess(file: KtFile) { + val visitor = LoopVisitor() + file.accept(visitor) + file.putUserData(numberOfLoopsKey, visitor.numberOfLoops) + } + + companion object { + val numberOfLoopsKey = Key("number of loops") + } + + class LoopVisitor : DetektVisitor() { + + internal var numberOfLoops = 0 + override fun visitLoopExpression(loopExpression: KtLoopExpression) { + super.visitLoopExpression(loopExpression) + numberOfLoops++ + } + } +} +``` + +To let detekt know about the new processor, we specify a `resources/META-INF/services/io.gitlab.arturbosch.detekt.api.FileProcessListener` file +with the full qualify name of our processor as the content: `io.gitlab.arturbosch.detekt.sample.extensions.processors.NumberOfLoopsProcessor`. + + +To test the code we use the `detekt-test` module and write a JUnit 5 testcase. + +```kotlin +class NumberOfLoopsProcessorTest { + + @Test + fun `should expect two loops`() { + val code = """ + fun main() { + for (i in 0..10) { + while (i < 5) { + println(i) + } + } + } + """ + + val ktFile = compileContentForTest(code) + NumberOfLoopsProcessor().onProcess(ktFile) + + assertThat(ktFile.getUserData(NumberOfLoopsProcessor.numberOfLoopsKey)).isEqualTo(2) + } +} +``` + +#### Custom Reports + +_detekt_ allows you to extend the console output and to create custom output formats. +If you want to customize the output, take a look at the `ConsoleReport` and `OutputReport` classes. + +All they need are an implementation of the `render()`-function which takes an object with all findings and returns a string to be printed out. + +```kotlin +abstract fun render(detektion: Detektion): String? +``` + +#### Let detekt know about your extensions + +So you have implemented your own rules or other extensions and want to integrate them +into your `detekt` run? Great, make sure to have a `jar` with all your needed dependencies +minus the ones `detekt` brings itself. + +Take a look at our [sample project](https://github.com/detekt/detekt/tree/main/detekt-sample-extensions) on how to achieve this with gradle. + +##### Integrate your extension with the detekt CLI + +Mention your `jar` with the `--plugins` flag when calling the cli fatjar: +```sh +detekt --input ... --plugins /path/to/my/jar +``` + +##### Integrate your extension with the detekt gradle plugin + +For example `detekt` itself provides a wrapper over [ktlint](https://github.com/pinterest/ktlint) as a +custom `formatting` rule set. +To enable it, we add the published dependency to `detekt` via the `detektPlugins` configuration: + +###### Gradle (Kotlin/Groovy DSL) + +```kotlin +dependencies { + detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:[detekt_version]") +} +``` + +##### Pitfalls + +- All rules are disabled by default and have to be explicitly enabled in the `detekt` yaml configuration file. +- If you do not pass the `Config` object from the `RuleSetProvider` to the rule, the rule is active, but you will not be able to use +any configuration options or disable the rule via config file. +- If your extension is part of your project and you integrate it like `detektPlugins project(":my-rules")` make sure that this +subproject is build before `gradle detekt` is run. +In the `kotlin-dsl` you could add something like `tasks.withType { dependsOn(":my-rules:assemble") }` to explicitly run `detekt` only +after your extension sub project is built. +- If you use detekt for your Android project, and if you want to integrate all your custom rules in a new module, please make sure that +you created a pure kotlin module which has no Android dependencies. `apply plugin: "kotlin"` is enough to make it work. +- Sometimes when you run detekt task, you may not see the violations detected by your custom rules. In this case open a terminal and run +`./gradlew --stop` to stop gradle daemons and run the task again. + +#### autoCorrect property + +In detekt you can write custom rules which can manipulate your code base. +For this a cli flag `--auto-correct` and the gradle plugin property `autoCorrect` exists. +Only write auto correcting code within the `Rule#withAutoCorrect()`-function. diff --git a/website/versioned_docs/version-1.22.0/introduction/reporting.md b/website/versioned_docs/version-1.22.0/introduction/reporting.md new file mode 100644 index 00000000000..141766034d0 --- /dev/null +++ b/website/versioned_docs/version-1.22.0/introduction/reporting.md @@ -0,0 +1,175 @@ +--- +id: reporting +title: "Reporting" +keywords: [reporting] +summary: This page describes each reporting format and explains how to leverage them. +sidebar_position: 4 +--- + +## Formats + +In addition to the CLI output, Detekt supports 4 different types of output reporting formats. +You can refer to [CLI](/docs/gettingstarted/cli) or [Gradle](/docs/gettingstarted/gradle) to find +out how to configure these report formats. + +### TXT +Similar to the console output, each line of the txt output represents a finding and contains +finding signature to help edit [baseline files](/docs/gettingstarted/gradle). + +``` +EmptyFunctionBlock - [This empty block of code can be removed.] at /user/home/detekt/detekt-gradle-plugin/src/main/kotlin/io/gitlab/arturbosch/detekt/DetektPlugin.kt:14:42 - Signature=DetektPlugin.kt$DetektPlugin${ } +NoUnusedImports - [Unused import] at /user/home/detekt/detekt-gradle-plugin/src/main/kotlin/io/gitlab/arturbosch/detekt/DetektPlugin.kt:9:1 - Signature=io.gitlab.arturbosch.detekt.DetektPlugin.kt:9 +NoUnusedImports - [Unused import] at /user/home/detekt/detekt-gradle-plugin/src/main/kotlin/io/gitlab/arturbosch/detekt/DetektPlugin.kt:10:1 - Signature=io.gitlab.arturbosch.detekt.DetektPlugin.kt:10 +NoConsecutiveBlankLines - [Needless blank line(s)] at /user/home/detekt/detekt-gradle-plugin/src/main/kotlin/io/gitlab/arturbosch/detekt/DetektPlugin.kt:86:1 - Signature=io.gitlab.arturbosch.detekt.DetektPlugin.kt:86 +UnusedPrivateMember - [Private function registerDetektJvmTasks is unused.] at /user/home/detekt/detekt-gradle-plugin/src/main/kotlin/io/gitlab/arturbosch/detekt/DetektPlugin.kt:17:5 - Signature=DetektPlugin.kt$DetektPlugin$private fun Project.registerDetektJvmTasks(extension: DetektExtension) +``` + +### HTML +HTML is a human-readable format that can be open through browser. It includes different metrics +and complexity reports of this run, in addition to the findings with detailed descriptions and +report. Check out the example: ![HTML report](/img/tutorial/html.png) + +### XML +XML is a machine-readable format that can be integrated with CI tools. It is compatible with +[Checkstyle](https://checkstyle.sourceforge.io/) output. + +### SARIF +[SARIF](https://sarifweb.azurewebsites.net/) is a standard format for the output of +static analysis tools. It is a JSON format with a defined +[schema](https://docs.oasis-open.org/sarif/sarif/v2.0/csprd02/schemas/). It is currently supported +by GitHub Code Scanning, and we expect more consuming tools will adopt this format in the future. + +### MD +Markdown is a lightweight markup language for creating formatted text using a plain-text editor. +The output structure looks similar to HTML format. +About [markdown](https://github.github.com/gfm/#what-is-markdown-) on GitHub. + +## Severity +For machine-readable format, it is possible to configure the severity of each finding to fit +your CI policy with respects to errors. You may specify the severity level in the config file +for rule, or ruleSets: + +```yaml +empty-blocks: + active: true + severity: error + EmptyCatchBlock: + active: true + severity: info +``` + +The severity will be computed in the priority order: +- Severity of the rule if exists +- Severity of the parent ruleset if exists +- Default severity: warning + +## Relative path +In a shared codebase, it is often required to use relative path so that all developers and tooling +have a consistent view. This can be enabled by CLI option `--base-path` or Gradle as the following: + +```groovy +detekt { + basePath = projectDir +} +``` + +Note that this option only affects file paths in those formats for machine consumers, +namely XML and SARIF. + +## Merging reports + +The machine-readable report formats support report merging. +Detekt Gradle plugin is not opinionated in how merging is set up and respects each project's build logic, especially +the merging makes most sense in a multi-module project. In this spirit, only Gradle tasks are provided. + +At the moment, merging XML and SARIF are supported. You can refer to the sample build script below and +run `./gradlew detekt reportMerge --continue` to execute detekt tasks and merge the corresponding reports. + +### Groovy DSL +```groovy +tasks.register("reportMerge", io.gitlab.arturbosch.detekt.report.ReportMergeTask) { + output = project.layout.buildDirectory.file("reports/detekt/merge.xml") // or "reports/detekt/merge.sarif" +} + +subprojects { + detekt { + reports.xml.required.set(true) + // reports.sarif.required.set(true) + } + + plugins.withType(io.gitlab.arturbosch.detekt.DetektPlugin) { + tasks.withType(io.gitlab.arturbosch.detekt.Detekt) { detektTask -> // Sadly it has to be eager. + finalizedBy(reportMerge) + + reportMerge.configure { mergeTask -> + mergeTask.input.from(detektTask.xmlReportFile) // or detektTask.sarifReportFile + } + } + } +} +``` + +### Kotlin DSL + +```kotlin +val reportMerge by tasks.registering(io.gitlab.arturbosch.detekt.report.ReportMergeTask::class) { + output.set(rootProject.layout.buildDirectory.file("reports/detekt/merge.xml")) // or "reports/detekt/merge.sarif" +} + +subprojects { + detekt { + reports.xml.required.set(true) + // reports.sarif.required.set(true) + } + + plugins.withType { + tasks.withType detekt@{ // Sadly it has to be eager. + finalizedBy(reportMerge) + + reportMerge.configure { + input.from(this@detekt.xmlReportFile) // or .sarifReportFile + } + } + } +} +``` + +## Integration with Github Code Scanning +If your repository is hosted on Github, you can enable SARIF output in your repository. +You can follow to the [official documentation](https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/uploading-a-sarif-file-to-github). + +To change the severity level to fail your GitHub Action build configure it in [GitHub Settings](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#defining-the-severities-causing-pull-request-check-failure). + +You can follow the example below as a quick start: +```yaml +jobs: + without-type-resolution: + runs-on: ubuntu-latest + env: + GRADLE_OPTS: -Dorg.gradle.daemon=false + steps: + - name: Checkout Repo + uses: actions/checkout@v3 + + - name: Setup Java + uses: actions/setup-java@v3 + with: + java-version: 11 + + - name: Run detekt + run: ./gradlew detekt + + # Make sure we always run this upload task, + # because the previous step may fail if there are findings. + - name: Upload SARIF to Github using the upload-sarif action + uses: github/codeql-action/upload-sarif@v2 + if: success() || failure() + with: + sarif_file: build/reports/detekt/detekt.sarif +``` + +Note: you'll have to set `Detekt.basePath` on each Detekt Gradle task, +so that GitHub knows where the repository is to place annotations correctly. +```gradle +basePath = rootProject.projectDir.absolutePath +``` diff --git a/website/versioned_docs/version-1.22.0/introduction/snapshots.md b/website/versioned_docs/version-1.22.0/introduction/snapshots.md new file mode 100644 index 00000000000..4f40242851a --- /dev/null +++ b/website/versioned_docs/version-1.22.0/introduction/snapshots.md @@ -0,0 +1,82 @@ +--- +id: snapshots +title: "Using Snapshots" +keywords: [snapshot, releases] +summary: This page explains how you can setup snapshots for your Detekt build to test the latest unreleased features. +sidebar_position: 10 +--- + +This page explains how you can use our **latest snapshots** to test upcoming unreleased features. + +## Where to download snapshots + +You can find the latest snapshot on [sonatype](https://oss.sonatype.org/#view-repositories;snapshots~browsestorage~io/gitlab/arturbosch/detekt). A new snapshot is published after every merge to `main` from the [Deploy Snapshot](https://github.com/detekt/detekt/actions?query=workflow%3A%22Deploy+Snapshot%22) Github Action workflow. + +## Gradle setup with Buildscript + +If you're using Gradle with the `buildscript` block, you should update your top level `build.gradle` file with: + +```groovy +buildscript { + repositories { + maven { + url "https://oss.sonatype.org/content/repositories/snapshots/" + } + } + dependencies { + classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:main-SNAPSHOT" + } +} + +apply plugin: "io.gitlab.arturbosch.detekt" + +allprojects { + repositories { + maven { + url "https://oss.sonatype.org/content/repositories/snapshots/" + } + } +} +``` + +Make sure that you're adding the sonatype maven repository to both the `repositories{}` block **inside** the `buildscript{}` block and outside it. + +## Gradle setup with Plugin block + +If you're using the `plugins{}` block to apply detekt, you should update your `build.gradle` file to: + +```groovy +plugins { + id("io.gitlab.arturbosch.detekt") version "main-SNAPSHOT" +} + +allprojects { + repositories { + maven { + url "https://oss.sonatype.org/content/repositories/snapshots/" + } + } +} +``` + +Plus you need to update the `settings.gradle` file as follows: + +```groovy +pluginManagement { + resolutionStrategy { + eachPlugin { + if (requested.id.id == "io.gitlab.arturbosch.detekt") { + useModule("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:${requested.version}") + } + } + } + repositories { + // Your other repos here. + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots/") + } + } +} +``` + +Please note that the extra `resolutionStrategy{}` block is needed as we don't publish a Gradle Plugin marker for our snapshots. diff --git a/website/versioned_docs/version-1.22.0/introduction/suppressing-rules.md b/website/versioned_docs/version-1.22.0/introduction/suppressing-rules.md new file mode 100644 index 00000000000..1a4daa680f1 --- /dev/null +++ b/website/versioned_docs/version-1.22.0/introduction/suppressing-rules.md @@ -0,0 +1,43 @@ +--- +id: suppressing-rules +title: "Suppressing Issues" +keywords: [suppressing, issues] +sidebar_position: 6 +--- + +_detekt_ supports the Java (`@SuppressWarnings`) and Kotlin (`@Suppress`) style suppression. +If both annotations are present, Kotlin's annotation is favored! + +To suppress an issue, the id of the issue must be written inside the values field of the annotation. +Furthermore, the ruleset plus rulename can be used to suppress issues (e.g. `@Suppress("LongMethod", "complexity.LongParameterList", ...)`). +The issue-id is also exactly the id of the reporting rule. + +If a `LargeClass` is reported, but that is totally fine for you codebase, then just annotate it: + +```kotlin +@Suppress("LargeClass") // or use complexity.LargeClass +object Constants { + ... +} +``` + +Some rules like `TooManyFunctions` can be suppressed by using a file level annotation `@file:Suppress("TooManyFunctions")`. + +**Formatting rules suppression** + +Please note that rules inside the [`formatting`](/docs/rules/formatting) ruleset can only be suppressed at **the file level**. + +Rules inside this ruleset are wrappers around KtLint rules, and we don't have the same reporting capabilities that we offer for first party rules. For example, you can suppress the [MaximumLineLength](/docs/rules/formatting#maximumlinelength) rule only in your entire file with: + +```kotlin +@file:Suppress("MaximumLineLength") +package com.example + +object AClassWithLongLines { + //... +} +``` + +Several rules in the [`formatting`](/docs/rules/formatting) ruleset also have a "first party" counterpart. For instance you can use the [`MaxLineLength`](/docs/rules/style#maxlinelength) rule instead from the [`style`](/docs/rules/style) ruleset. + +For those rules, you can suppress the inspection also locally (on top of an expression, function, class, etc.). diff --git a/website/versioned_docs/version-1.22.0/introduction/suppressors.md b/website/versioned_docs/version-1.22.0/introduction/suppressors.md new file mode 100644 index 00000000000..656ebab226d --- /dev/null +++ b/website/versioned_docs/version-1.22.0/introduction/suppressors.md @@ -0,0 +1,41 @@ +--- +id: suppressors +title: "Suppressors" +keywords: [suppressing, issues, smells] +sidebar_position: 8 +--- + +The `Suppressor`s are a tool that you can use to customize the reports of detekt. They allow you to (surprise) suppress some issues detected by some rules, and they can be applied to any rule. + +An example is the **annotation** suppressor. It works like this. First, you need to configure the tag `ignoreAnnotated` with a list of annotations, you want the suppressor to consider. Example: + +```yaml +UnusedPrivateMember: + active: true + ignoreAnnotated: + - 'Preview' +``` + +Now, if an issue is found under a code that is annotated with `@Preview` that issue will be suppressed. This example is really handy if you use [Jetpack Compose](/docs/introduction/compose), for example. + +## Available `Suppressor`s + +### Annotation Suppressor + +Suppress all the issues that are raised under a code that is annotated with the annotations defined at `ignoreAnnotated`. + +##### Config tag + +`ignoreAnnotated: List`: The annotations can be defined just by its name or with its fully qualified name. If you don't run detekt with type solving the fully qualified name does not work. + +### Function Suppressor + +Suppress any issue raised under a function definition that matches the signatures defined at `ignoreFunction`. + +*Note*: this Suppressor doesn't suppress issues found when you call these functions. It just suppresses the ones in the function **definition**. + +##### Config tag: + +`ignoreFunction: List`: The signature of the function. You can ignore all the overloads of a function defining just its name like `java.time.LocalDate.now` or you can specify the parameters to only suppress one: `java.time.LocalDate(java.time.Clock)`. + +*Note:* you need to write all the types with fully qualified names e.g. `org.example.foo(kotlin.String)`. It is important to add `kotlin.String`. Just adding `String` will not work. diff --git a/website/versioned_docs/version-1.22.0/rules/_category_.json b/website/versioned_docs/version-1.22.0/rules/_category_.json new file mode 100644 index 00000000000..017ff3449cf --- /dev/null +++ b/website/versioned_docs/version-1.22.0/rules/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Rules Documentation", + "position": 4 +} diff --git a/website/versioned_docs/version-1.22.0/rules/comments.md b/website/versioned_docs/version-1.22.0/rules/comments.md new file mode 100644 index 00000000000..e15e367b619 --- /dev/null +++ b/website/versioned_docs/version-1.22.0/rules/comments.md @@ -0,0 +1,277 @@ +--- +title: Comments Rule Set +sidebar: home_sidebar +keywords: [rules, comments] +permalink: comments.html +toc: true +folder: documentation +--- +This rule set provides rules that address issues in comments and documentation +of the code. + +### AbsentOrWrongFileLicense + +This rule will report every Kotlin source file which doesn't have the required license header. +The rule validates each Kotlin source and operates in two modes: if `licenseTemplateIsRegex = false` (or missing) +the rule checks whether the input file header starts with the read text from the passed file in the +`licenseTemplateFile` configuration option. If `licenseTemplateIsRegex = true` the rule matches the header with +a regular expression produced from the passed template license file (defined via `licenseTemplateFile` configuration +option). + +**Active by default**: No + +**Debt**: 5min + +#### Configuration options: + +* ``licenseTemplateFile`` (default: ``'license.template'``) + + path to file with license header template resolved relatively to config file + +* ``licenseTemplateIsRegex`` (default: ``false``) + + whether or not the license header template is a regex template + +### CommentOverPrivateFunction + +This rule reports comments and documentation that has been added to private functions. These comments get reported +because they probably explain the functionality of the private function. However, private functions should be small +enough and have an understandable name so that they are self-explanatory and do not need this comment in the first +place. + +Instead of simply removing this comment to solve this issue prefer to split up the function into smaller functions +with better names if necessary. Giving the function a better, more descriptive name can also help in +solving this issue. + +**Active by default**: No + +**Debt**: 20min + +### CommentOverPrivateProperty + +This rule reports comments and documentation above private properties. This can indicate that the property has a +confusing name or is not in a small enough context to be understood. +Private properties should be named in a self-explanatory way and readers of the code should be able to understand +why the property exists and what purpose it solves without the comment. + +Instead of simply removing the comment to solve this issue, prefer renaming the property to a more self-explanatory +name. If this property is inside a bigger class, it makes sense to refactor and split up the class. This can +increase readability and make the documentation obsolete. + +**Active by default**: No + +**Debt**: 20min + +### DeprecatedBlockTag + +This rule reports use of the `@deprecated` block tag in KDoc comments. Deprecation must be specified using a +`@Deprecated` annotation as adding a `@deprecated` block tag in KDoc comments +[has no effect and is not supported](https://kotlinlang.org/docs/kotlin-doc.html#suppress). The `@Deprecated` +annotation constructor has dedicated fields for a message and a type (warning, error, etc.). You can also use the +`@ReplaceWith` annotation to specify how to solve the deprecation automatically via the IDE. + +**Active by default**: No + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +/** +* This function prints a message followed by a new line. +* +* @deprecated Useless, the Kotlin standard library can already do this. Replace with println. +*/ +fun printThenNewline(what: String) { + // ... +} +``` + +#### Compliant Code: + +```kotlin +/** +* This function prints a message followed by a new line. +*/ +@Deprecated("Useless, the Kotlin standard library can already do this.") +@ReplaceWith("println(what)") +fun printThenNewline(what: String) { + // ... +} +``` + +### EndOfSentenceFormat + +This rule validates the end of the first sentence of a KDoc comment. +It should end with proper punctuation or with a correct URL. + +**Active by default**: No + +**Debt**: 5min + +#### Configuration options: + +* ``endOfSentenceFormat`` (default: ``'([.?!][ \t\n\r\f<])|([.?!:]$)'``) + + regular expression which should match the end of the first sentence in the KDoc + +### KDocReferencesNonPublicProperty + +This rule will report any KDoc comments that refer to non-public properties of a class. +Clients do not need to know the implementation details. + +**Active by default**: No + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +/** +* Comment +* [prop1] - non-public property +* [prop2] - public property +*/ +class Test { + private val prop1 = 0 + val prop2 = 0 +} +``` + +#### Compliant Code: + +```kotlin +/** +* Comment +* [prop2] - public property +*/ +class Test { + private val prop1 = 0 + val prop2 = 0 +} +``` + +### OutdatedDocumentation + +This rule will report any class, function or constructor with KDoc that does not match the declaration signature. +If KDoc is not present or does not contain any @param or @property tags, rule violation will not be reported. +By default, both type and value parameters need to be matched and declarations orders must be preserved. You can +turn off these features using configuration options. + +**Active by default**: No + +**Debt**: 10min + +#### Configuration options: + +* ``matchTypeParameters`` (default: ``true``) + + if type parameters should be matched + +* ``matchDeclarationsOrder`` (default: ``true``) + + if the order of declarations should be preserved + +* ``allowParamOnConstructorProperties`` (default: ``false``) + + if we allow constructor parameters to be marked as @param instead of @property + +#### Noncompliant Code: + +```kotlin +/** +* @param someParam +* @property someProp +*/ +class MyClass(otherParam: String, val otherProp: String) + +/** +* @param T +* @param someParam +*/ +fun myFun(someParam: String) +``` + +#### Compliant Code: + +```kotlin +/** +* @param someParam +* @property someProp +*/ +class MyClass(someParam: String, val someProp: String) + +/** +* @param T +* @param S +* @param someParam +*/ +fun myFun(someParam: String) +``` + +### UndocumentedPublicClass + +This rule reports public classes, objects and interfaces which do not have the required documentation. +Enable this rule if the codebase should have documentation on every public class, interface and object. + +By default, this rule also searches for nested and inner classes and objects. This default behavior can be changed +with the configuration options of this rule. + +**Active by default**: No + +**Debt**: 20min + +#### Configuration options: + +* ``searchInNestedClass`` (default: ``true``) + + if nested classes should be searched + +* ``searchInInnerClass`` (default: ``true``) + + if inner classes should be searched + +* ``searchInInnerObject`` (default: ``true``) + + if inner objects should be searched + +* ``searchInInnerInterface`` (default: ``true``) + + if inner interfaces should be searched + +* ``searchInProtectedClass`` (default: ``false``) + + if protected classes should be searched + +### UndocumentedPublicFunction + +This rule will report any public function which does not have the required documentation. +If the codebase should have documentation on all public functions enable this rule to enforce this. +Overridden functions are excluded by this rule. + +**Active by default**: No + +**Debt**: 20min + +#### Configuration options: + +* ``searchProtectedFunction`` (default: ``false``) + + if protected functions should be searched + +### UndocumentedPublicProperty + +This rule will report any public property which does not have the required documentation. +This also includes public properties defined in a primary constructor. +If the codebase should have documentation on all public properties enable this rule to enforce this. +Overridden properties are excluded by this rule. + +**Active by default**: No + +**Debt**: 20min + +#### Configuration options: + +* ``searchProtectedProperty`` (default: ``false``) + + if protected functions should be searched diff --git a/website/versioned_docs/version-1.22.0/rules/complexity.md b/website/versioned_docs/version-1.22.0/rules/complexity.md new file mode 100644 index 00000000000..c2263165520 --- /dev/null +++ b/website/versioned_docs/version-1.22.0/rules/complexity.md @@ -0,0 +1,529 @@ +--- +title: Complexity Rule Set +sidebar: home_sidebar +keywords: [rules, complexity] +permalink: complexity.html +toc: true +folder: documentation +--- +This rule set contains rules that report complex code. + +### CognitiveComplexMethod + +Complex methods are hard to understand and read. It might not be obvious what side-effects a complex method has. +Prefer splitting up complex methods into smaller methods that are in turn easier to understand. +Smaller methods can also be named much clearer which leads to improved readability of the code. + +This rule measures and restricts the complexity of the method through the [Cognitive Complexity metric of Sonasource](https://www.sonarsource.com/docs/CognitiveComplexity.pdf). +Which improves McCabe's Cyclomatic Complexity ({@link CyclomaticComplexMethod}) considering the programmer's mental model. + +Similar to cyclomatic complexity, it is a mathematical model that increases +1 complexity for flow control statements, +but increases additional complexity when the statements are deeply nested. + +The statements that increase the complexity or the nesting level are as follows. +- __Complexity Increments__ - `if`, `when`, `for`, `while`, `do while`, `catch`, `labeled break`, `labeled continue`, `labeled return`, `recursion call`, `&&`, `||` +- __Nesting Level Increments__ - `if`, `when`, `for`, `while`, `do while`, `catch`, `nested function` +- __Additional Complexity Increments by Nesting Level__ - `if`, `when`, `for`, `while`, `do while`, `catch` + +**Active by default**: No + +**Debt**: 20min + +#### Configuration options: + +* ``threshold`` (default: ``15``) + + Cognitive Complexity number for a method. + +### ComplexCondition + +Complex conditions make it hard to understand which cases lead to the condition being true or false. To improve +readability and understanding of complex conditions consider extracting them into well-named functions or variables +and call those instead. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 20min + +#### Configuration options: + +* ``threshold`` (default: ``4``) + + the number of conditions which will trigger the rule + +#### Noncompliant Code: + +```kotlin +val str = "foo" +val isFoo = if (str.startsWith("foo") && !str.endsWith("foo") && !str.endsWith("bar") && !str.endsWith("_")) { + // ... +} +``` + +#### Compliant Code: + +```kotlin +val str = "foo" +val isFoo = if (str.startsWith("foo") && hasCorrectEnding()) { + // ... +} + +fun hasCorrectEnding() = return !str.endsWith("foo") && !str.endsWith("bar") && !str.endsWith("_") +``` + +### ComplexInterface + +Complex interfaces which contain too many functions and/or properties indicate that this interface is handling too +many things at once. Interfaces should follow the single-responsibility principle to also encourage implementations +of this interface to not handle too many things at once. + +Large interfaces should be split into smaller interfaces which have a clear responsibility and are easier +to understand and implement. + +**Active by default**: No + +**Debt**: 20min + +#### Configuration options: + +* ``threshold`` (default: ``10``) + + the amount of definitions in an interface to trigger the rule + +* ``includeStaticDeclarations`` (default: ``false``) + + whether static declarations should be included + +* ``includePrivateDeclarations`` (default: ``false``) + + whether private declarations should be included + +* ``ignoreOverloaded`` (default: ``false``) + + ignore overloaded methods - only count once + +### CyclomaticComplexMethod + +Complex methods are hard to understand and read. It might not be obvious what side-effects a complex method has. +Prefer splitting up complex methods into smaller methods that are in turn easier to understand. +Smaller methods can also be named much clearer which leads to improved readability of the code. + +This rule uses McCabe's Cyclomatic Complexity (MCC) metric to measure the number of +linearly independent paths through a function's source code (https://www.ndepend.com/docs/code-metrics#CC). +The higher the number of independent paths, the more complex a method is. +Complex methods use too many of the following statements. +Each one of them adds one to the complexity count. + +- __Conditional statements__ - `if`, `else if`, `when` +- __Jump statements__ - `continue`, `break` +- __Loops__ - `for`, `while`, `do-while`, `forEach` +- __Operators__ `&&`, `||`, `?:` +- __Exceptions__ - `catch`, `use` +- __Scope Functions__ - `let`, `run`, `with`, `apply`, and `also` -> +[Reference](https://kotlinlang.org/docs/scope-functions.html) + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 20min + +**Aliases**: ComplexMethod + +#### Configuration options: + +* ``threshold`` (default: ``15``) + + McCabe's Cyclomatic Complexity (MCC) number for a method. + +* ``ignoreSingleWhenExpression`` (default: ``false``) + + Ignores a complex method if it only contains a single when expression. + +* ``ignoreSimpleWhenEntries`` (default: ``false``) + + Whether to ignore simple (braceless) when entries. + +* ``ignoreNestingFunctions`` (default: ``false``) + + Whether to ignore functions which are often used instead of an `if` or `for` statement. + +* ``nestingFunctions`` (default: ``['also', 'apply', 'forEach', 'isNotNull', 'ifNull', 'let', 'run', 'use', 'with']``) + + Comma separated list of function names which add complexity. + +### LabeledExpression + +This rule reports labeled expressions. Expressions with labels generally increase complexity and worsen the +maintainability of the code. Refactor the violating code to not use labels instead. +Labeled expressions referencing an outer class with a label from an inner class are allowed, because there is no +way to get the instance of an outer class from an inner class in Kotlin. + +**Active by default**: No + +**Debt**: 20min + +#### Configuration options: + +* ``ignoredLabels`` (default: ``[]``) + + allows to provide a list of label names which should be ignored by this rule + +#### Noncompliant Code: + +```kotlin +val range = listOf("foo", "bar") +loop@ for (r in range) { + if (r == "bar") break@loop + println(r) +} + +class Outer { + inner class Inner { + fun f() { + val i = this@Inner // referencing itself, use `this instead + } + } +} +``` + +#### Compliant Code: + +```kotlin +val range = listOf("foo", "bar") +for (r in range) { + if (r == "bar") break + println(r) +} + +class Outer { + inner class Inner { + fun f() { + val outer = this@Outer + } + fun Int.extend() { + val inner = this@Inner // this would reference Int and not Inner + } + } +} +``` + +### LargeClass + +This rule reports large classes. Classes should generally have one responsibility. Large classes can indicate that +the class does instead handle multiple responsibilities. Instead of doing many things at once prefer to +split up large classes into smaller classes. These smaller classes are then easier to understand and handle less +things. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 20min + +#### Configuration options: + +* ``threshold`` (default: ``600``) + + the size of class required to trigger the rule + +### LongMethod + +Methods should have one responsibility. Long methods can indicate that a method handles too many cases at once. +Prefer smaller methods with clear names that describe their functionality clearly. + +Extract parts of the functionality of long methods into separate, smaller methods. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 20min + +#### Configuration options: + +* ``threshold`` (default: ``60``) + + number of lines in a method to trigger the rule + +### LongParameterList + +Reports functions and constructors which have more parameters than a certain threshold. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 20min + +#### Configuration options: + +* ~~``threshold``~~ (default: ``6``) + + **Deprecated**: Use `functionThreshold` and `constructorThreshold` instead + + number of parameters required to trigger the rule + +* ``functionThreshold`` (default: ``6``) + + number of function parameters required to trigger the rule + +* ``constructorThreshold`` (default: ``7``) + + number of constructor parameters required to trigger the rule + +* ``ignoreDefaultParameters`` (default: ``false``) + + ignore parameters that have a default value + +* ``ignoreDataClasses`` (default: ``true``) + + ignore long constructor parameters list for data classes + +* ``ignoreAnnotatedParameter`` (default: ``[]``) + + ignore the annotated parameters for the count (e.g. `fun foo(@Value bar: Int)` would not be counted + +### MethodOverloading + +This rule reports methods which are overloaded often. +Method overloading tightly couples these methods together which might make the code harder to understand. + +Refactor these methods and try to use optional parameters instead to prevent some of the overloading. + +**Active by default**: No + +**Debt**: 20min + +#### Configuration options: + +* ``threshold`` (default: ``6``) + + number of overloads which will trigger the rule + +### NamedArguments + +Reports function invocations which have more arguments than a certain threshold and are all not named. Calls with +too many arguments are more difficult to understand so a named arguments help. + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 5min + +#### Configuration options: + +* ``threshold`` (default: ``3``) + + number of arguments that triggers this inspection + +* ``ignoreArgumentsMatchingNames`` (default: ``false``) + + ignores when argument values are the same as the parameter names + +#### Noncompliant Code: + +```kotlin +fun sum(a: Int, b: Int, c: Int, d: Int) { +} +sum(1, 2, 3, 4) +``` + +#### Compliant Code: + +```kotlin +fun sum(a: Int, b: Int, c: Int, d: Int) { +} +sum(a = 1, b = 2, c = 3, d = 4) +``` + +### NestedBlockDepth + +This rule reports excessive nesting depth in functions. Excessively nested code becomes harder to read and increases +its hidden complexity. It might become harder to understand edge-cases of the function. + +Prefer extracting the nested code into well-named functions to make it easier to understand. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 20min + +#### Configuration options: + +* ``threshold`` (default: ``4``) + + the nested depth required to trigger rule + +### NestedScopeFunctions + +Although the scope functions are a way of making the code more concise, avoid overusing them: it can decrease +your code readability and lead to errors. Avoid nesting scope functions and be careful when chaining them: +it's easy to get confused about the current context object and the value of this or it. + +[Reference](https://kotlinlang.org/docs/scope-functions.html) + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 5min + +#### Configuration options: + +* ``threshold`` (default: ``1``) + + Number of nested scope functions allowed. + +* ``functions`` (default: ``['kotlin.apply', 'kotlin.run', 'kotlin.with', 'kotlin.let', 'kotlin.also']``) + + Set of scope function names which add complexity. Function names have to be fully qualified. For example 'kotlin.apply'. + +#### Noncompliant Code: + +```kotlin +// Try to figure out, what changed, without knowing the details +first.apply { + second.apply { + b = a + c = b + } +} +``` + +#### Compliant Code: + +```kotlin +// 'a' is a property of current class +// 'b' is a property of class 'first' +// 'c' is a property of class 'second' +first.b = this.a +second.c = first.b +``` + +### ReplaceSafeCallChainWithRun + +Chains of safe calls on non-nullable types are redundant and can be removed by enclosing the redundant safe calls in +a `run {}` block. This improves code coverage and reduces cyclomatic complexity as redundant null checks are removed. + +This rule only checks from the end of a chain and works backwards, so it won't recommend inserting run blocks in the +middle of a safe call chain as that is likely to make the code more difficult to understand. + +The rule will check for every opportunity to replace a safe call when it sits at the end of a chain, even if there's +only one, as that will still improve code coverage and reduce cyclomatic complexity. + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 10min + +#### Noncompliant Code: + +```kotlin +val x = System.getenv() + ?.getValue("HOME") + ?.toLowerCase() + ?.split("/") ?: emptyList() +``` + +#### Compliant Code: + +```kotlin +val x = getenv()?.run { + getValue("HOME") + .toLowerCase() + .split("/") +} ?: emptyList() +``` + +### StringLiteralDuplication + +This rule detects and reports duplicated String literals. Repeatedly typing out the same String literal across the +codebase makes it harder to change and maintain. + +Instead, prefer extracting the String literal into a property or constant. + +**Active by default**: No + +**Debt**: 5min + +#### Configuration options: + +* ``threshold`` (default: ``3``) + + amount of duplications to trigger rule + +* ``ignoreAnnotation`` (default: ``true``) + + if values in Annotations should be ignored + +* ``excludeStringsWithLessThan5Characters`` (default: ``true``) + + if short strings should be excluded + +* ``ignoreStringsRegex`` (default: ``'$^'``) + + RegEx of Strings that should be ignored + +#### Noncompliant Code: + +```kotlin +class Foo { + + val s1 = "lorem" + fun bar(s: String = "lorem") { + s1.equals("lorem") + } +} +``` + +#### Compliant Code: + +```kotlin +class Foo { + val lorem = "lorem" + val s1 = lorem + fun bar(s: String = lorem) { + s1.equals(lorem) + } +} +``` + +### TooManyFunctions + +This rule reports files, classes, interfaces, objects and enums which contain too many functions. +Each element can be configured with different thresholds. + +Too many functions indicate a violation of the single responsibility principle. Prefer extracting functionality +which clearly belongs together in separate parts of the code. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 20min + +#### Configuration options: + +* ``thresholdInFiles`` (default: ``11``) + + threshold in files + +* ``thresholdInClasses`` (default: ``11``) + + threshold in classes + +* ``thresholdInInterfaces`` (default: ``11``) + + threshold in interfaces + +* ``thresholdInObjects`` (default: ``11``) + + threshold in objects + +* ``thresholdInEnums`` (default: ``11``) + + threshold in enums + +* ``ignoreDeprecated`` (default: ``false``) + + ignore deprecated functions + +* ``ignorePrivate`` (default: ``false``) + + ignore private functions + +* ``ignoreOverridden`` (default: ``false``) + + ignore overridden functions diff --git a/website/versioned_docs/version-1.22.0/rules/coroutines.md b/website/versioned_docs/version-1.22.0/rules/coroutines.md new file mode 100644 index 00000000000..4d311efe4fb --- /dev/null +++ b/website/versioned_docs/version-1.22.0/rules/coroutines.md @@ -0,0 +1,235 @@ +--- +title: Coroutines Rule Set +sidebar: home_sidebar +keywords: [rules, coroutines] +permalink: coroutines.html +toc: true +folder: documentation +--- +The coroutines rule set analyzes code for potential coroutines problems. + +### GlobalCoroutineUsage + +Report usages of `GlobalScope.launch` and `GlobalScope.async`. It is highly discouraged by the Kotlin documentation: + +> Global scope is used to launch top-level coroutines which are operating on the whole application lifetime and are +> not cancelled prematurely. + +> Application code usually should use an application-defined CoroutineScope. Using async or launch on the instance +> of GlobalScope is highly discouraged. + +See https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/ + +**Active by default**: No + +**Debt**: 10min + +#### Noncompliant Code: + +```kotlin +fun foo() { + GlobalScope.launch { delay(1_000L) } +} +``` + +#### Compliant Code: + +```kotlin +val scope = CoroutineScope(Dispatchers.Default) + +fun foo() { + scope.launch { delay(1_000L) } +} + +fun onDestroy() { + scope.cancel() +} +``` + +### InjectDispatcher + +Always use dependency injection to inject dispatchers for easier testing. +This rule is based on the recommendation +https://developer.android.com/kotlin/coroutines/coroutines-best-practices#inject-dispatchers + +**Active by default**: Yes - Since v1.21.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Configuration options: + +* ``dispatcherNames`` (default: ``['IO', 'Default', 'Unconfined']``) + + The names of dispatchers to detect by this rule + +#### Noncompliant Code: + +```kotlin +fun myFunc() { +coroutineScope(Dispatchers.IO) +} +``` + +#### Compliant Code: + +```kotlin +fun myFunc(dispatcher: CoroutineDispatcher = Dispatchers.IO) { +coroutineScope(dispatcher) +} + +class MyRepository(dispatchers: CoroutineDispatcher = Dispatchers.IO) +``` + +### RedundantSuspendModifier + +`suspend` modifier should only be used where needed, otherwise the function can only be used from other suspending +functions. This needlessly restricts use of the function and should be avoided by removing the `suspend` modifier +where it's not needed. + +**Active by default**: Yes - Since v1.21.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +suspend fun normalFunction() { + println("string") +} +``` + +#### Compliant Code: + +```kotlin +fun normalFunction() { + println("string") +} +``` + +### SleepInsteadOfDelay + +Report usages of `Thread.sleep` in suspending functions and coroutine blocks. A thread can +contain multiple coroutines at one time due to coroutines' lightweight nature, so if one +coroutine invokes `Thread.sleep`, it could potentially halt the execution of unrelated coroutines +and cause unpredictable behavior. + +**Active by default**: Yes - Since v1.21.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +suspend fun foo() { + Thread.sleep(1_000L) +} +``` + +#### Compliant Code: + +```kotlin +suspend fun foo() { + delay(1_000L) +} +``` + +### SuspendFunWithCoroutineScopeReceiver + +Suspend functions that use `CoroutineScope` as receiver should not be marked as `suspend`. +A `CoroutineScope` provides structured concurrency via its `coroutineContext`. A `suspend` +function also has its own `coroutineContext`, which is now ambiguous and mixed with the +receiver`s. + +See https://kotlinlang.org/docs/coroutines-basics.html#scope-builder-and-concurrency + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 10min + +#### Noncompliant Code: + +```kotlin +suspend fun CoroutineScope.foo() { + launch { + delay(1.seconds) + } +} +``` + +#### Compliant Code: + +```kotlin +fun CoroutineScope.foo() { + launch { + delay(1.seconds) + } +} + +// Alternative +suspend fun foo() = coroutineScope { + launch { + delay(1.seconds) + } +} +``` + +### SuspendFunWithFlowReturnType + +Functions that return `Flow` from `kotlinx.coroutines.flow` should not be marked as `suspend`. +`Flows` are intended to be cold observable streams. The act of simply invoking a function that +returns a `Flow`, should not have any side effects. Only once collection begins against the +returned `Flow`, should work actually be done. + +See https://kotlinlang.org/docs/flow.html#flows-are-cold + +**Active by default**: Yes - Since v1.21.0 + +**Requires Type Resolution** + +**Debt**: 10min + +#### Noncompliant Code: + +```kotlin +suspend fun observeSignals(): Flow { + val pollingInterval = getPollingInterval() // Done outside of the flow builder block. + return flow { + while (true) { + delay(pollingInterval) + emit(Unit) + } + } +} + +private suspend fun getPollingInterval(): Long { + // Return the polling interval from some repository + // in a suspending manner. +} +``` + +#### Compliant Code: + +```kotlin +fun observeSignals(): Flow { + return flow { + val pollingInterval = getPollingInterval() // Moved into the flow builder block. + while (true) { + delay(pollingInterval) + emit(Unit) + } + } +} + +private suspend fun getPollingInterval(): Long { + // Return the polling interval from some repository + // in a suspending manner. +} +``` diff --git a/website/versioned_docs/version-1.22.0/rules/empty-blocks.md b/website/versioned_docs/version-1.22.0/rules/empty-blocks.md new file mode 100644 index 00000000000..4f728e35889 --- /dev/null +++ b/website/versioned_docs/version-1.22.0/rules/empty-blocks.md @@ -0,0 +1,155 @@ +--- +title: Empty-blocks Rule Set +sidebar: home_sidebar +keywords: [rules, empty-blocks] +permalink: empty-blocks.html +toc: true +folder: documentation +--- +The empty-blocks ruleset contains rules that will report empty blocks of code +which should be avoided. + +### EmptyCatchBlock + +Reports empty `catch` blocks. Empty catch blocks indicate that an exception is ignored and not handled. +In case exceptions are ignored intentionally, this should be made explicit +by using the specified names in the `allowedExceptionNameRegex`. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +#### Configuration options: + +* ``allowedExceptionNameRegex`` (default: ``'_|(ignore|expected).*'``) + + ignores exception types which match this regex + +### EmptyClassBlock + +Reports empty classes. Empty blocks of code serve no purpose and should be removed. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +### EmptyDefaultConstructor + +Reports empty default constructors. Empty blocks of code serve no purpose and should be removed. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +### EmptyDoWhileBlock + +Reports empty `do`/`while` loops. Empty blocks of code serve no purpose and should be removed. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +### EmptyElseBlock + +Reports empty `else` blocks. Empty blocks of code serve no purpose and should be removed. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +### EmptyFinallyBlock + +Reports empty `finally` blocks. Empty blocks of code serve no purpose and should be removed. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +### EmptyForBlock + +Reports empty `for` loops. Empty blocks of code serve no purpose and should be removed. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +### EmptyFunctionBlock + +Reports empty functions. Empty blocks of code serve no purpose and should be removed. +This rule will not report functions with the override modifier that have a comment as their only body contents +(e.g., a // no-op comment in an unused listener function). + +Set the [ignoreOverridden] parameter to `true` to exclude all functions which are overriding other +functions from the superclass or from an interface (i.e., functions declared with the override modifier). + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +#### Configuration options: + +* ~~``ignoreOverriddenFunctions``~~ (default: ``false``) + + **Deprecated**: Use `ignoreOverridden` instead + + Excludes all the overridden functions + +* ``ignoreOverridden`` (default: ``false``) + + Excludes all the overridden functions + +### EmptyIfBlock + +Reports empty `if` blocks. Empty blocks of code serve no purpose and should be removed. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +### EmptyInitBlock + +Reports empty `init` expressions. Empty blocks of code serve no purpose and should be removed. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +### EmptyKtFile + +Reports empty Kotlin (.kt) files. Empty blocks of code serve no purpose and should be removed. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +### EmptySecondaryConstructor + +Reports empty secondary constructors. Empty blocks of code serve no purpose and should be removed. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +### EmptyTryBlock + +Reports empty `try` blocks. Empty blocks of code serve no purpose and should be removed. + +**Active by default**: Yes - Since v1.6.0 + +**Debt**: 5min + +### EmptyWhenBlock + +Reports empty `when` expressions. Empty blocks of code serve no purpose and should be removed. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +### EmptyWhileBlock + +Reports empty `while` expressions. Empty blocks of code serve no purpose and should be removed. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min diff --git a/website/versioned_docs/version-1.22.0/rules/exceptions.md b/website/versioned_docs/version-1.22.0/rules/exceptions.md new file mode 100644 index 00000000000..6b8745d0952 --- /dev/null +++ b/website/versioned_docs/version-1.22.0/rules/exceptions.md @@ -0,0 +1,507 @@ +--- +title: Exceptions Rule Set +sidebar: home_sidebar +keywords: [rules, exceptions] +permalink: exceptions.html +toc: true +folder: documentation +--- +Rules in this rule set report issues related to how code throws and handles Exceptions. + +### ExceptionRaisedInUnexpectedLocation + +This rule reports functions which should never throw an exception. If a function exists that does throw +an exception it will be reported. By default, this rule checks `toString`, `hashCode`, `equals` and +`finalize`. This rule is configurable via the `methodNames` configuration to change the list of functions which +should not throw any exceptions. + +**Active by default**: Yes - Since v1.16.0 + +**Debt**: 20min + +#### Configuration options: + +* ``methodNames`` (default: ``['equals', 'finalize', 'hashCode', 'toString']``) + + methods which should not throw exceptions + +#### Noncompliant Code: + +```kotlin +class Foo { + + override fun toString(): String { + throw IllegalStateException() // exception should not be thrown here + } +} +``` + +### InstanceOfCheckForException + +This rule reports `catch` blocks which check for the type of exception via `is` checks or casts. +Instead of catching generic exception types and then checking for specific exception types the code should +use multiple catch blocks. These catch blocks should then catch the specific exceptions. + +**Active by default**: Yes - Since v1.21.0 + +**Debt**: 20min + +#### Noncompliant Code: + +```kotlin +fun foo() { + try { + // ... do some I/O + } catch(e: IOException) { + if (e is MyException || (e as MyException) != null) { } + } +} +``` + +#### Compliant Code: + +```kotlin +fun foo() { + try { + // ... do some I/O + } catch(e: MyException) { + } catch(e: IOException) { + } +} +``` + +### NotImplementedDeclaration + +This rule reports all exceptions of the type `NotImplementedError` that are thrown. It also reports all `TODO(..)` +functions. +These indicate that functionality is still under development and will not work properly. Both of these should only +serve as temporary declarations and should not be put into production environments. + +**Active by default**: No + +**Debt**: 20min + +#### Noncompliant Code: + +```kotlin +fun foo() { + throw NotImplementedError() +} + +fun todo() { + TODO("") +} +``` + +### ObjectExtendsThrowable + +This rule reports all `objects` including `companion objects` that extend any type of +`Throwable`. Throwable instances are not intended for reuse as they are stateful and contain +mutable information about a specific exception or error. Hence, global singleton `Throwables` +should be avoided. + +See https://kotlinlang.org/docs/object-declarations.html#object-declarations-overview +See https://kotlinlang.org/docs/object-declarations.html#companion-objects + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 10min + +#### Noncompliant Code: + +```kotlin +object InvalidCredentialsException : Throwable() + +object BanException : Exception() + +object AuthException : RuntimeException() +``` + +#### Compliant Code: + +```kotlin +class InvalidCredentialsException : Throwable() + +class BanException : Exception() + +class AuthException : RuntimeException() +``` + +### PrintStackTrace + +This rule reports code that tries to print the stacktrace of an exception. Instead of simply printing a stacktrace +a better logging solution should be used. + +**Active by default**: Yes - Since v1.16.0 + +**Debt**: 20min + +#### Noncompliant Code: + +```kotlin +fun foo() { + Thread.dumpStack() +} + +fun bar() { + try { + // ... + } catch (e: IOException) { + e.printStackTrace() + } +} +``` + +#### Compliant Code: + +```kotlin +val LOGGER = Logger.getLogger() + +fun bar() { + try { + // ... + } catch (e: IOException) { + LOGGER.info(e) + } +} +``` + +### RethrowCaughtException + +This rule reports all exceptions that are caught and then later re-thrown without modification. +It ignores cases: +1. When caught exceptions that are rethrown if there is work done before that. +2. When there are more than one catch in try block and at least one of them has some work. + +**Active by default**: Yes - Since v1.16.0 + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +fun foo() { + try { + // ... + } catch (e: IOException) { + throw e + } +} +``` + +#### Compliant Code: + +```kotlin +fun foo() { + try { + // ... + } catch (e: IOException) { + throw MyException(e) + } + try { + // ... + } catch (e: IOException) { + print(e) + throw e + } + try { + // ... + } catch (e: IOException) { + print(e.message) + throw e + } + + try { + // ... + } catch (e: IOException) { + throw e + } catch (e: Exception) { + print(e.message) + } +} +``` + +### ReturnFromFinally + +Reports all `return` statements in `finally` blocks. +Using `return` statements in `finally` blocks can discard and hide exceptions that are thrown in the `try` block. +Furthermore, this rule reports values from `finally` blocks, if the corresponding `try` is used as an expression. + +**Active by default**: Yes - Since v1.16.0 + +**Requires Type Resolution** + +**Debt**: 20min + +#### Configuration options: + +* ``ignoreLabeled`` (default: ``false``) + + ignores labeled return statements + +#### Noncompliant Code: + +```kotlin +fun foo() { + try { + throw MyException() + } finally { + return // prevents MyException from being propagated + } +} + +val a: String = try { "s" } catch (e: Exception) { "e" } finally { "f" } +``` + +### SwallowedException + +Exceptions should not be swallowed. This rule reports all instances where exceptions are `caught` and not correctly +passed (e.g. as a cause) into a newly thrown exception. + +The exception types configured in `ignoredExceptionTypes` indicate nonexceptional outcomes. +These by default configured exception types are part of Java. +Therefore, Kotlin developers have to handle them by using the catch clause. +For that reason, this rule ignores that these configured exception types are caught. + +**Active by default**: Yes - Since v1.16.0 + +**Debt**: 20min + +#### Configuration options: + +* ``ignoredExceptionTypes`` (default: ``['InterruptedException', 'MalformedURLException', 'NumberFormatException', 'ParseException']``) + + exception types which should be ignored (both in the catch clause and body) + +* ``allowedExceptionNameRegex`` (default: ``'_|(ignore|expected).*'``) + + ignores too generic exception types which match this regex + +#### Noncompliant Code: + +```kotlin +fun foo() { + try { + // ... + } catch(e: IOException) { + throw MyException(e.message) // e is swallowed + } + try { + // ... + } catch(e: IOException) { + throw MyException() // e is swallowed + } + try { + // ... + } catch(e: IOException) { + bar() // exception is unused + } +} +``` + +#### Compliant Code: + +```kotlin +fun foo() { + try { + // ... + } catch(e: IOException) { + throw MyException(e) + } + try { + // ... + } catch(e: IOException) { + println(e) // logging is ok here + } +} +``` + +### ThrowingExceptionFromFinally + +This rule reports all cases where exceptions are thrown from a `finally` block. Throwing exceptions from a `finally` +block should be avoided as it can lead to confusion and discarded exceptions. + +**Active by default**: Yes - Since v1.16.0 + +**Debt**: 20min + +#### Noncompliant Code: + +```kotlin +fun foo() { + try { + // ... + } finally { + throw IOException() + } +} +``` + +### ThrowingExceptionInMain + +This rule reports all exceptions that are thrown in a `main` method. +An exception should only be thrown if it can be handled by a "higher" function. + +**Active by default**: No + +**Debt**: 20min + +#### Noncompliant Code: + +```kotlin +fun main(args: Array) { + // ... + throw IOException() // exception should not be thrown here +} +``` + +### ThrowingExceptionsWithoutMessageOrCause + +This rule reports all exceptions which are thrown without arguments or further description. +Exceptions should always call one of the constructor overloads to provide a message or a cause. +Exceptions should be meaningful and contain as much detail about the error case as possible. This will help to track +down an underlying issue in a better way. + +**Active by default**: Yes - Since v1.16.0 + +**Debt**: 5min + +#### Configuration options: + +* ``exceptions`` (default: ``['ArrayIndexOutOfBoundsException', 'Exception', 'IllegalArgumentException', 'IllegalMonitorStateException', 'IllegalStateException', 'IndexOutOfBoundsException', 'NullPointerException', 'RuntimeException', 'Throwable']``) + + exceptions which should not be thrown without message or cause + +#### Noncompliant Code: + +```kotlin +fun foo(bar: Int) { + if (bar < 1) { + throw IllegalArgumentException() + } + // ... +} +``` + +#### Compliant Code: + +```kotlin +fun foo(bar: Int) { + if (bar < 1) { + throw IllegalArgumentException("bar must be greater than zero") + } + // ... +} +``` + +### ThrowingNewInstanceOfSameException + +Exceptions should not be wrapped inside the same exception type and then rethrown. Prefer wrapping exceptions in more +meaningful exception types. + +**Active by default**: Yes - Since v1.16.0 + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +fun foo() { + try { + // ... + } catch (e: IllegalStateException) { + throw IllegalStateException(e) // rethrows the same exception + } +} +``` + +#### Compliant Code: + +```kotlin +fun foo() { + try { + // ... + } catch (e: IllegalStateException) { + throw MyException(e) + } +} +``` + +### TooGenericExceptionCaught + +This rule reports `catch` blocks for exceptions that have a type that is too generic. +It should be preferred to catch specific exceptions to the case that is currently handled. If the scope of the caught +exception is too broad it can lead to unintended exceptions being caught. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 20min + +#### Configuration options: + +* ``exceptionNames`` (default: ``['ArrayIndexOutOfBoundsException', 'Error', 'Exception', 'IllegalMonitorStateException', 'IndexOutOfBoundsException', 'NullPointerException', 'RuntimeException', 'Throwable']``) + + exceptions which are too generic and should not be caught + +* ``allowedExceptionNameRegex`` (default: ``'_|(ignore|expected).*'``) + + ignores too generic exception types which match this regex + +#### Noncompliant Code: + +```kotlin +fun foo() { + try { + // ... do some I/O + } catch(e: Exception) { } // too generic exception caught here +} +``` + +#### Compliant Code: + +```kotlin +fun foo() { + try { + // ... do some I/O + } catch(e: IOException) { } +} +``` + +### TooGenericExceptionThrown + +This rule reports thrown exceptions that have a type that is too generic. It should be preferred to throw specific +exceptions to the case that has currently occurred. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 20min + +#### Configuration options: + +* ``exceptionNames`` (default: ``['Error', 'Exception', 'RuntimeException', 'Throwable']``) + + exceptions which are too generic and should not be thrown + +#### Noncompliant Code: + +```kotlin +fun foo(bar: Int) { + if (bar < 1) { + throw Exception() // too generic exception thrown here + } + // ... +} +``` + +#### Compliant Code: + +```kotlin +fun foo(bar: Int) { + if (bar < 1) { + throw IllegalArgumentException("bar must be greater than zero") + } + // ... +} +``` diff --git a/website/versioned_docs/version-1.22.0/rules/formatting.md b/website/versioned_docs/version-1.22.0/rules/formatting.md new file mode 100644 index 00000000000..b62dc6f57e0 --- /dev/null +++ b/website/versioned_docs/version-1.22.0/rules/formatting.md @@ -0,0 +1,539 @@ +--- +title: Formatting Rule Set +sidebar: home_sidebar +keywords: [rules, formatting] +permalink: formatting.html +toc: true +folder: documentation +--- +This rule set provides wrappers for rules implemented by ktlint - https://ktlint.github.io/. + +Note: Issues reported by this rule set can only be suppressed on file level (`@file:Suppress("detekt.rule")`). +Note: The formatting rule set is not included in the detekt-cli or gradle plugin. + +To enable this rule set, add `detektPlugins "io.gitlab.arturbosch.detekt:detekt-formatting:$version"` +to your gradle dependencies or reference the `detekt-formatting`-jar with the `--plugins` option +in the command line interface. + +### AnnotationOnSeparateLine + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#annotation-formatting) for documentation. + +**Active by default**: Yes - Since v1.22.0 + +### AnnotationSpacing + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#annotation-spacing) for documentation. + +**Active by default**: Yes - Since v1.22.0 + +### ArgumentListWrapping + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#argument-list-wrapping) for documentation. + +**Active by default**: Yes - Since v1.22.0 + +#### Configuration options: + +* ``indentSize`` (default: ``4``) + + indentation size + +* ``maxLineLength`` (default: ``120``) (android default: ``100``) + + maximum line length + +### BlockCommentInitialStarAlignment + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/experimental/#block-comment-initial-star-alignment) for +documentation. + +**Active by default**: No + +### ChainWrapping + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#chain-wrapping) for documentation. + +**Active by default**: Yes - Since v1.0.0 + +### CommentSpacing + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#comment-spacing) for documentation. + +**Active by default**: Yes - Since v1.0.0 + +### CommentWrapping + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/experimental/#comment-wrapping) for documentation. + +**Active by default**: No + +#### Configuration options: + +* ``indentSize`` (default: ``4``) + + indentation size + +### DiscouragedCommentLocation + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/experimental/#discouraged-comment-location) for +documentation. + +**Active by default**: No + +### EnumEntryNameCase + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#enum-entry) for documentation. + +**Active by default**: Yes - Since v1.22.0 + +### Filename + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#file-name) for documentation. + +This rules overlaps with [naming>MatchingDeclarationName](https://detekt.dev/naming.html#matchingdeclarationname) +from the standard rules, make sure to enable just one. + +**Active by default**: Yes - Since v1.0.0 + +### FinalNewline + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#final-newline) for documentation. + +This rules overlaps with [style>NewLineAtEndOfFile](https://detekt.dev/style.html#newlineatendoffile) +from the standard rules, make sure to enable just one. The pro of this rule is that it can auto-correct the issue. + +**Active by default**: Yes - Since v1.0.0 + +#### Configuration options: + +* ``insertFinalNewLine`` (default: ``true``) + + report absence or presence of a newline + +### FunKeywordSpacing + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/experimental/#fun-keyword-spacing) for documentation. + +**Active by default**: No + +### FunctionReturnTypeSpacing + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/experimental/#function-return-type-spacing) for +documentation. + +**Active by default**: No + +### FunctionSignature + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/experimental/#function-signature) for +documentation. + +**Active by default**: No + +#### Configuration options: + +* ``forceMultilineWhenParameterCountGreaterOrEqualThan`` (default: ``2147483647``) + + parameter count means multiline threshold + +* ``functionBodyExpressionWrapping`` (default: ``'default'``) + + indentation size + +* ``maxLineLength`` (default: ``120``) (android default: ``100``) + + maximum line length + +* ``indentSize`` (default: ``4``) + + indentation size + +### FunctionStartOfBodySpacing + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/experimental/#function-start-of-body-spacing) for +documentation. + +**Active by default**: No + +### FunctionTypeReferenceSpacing + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/experimental/#function-type-reference-spacing) for +documentation. + +**Active by default**: No + +### ImportOrdering + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#import-ordering) for documentation. + +For defining import layout patterns see the [KtLint Source Code](https://github.com/pinterest/ktlint/blob/a6ca5b2edf95cc70a138a9470cfb6c4fd5d9d3ce/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ImportOrderingRule.kt) + +**Active by default**: Yes - Since v1.19.0 + +#### Configuration options: + +* ``layout`` (default: ``'*,java.**,javax.**,kotlin.**,^'``) (android default: ``'*'``) + + the import ordering layout + +### Indentation + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#indentation) for documentation. + +**Active by default**: Yes - Since v1.19.0 + +#### Configuration options: + +* ``indentSize`` (default: ``4``) + + indentation size + +* ~~``continuationIndentSize``~~ (default: ``4``) + + **Deprecated**: `continuationIndentSize` is ignored by KtLint and will have no effect + + continuation indentation size + +### KdocWrapping + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/experimental/#kdoc-wrapping) for documentation. + +**Active by default**: No + +#### Configuration options: + +* ``indentSize`` (default: ``4``) + + indentation size + +### MaximumLineLength + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#max-line-length) for documentation. + +This rules overlaps with [style>MaxLineLength](https://detekt.dev/style.html#maxlinelength) +from the standard rules, make sure to enable just one or keep them aligned. The pro of this rule is that it can +auto-correct the issue. + +**Active by default**: Yes - Since v1.0.0 + +#### Configuration options: + +* ``maxLineLength`` (default: ``120``) (android default: ``100``) + + maximum line length + +* ``ignoreBackTickedIdentifier`` (default: ``false``) + + ignore back ticked identifier + +### ModifierListSpacing + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/experimental/#modifier-list-spacing) for documentation. + +**Active by default**: No + +### ModifierOrdering + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#modifier-order) for documentation. + +This rules overlaps with [style>ModifierOrder](https://detekt.dev/style.html#modifierorder) +from the standard rules, make sure to enable just one. The pro of this rule is that it can auto-correct the issue. + +**Active by default**: Yes - Since v1.0.0 + +### MultiLineIfElse + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#multiline-if-else) for documentation. + +**Active by default**: Yes - Since v1.22.0 + +### NoBlankLineBeforeRbrace + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#no-blank-lines-before) for documentation. + +**Active by default**: Yes - Since v1.0.0 + +### NoBlankLinesInChainedMethodCalls + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#no-blank-lines-in-chained-method-calls) for +documentation. + +**Active by default**: Yes - Since v1.22.0 + +### NoConsecutiveBlankLines + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#no-consecutive-blank-lines) for documentation. + +**Active by default**: Yes - Since v1.0.0 + +### NoEmptyClassBody + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#no-empty-class-bodies) for documentation. + +**Active by default**: Yes - Since v1.0.0 + +### NoEmptyFirstLineInMethodBlock + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#no-leading-empty-lines-in-method-blocks) for +documentation. + +**Active by default**: Yes - Since v1.22.0 + +### NoLineBreakAfterElse + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#no-line-break-after-else) for documentation. + +**Active by default**: Yes - Since v1.0.0 + +### NoLineBreakBeforeAssignment + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#no-line-break-before-assignment) for +documentation. + +**Active by default**: Yes - Since v1.0.0 + +### NoMultipleSpaces + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#no-multi-spaces) for documentation. + +**Active by default**: Yes - Since v1.0.0 + +### NoSemicolons + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#no-semicolons) for documentation. + +**Active by default**: Yes - Since v1.0.0 + +### NoTrailingSpaces + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#no-trailing-whitespaces) for documentation. + +**Active by default**: Yes - Since v1.0.0 + +### NoUnitReturn + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#no-unit-as-return-type) for documentation. + +**Active by default**: Yes - Since v1.0.0 + +### NoUnusedImports + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#no-unused-imports) for documentation. + +**Active by default**: Yes - Since v1.0.0 + +### NoWildcardImports + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#no-wildcard-imports) for documentation. + +**Active by default**: Yes - Since v1.0.0 + +#### Configuration options: + +* ``packagesToUseImportOnDemandProperty`` (default: ``'java.util.*,kotlinx.android.synthetic.**'``) + + Defines allowed wildcard imports + +### NullableTypeSpacing + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/experimental/#nullable-type-spacing) for +documentation. + +**Active by default**: No + +### PackageName + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#no-underscores-in-package-names) for +documentation. + +**Active by default**: Yes - Since v1.22.0 + +### ParameterListSpacing + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/experimental/#parameter-list-spacing) for +documentation. + +**Active by default**: No + +### ParameterListWrapping + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#parameter-list-wrapping) for documentation. + +**Active by default**: Yes - Since v1.0.0 + +#### Configuration options: + +* ``maxLineLength`` (default: ``120``) (android default: ``100``) + + maximum line length + +* ~~``indentSize``~~ (default: ``4``) + + **Deprecated**: `indentSize` is ignored by KtLint and will have no effect + + indentation size + +### SpacingAroundAngleBrackets + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#angle-bracket-spacing) for documentation. + +**Active by default**: Yes - Since v1.22.0 + +### SpacingAroundColon + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#colon-spacing) for documentation. + +**Active by default**: Yes - Since v1.0.0 + +### SpacingAroundComma + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#comma-spacing) for documentation. + +**Active by default**: Yes - Since v1.0.0 + +### SpacingAroundCurly + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#curly-spacing) for documentation. + +**Active by default**: Yes - Since v1.0.0 + +### SpacingAroundDot + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#dot-spacing) for documentation. + +**Active by default**: Yes - Since v1.0.0 + +### SpacingAroundDoubleColon + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#double-colon-spacing) for documentation. + +**Active by default**: Yes - Since v1.22.0 + +### SpacingAroundKeyword + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#keyword-spacing) for documentation. + +**Active by default**: Yes - Since v1.0.0 + +### SpacingAroundOperators + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#operator-spacing) for documentation. + +**Active by default**: Yes - Since v1.0.0 + +### SpacingAroundParens + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#parenthesis-spacing) for documentation. + +**Active by default**: Yes - Since v1.0.0 + +### SpacingAroundRangeOperator + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#range-spacing) for documentation. + +**Active by default**: Yes - Since v1.0.0 + +### SpacingAroundUnaryOperator + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#unary-operator-spacing) for documentation. + +**Active by default**: Yes - Since v1.22.0 + +### SpacingBetweenDeclarationsWithAnnotations + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#blank-line-between-declarations-with-annotations) +for documentation. + +**Active by default**: Yes - Since v1.22.0 + +### SpacingBetweenDeclarationsWithComments + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#blank-line-between-declaration-with-comments) +for documentation. + +**Active by default**: Yes - Since v1.22.0 + +### SpacingBetweenFunctionNameAndOpeningParenthesis + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/experimental/#spacing-between-function-name-and-opening-parenthesis) for +documentation. + +**Active by default**: No + +### StringTemplate + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#string-template) for documentation. + +**Active by default**: Yes - Since v1.0.0 + +### TrailingCommaOnCallSite + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/) for documentation. + +The default config comes from ktlint and follows these conventions: +- [Kotlin coding convention](https://kotlinlang.org/docs/coding-conventions.html#trailing-commas) recommends +trailing comma encourage the use of trailing commas at the declaration site and +leaves it at your discretion for the call site. +- [Android Kotlin style guide](https://developer.android.com/kotlin/style-guide) does not include +trailing comma usage yet. + +**Active by default**: No + +#### Configuration options: + +* ``useTrailingCommaOnCallSite`` (default: ``true``) (android default: ``false``) + + Defines whether trailing commas are required (true) or forbidden (false) at call sites + +### TrailingCommaOnDeclarationSite + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/) for documentation. + +The default config comes from ktlint and follows these conventions: +- [Kotlin coding convention](https://kotlinlang.org/docs/coding-conventions.html#trailing-commas) recommends +trailing comma encourage the use of trailing commas at the declaration site and +leaves it at your discretion for the call site. +- [Android Kotlin style guide](https://developer.android.com/kotlin/style-guide) does not include +trailing comma usage yet. + +**Active by default**: No + +#### Configuration options: + +* ``useTrailingCommaOnDeclarationSite`` (default: ``true``) (android default: ``false``) + + Defines whether trailing commas are required (true) or forbidden (false) at declaration sites + +### TypeArgumentListSpacing + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/experimental/#type-argument-list-spacing) for +documentation. + +**Active by default**: No + +### TypeParameterListSpacing + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/experimental/#type-parameter-list-spacing) for +documentation. + +**Active by default**: No + +### UnnecessaryParenthesesBeforeTrailingLambda + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/experimental/#unnecessary-parenthesis-before-trailing-lambda) +for documentation. + +**Active by default**: No + +### Wrapping + +See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#wrapping) for documentation. + +**Active by default**: Yes - Since v1.20.0 + +#### Configuration options: + +* ``indentSize`` (default: ``4``) + + indentation size diff --git a/website/versioned_docs/version-1.22.0/rules/libraries.md b/website/versioned_docs/version-1.22.0/rules/libraries.md new file mode 100644 index 00000000000..d8be90d851e --- /dev/null +++ b/website/versioned_docs/version-1.22.0/rules/libraries.md @@ -0,0 +1,97 @@ +--- +title: Libraries Rule Set +sidebar: home_sidebar +keywords: [rules, libraries] +permalink: libraries.html +toc: true +folder: documentation +--- +Rules in this rule set report issues related to libraries API exposure. + +### ForbiddenPublicDataClass + +Data classes are bad for binary compatibility in public APIs. Avoid using them. + +This rule is aimed at library maintainers. If you are developing a final application you can ignore this issue. + +More info: [Public API challenges in Kotlin](https://jakewharton.com/public-api-challenges-in-kotlin/) + +**Active by default**: Yes - Since v1.16.0 + +**Debt**: 20min + +#### Configuration options: + +* ``ignorePackages`` (default: ``['*.internal', '*.internal.*']``) + + ignores classes in the specified packages. + +#### Noncompliant Code: + +```kotlin +data class C(val a: String) // violation: public data class +``` + +#### Compliant Code: + +```kotlin +internal data class C(val a: String) +``` + +### LibraryCodeMustSpecifyReturnType + +Functions/properties exposed as public APIs of a library should have an explicit return type. +Inferred return type can easily be changed by mistake which may lead to breaking changes. + +See also: [Kotlin 1.4 Explicit API](https://kotlinlang.org/docs/whatsnew14.html#explicit-api-mode-for-library-authors) + +**Active by default**: Yes - Since v1.2.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +// code from a library +val strs = listOf("foo, bar") +fun bar() = 5 +class Parser { + fun parse() = ... +} +``` + +#### Compliant Code: + +```kotlin +// code from a library +val strs: List = listOf("foo, bar") +fun bar(): Int = 5 + +class Parser { + fun parse(): ParsingResult = ... +} +``` + +### LibraryEntitiesShouldNotBePublic + +Library typealias and classes should be internal or private. + +**Active by default**: Yes - Since v1.16.0 + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +// code from a library +class A +``` + +#### Compliant Code: + +```kotlin +// code from a library +internal class A +``` diff --git a/website/versioned_docs/version-1.22.0/rules/naming.md b/website/versioned_docs/version-1.22.0/rules/naming.md new file mode 100644 index 00000000000..d5c6b861221 --- /dev/null +++ b/website/versioned_docs/version-1.22.0/rules/naming.md @@ -0,0 +1,495 @@ +--- +title: Naming Rule Set +sidebar: home_sidebar +keywords: [rules, naming] +permalink: naming.html +toc: true +folder: documentation +--- +The naming ruleset contains rules which assert the naming of different parts of the codebase. + +### BooleanPropertyNaming + +Reports when a boolean property doesn't match a pattern + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 5min + +#### Configuration options: + +* ``allowedPattern`` (default: ``'^(is|has|are)'``) + + naming pattern + +* ``ignoreOverridden`` (default: ``true``) + + ignores properties that have the override modifier + +#### Noncompliant Code: + +```kotlin +val progressBar: Boolean = true +``` + +#### Compliant Code: + +```kotlin +val hasProgressBar: Boolean = true +``` + +### ClassNaming + +Reports class or object names that do not follow the specified naming convention. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +**Aliases**: ClassName + +#### Configuration options: + +* ``classPattern`` (default: ``'[A-Z][a-zA-Z0-9]*'``) + + naming pattern + +### ConstructorParameterNaming + +Reports constructor parameter names that do not follow the specified naming convention. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +#### Configuration options: + +* ``parameterPattern`` (default: ``'[a-z][A-Za-z0-9]*'``) + + naming pattern + +* ``privateParameterPattern`` (default: ``'[a-z][A-Za-z0-9]*'``) + + naming pattern + +* ``excludeClassPattern`` (default: ``'$^'``) + + ignores variables in classes which match this regex + +* ``ignoreOverridden`` (default: ``true``) + + ignores constructor properties that have the override modifier + +### EnumNaming + +Reports enum names that do not follow the specified naming convention. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +#### Configuration options: + +* ``enumEntryPattern`` (default: ``'[A-Z][_a-zA-Z0-9]*'``) + + naming pattern + +### ForbiddenClassName + +Reports class names which are forbidden per configuration. By default, this rule does not report any classes. +Examples for forbidden names might be too generic class names like `...Manager`. + +**Active by default**: No + +**Debt**: 5min + +#### Configuration options: + +* ``forbiddenName`` (default: ``[]``) + + forbidden class names + +### FunctionMaxLength + +Reports when very long function names are used. + +**Active by default**: No + +**Debt**: 5min + +#### Configuration options: + +* ``maximumFunctionNameLength`` (default: ``30``) + + maximum name length + +### FunctionMinLength + +Reports when very short function names are used. + +**Active by default**: No + +**Debt**: 5min + +#### Configuration options: + +* ``minimumFunctionNameLength`` (default: ``3``) + + minimum name length + +### FunctionNaming + +Reports function names that do not follow the specified naming convention. +One exception are factory functions used to create instances of classes. +These factory functions can have the same name as the class being created. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +**Aliases**: FunctionName + +#### Configuration options: + +* ``functionPattern`` (default: ``'[a-z][a-zA-Z0-9]*'``) + + naming pattern + +* ``excludeClassPattern`` (default: ``'$^'``) + + ignores functions in classes which match this regex + +* ``ignoreOverridden`` (default: ``true``) + + ignores functions that have the override modifier + +### FunctionParameterNaming + +Reports function parameter names that do not follow the specified naming convention. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +#### Configuration options: + +* ``parameterPattern`` (default: ``'[a-z][A-Za-z0-9]*'``) + + naming pattern + +* ``excludeClassPattern`` (default: ``'$^'``) + + ignores variables in classes which match this regex + +* ~~``ignoreOverriddenFunctions``~~ (default: ``true``) + + **Deprecated**: Use `ignoreOverridden` instead + + ignores overridden functions with parameters not matching the pattern + +* ``ignoreOverridden`` (default: ``true``) + + ignores overridden functions with parameters not matching the pattern + +### InvalidPackageDeclaration + +Reports when the file location does not match the declared package. + +**Active by default**: Yes - Since v1.21.0 + +**Debt**: 5min + +#### Configuration options: + +* ``rootPackage`` (default: ``''``) + + if specified this part of the package structure is ignored + +* ``requireRootInDeclaration`` (default: ``false``) + + requires the declaration to start with the specified rootPackage + +### LambdaParameterNaming + +Reports lambda parameter names that do not follow the specified naming convention. + +**Active by default**: No + +**Debt**: 5min + +#### Configuration options: + +* ``parameterPattern`` (default: ``'[a-z][A-Za-z0-9]*|_'``) + + naming pattern + +### MatchingDeclarationName + +"If a Kotlin file contains a single non-private class (potentially with related top-level declarations), +its name should be the same as the name of the class, with the .kt extension appended. +If a file contains multiple classes, or only top-level declarations, +choose a name describing what the file contains, and name the file accordingly. +Use camel humps with an uppercase first letter (e.g. ProcessDeclarations.kt). + +The name of the file should describe what the code in the file does. +Therefore, you should avoid using meaningless words such as "Util" in file names." - Official Kotlin Style Guide + +More information at: https://kotlinlang.org/docs/coding-conventions.html + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +#### Configuration options: + +* ``mustBeFirst`` (default: ``true``) + + name should only be checked if the file starts with a class or object + +#### Noncompliant Code: + +```kotlin +class Foo // FooUtils.kt + +fun Bar.toFoo(): Foo = ... +fun Foo.toBar(): Bar = ... +``` + +#### Compliant Code: + +```kotlin +class Foo { // Foo.kt + fun stuff() = 42 +} + +fun Bar.toFoo(): Foo = ... +``` + +### MemberNameEqualsClassName + +This rule reports a member that has the same as the containing class or object. +This might result in confusion. +The member should either be renamed or changed to a constructor. +Factory functions that create an instance of the class are exempt from this rule. + +**Active by default**: Yes - Since v1.2.0 + +**Debt**: 5min + +#### Configuration options: + +* ~~``ignoreOverriddenFunction``~~ (default: ``true``) + + **Deprecated**: Use `ignoreOverridden` instead + + if overridden functions and properties should be ignored + +* ``ignoreOverridden`` (default: ``true``) + + if overridden functions and properties should be ignored + +#### Noncompliant Code: + +```kotlin +class MethodNameEqualsClassName { + + fun methodNameEqualsClassName() { } +} + +class PropertyNameEqualsClassName { + + val propertyEqualsClassName = 0 +} +``` + +#### Compliant Code: + +```kotlin +class Manager { + + companion object { + // factory functions can have the same name as the class + fun manager(): Manager { + return Manager() + } + } +} +``` + +### NoNameShadowing + +Disallows shadowing variable declarations. +Shadowing makes it impossible to access a variable with the same name in the scope. + +**Active by default**: Yes - Since v1.21.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +fun test(i: Int, j: Int, k: Int) { + val i = 1 + val (j, _) = 1 to 2 + listOf(1).map { k -> println(k) } + listOf(1).forEach { + listOf(2).forEach { + } + } +} +``` + +#### Compliant Code: + +```kotlin +fun test(i: Int, j: Int, k: Int) { + val x = 1 + val (y, _) = 1 to 2 + listOf(1).map { z -> println(z) } + listOf(1).forEach { + listOf(2).forEach { x -> + } + } +} +``` + +### NonBooleanPropertyPrefixedWithIs + +Reports when property with 'is' prefix doesn't have a boolean type. +Please check the [chapter 8.3.2 at Java Language Specification](https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.3.2) + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +val isEnabled: Int = 500 +``` + +#### Compliant Code: + +```kotlin +val isEnabled: Boolean = false +``` + +### ObjectPropertyNaming + +Reports property names inside objects that do not follow the specified naming convention. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +#### Configuration options: + +* ``constantPattern`` (default: ``'[A-Za-z][_A-Za-z0-9]*'``) + + naming pattern + +* ``propertyPattern`` (default: ``'[A-Za-z][_A-Za-z0-9]*'``) + + naming pattern + +* ``privatePropertyPattern`` (default: ``'(_)?[A-Za-z][_A-Za-z0-9]*'``) + + naming pattern + +### PackageNaming + +Reports package names that do not follow the specified naming convention. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +**Aliases**: PackageDirectoryMismatch + +#### Configuration options: + +* ``packagePattern`` (default: ``'[a-z]+(\.[a-z][A-Za-z0-9]*)*'``) + + naming pattern + +### TopLevelPropertyNaming + +Reports top level constant that which do not follow the specified naming convention. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +#### Configuration options: + +* ``constantPattern`` (default: ``'[A-Z][_A-Z0-9]*'``) + + naming pattern + +* ``propertyPattern`` (default: ``'[A-Za-z][_A-Za-z0-9]*'``) + + naming pattern + +* ``privatePropertyPattern`` (default: ``'_?[A-Za-z][_A-Za-z0-9]*'``) + + naming pattern + +### VariableMaxLength + +Reports when very long variable names are used. + +**Active by default**: No + +**Debt**: 5min + +#### Configuration options: + +* ``maximumVariableNameLength`` (default: ``64``) + + maximum name length + +### VariableMinLength + +Reports when very short variable names are used. + +**Active by default**: No + +**Debt**: 5min + +#### Configuration options: + +* ``minimumVariableNameLength`` (default: ``1``) + + minimum name length + +### VariableNaming + +Reports variable names that do not follow the specified naming convention. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +#### Configuration options: + +* ``variablePattern`` (default: ``'[a-z][A-Za-z0-9]*'``) + + naming pattern + +* ``privateVariablePattern`` (default: ``'(_)?[a-z][A-Za-z0-9]*'``) + + naming pattern + +* ``excludeClassPattern`` (default: ``'$^'``) + + ignores variables in classes which match this regex + +* ``ignoreOverridden`` (default: ``true``) + + ignores member properties that have the override modifier diff --git a/website/versioned_docs/version-1.22.0/rules/performance.md b/website/versioned_docs/version-1.22.0/rules/performance.md new file mode 100644 index 00000000000..eb5fcecb719 --- /dev/null +++ b/website/versioned_docs/version-1.22.0/rules/performance.md @@ -0,0 +1,194 @@ +--- +title: Performance Rule Set +sidebar: home_sidebar +keywords: [rules, performance] +permalink: performance.html +toc: true +folder: documentation +--- +The performance rule set analyzes code for potential performance problems. + +### ArrayPrimitive + +Using Array<Primitive> leads to implicit boxing and performance hit. Prefer using Kotlin specialized Array +Instances. + +As stated in the Kotlin [documentation](https://kotlinlang.org/docs/basic-types.html#arrays) Kotlin has +specialized arrays to represent primitive types without boxing overhead, such as `IntArray`, `ByteArray` and so on. + +**Active by default**: Yes - Since v1.2.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +fun function(array: Array) { } + +fun returningFunction(): Array { } +``` + +#### Compliant Code: + +```kotlin +fun function(array: IntArray) { } + +fun returningFunction(): DoubleArray { } +``` + +### CouldBeSequence + +Long chains of collection operations will have a performance penalty due to a new list being created for each call. Consider using sequences instead. Read more about this in the [documentation](https://kotlinlang.org/docs/sequences.html) + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 5min + +#### Configuration options: + +* ``threshold`` (default: ``3``) + + the number of chained collection operations required to trigger rule + +#### Noncompliant Code: + +```kotlin +listOf(1, 2, 3, 4).map { it*2 }.filter { it < 4 }.map { it*it } +``` + +#### Compliant Code: + +```kotlin +listOf(1, 2, 3, 4).asSequence().map { it*2 }.filter { it < 4 }.map { it*it }.toList() + +listOf(1, 2, 3, 4).map { it*2 } +``` + +### ForEachOnRange + +Using the forEach method on ranges has a heavy performance cost. Prefer using simple for loops. + +Benchmarks have shown that using forEach on a range can have a huge performance cost in comparison to +simple for loops. Hence, in most contexts, a simple for loop should be used instead. +See more details here: https://sites.google.com/a/athaydes.com/renato-athaydes/posts/kotlinshiddencosts-benchmarks +To solve this CodeSmell, the forEach usage should be replaced by a for loop. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +(1..10).forEach { + println(it) +} +(1 until 10).forEach { + println(it) +} +(10 downTo 1).forEach { + println(it) +} +``` + +#### Compliant Code: + +```kotlin +for (i in 1..10) { + println(i) +} +``` + +### SpreadOperator + +In most cases using a spread operator causes a full copy of the array to be created before calling a method. +This has a very high performance penalty. Benchmarks showing this performance penalty can be seen here: +https://sites.google.com/a/athaydes.com/renato-athaydes/posts/kotlinshiddencosts-benchmarks + +The Kotlin compiler since v1.1.60 has an optimization that skips the array copy when an array constructor +function is used to create the arguments that are passed to the vararg parameter. When type resolution is enabled in +detekt this case will not be flagged by the rule since it doesn't suffer the performance penalty of an array copy. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 20min + +#### Noncompliant Code: + +```kotlin +val strs = arrayOf("value one", "value two") +val foo = bar(*strs) + +fun bar(vararg strs: String) { + strs.forEach { println(it) } +} +``` + +#### Compliant Code: + +```kotlin +// array copy skipped in this case since Kotlin 1.1.60 +val foo = bar(*arrayOf("value one", "value two")) + +// array not passed so no array copy is required +val foo2 = bar("value one", "value two") + +fun bar(vararg strs: String) { + strs.forEach { println(it) } +} +``` + +### UnnecessaryPartOfBinaryExpression + +Unnecessary binary expression add complexity to the code and accomplish nothing. They should be removed. +The rule works with all binary expression included if and when condition. The rule also works with all predicates. +The rule verify binary expression only in case when the expression use only one type of the following +operators || or &&. + +**Active by default**: No + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +val foo = true +val bar = true + +if (foo || bar || foo) { +} +``` + +#### Compliant Code: + +```kotlin +val foo = true +if (foo) { +} +``` + +### UnnecessaryTemporaryInstantiation + +Avoid temporary objects when converting primitive types to String. This has a performance penalty when compared +to using primitive types directly. +To solve this issue, remove the wrapping type. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +val i = Integer(1).toString() // temporary Integer instantiation just for the conversion +``` + +#### Compliant Code: + +```kotlin +val i = Integer.toString(1) +``` diff --git a/website/versioned_docs/version-1.22.0/rules/potential-bugs.md b/website/versioned_docs/version-1.22.0/rules/potential-bugs.md new file mode 100644 index 00000000000..4d80023303a --- /dev/null +++ b/website/versioned_docs/version-1.22.0/rules/potential-bugs.md @@ -0,0 +1,1189 @@ +--- +title: Potential-bugs Rule Set +sidebar: home_sidebar +keywords: [rules, potential-bugs] +permalink: potential-bugs.html +toc: true +folder: documentation +--- +The potential-bugs rule set provides rules that detect potential bugs. + +### AvoidReferentialEquality + +Kotlin supports two types of equality: structural equality and referential equality. While there are +use cases for both, checking for referential equality for some types (such as `String` or `List`) is +likely not intentional and may cause unexpected results. + +**Active by default**: Yes - Since v1.21.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Configuration options: + +* ``forbiddenTypePatterns`` (default: ``['kotlin.String']``) + + Specifies those types for which referential equality checks are considered a rule violation. The types are defined by a list of simple glob patterns (supporting `*` and `?` wildcards) that match the fully qualified type name. + +#### Noncompliant Code: + +```kotlin + val areEqual = "aString" === otherString + val areNotEqual = "aString" !== otherString +``` + +#### Compliant Code: + +```kotlin + val areEqual = "aString" == otherString + val areNotEqual = "aString" != otherString +``` + +### CastToNullableType + +Disallow to cast to nullable types. +There are cases where `as String?` is misused as safe cast (`as? String`). +So if you want to prevent those cases, turn on this rule. + +**Active by default**: No + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +fun foo(a: Any?) { + val x: String? = a as String? // If 'a' is not String, ClassCastException will be thrown. +} +``` + +#### Compliant Code: + +```kotlin +fun foo(a: Any?) { + val x: String? = a as? String +} +``` + +### Deprecation + +Deprecated elements are expected to be removed in the future. Alternatives should be found if possible. + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 20min + +**Aliases**: DEPRECATION + +### DontDowncastCollectionTypes + +Down-casting immutable types from kotlin.collections should be discouraged. +The result of the downcast is platform specific and can lead to unexpected crashes. +Prefer to use instead the `toMutable<Type>()` functions. + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 10min + +#### Noncompliant Code: + +```kotlin +val list : List = getAList() +if (list is MutableList) { + list.add(42) +} + +(list as MutableList).add(42) +``` + +#### Compliant Code: + +```kotlin +val list : List = getAList() +list.toMutableList().add(42) +``` + +### DoubleMutabilityForCollection + +Using `var` when declaring a mutable collection or value holder leads to double mutability. +Consider instead declaring your variable with `val` or switching your declaration to use an +immutable type. + +By default, the rule triggers on standard mutable collections, however it can be configured +to trigger on other types of mutable value types, such as `MutableState` from Jetpack +Compose. + +**Active by default**: Yes - Since v1.21.0 + +**Requires Type Resolution** + +**Debt**: 5min + +**Aliases**: DoubleMutability + +#### Configuration options: + +* ``mutableTypes`` (default: ``['kotlin.collections.MutableList', 'kotlin.collections.MutableMap', 'kotlin.collections.MutableSet', 'java.util.ArrayList', 'java.util.LinkedHashSet', 'java.util.HashSet', 'java.util.LinkedHashMap', 'java.util.HashMap']``) + + Define a list of mutable types to trigger on when defined with `var`. + +#### Noncompliant Code: + +```kotlin +var myList = mutableListOf(1,2,3) +var mySet = mutableSetOf(1,2,3) +var myMap = mutableMapOf("answer" to 42) +``` + +#### Compliant Code: + +```kotlin +// Use val +val myList = mutableListOf(1,2,3) +val mySet = mutableSetOf(1,2,3) +val myMap = mutableMapOf("answer" to 42) + +// Use immutable types +var myList = listOf(1,2,3) +var mySet = setOf(1,2,3) +var myMap = mapOf("answer" to 42) +``` + +### ~~DuplicateCaseInWhenExpression~~ + +Rule deprecated as compiler performs this check by default + +Flags duplicate `case` statements in `when` expressions. + +If a `when` expression contains the same `case` statement multiple times they should be merged. Otherwise, it might be +easy to miss one of the cases when reading the code, leading to unwanted side effects. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 10min + +#### Noncompliant Code: + +```kotlin +when (i) { + 1 -> println("one") + 1 -> println("one") + else -> println("else") +} +``` + +#### Compliant Code: + +```kotlin +when (i) { + 1 -> println("one") + else -> println("else") +} +``` + +### ElseCaseInsteadOfExhaustiveWhen + +This rule reports `when` expressions that contain an `else` case even though they have an exhaustive set of cases. + +This occurs when the subject of the `when` expression is either an enum class, sealed class or of type boolean. +Using `else` cases for these expressions can lead to unintended behavior when adding new enum types, sealed subtypes +or changing the nullability of a boolean, since this will be implicitly handled by the `else` case. + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +enum class Color { + RED, + GREEN, + BLUE +} + +when(c) { + Color.RED -> {} + Color.GREEN -> {} + else -> {} +} +``` + +#### Compliant Code: + +```kotlin +enum class Color { + RED, + GREEN, + BLUE +} + +when(c) { + Color.RED -> {} + Color.GREEN -> {} + Color.BLUE -> {} +} +``` + +### EqualsAlwaysReturnsTrueOrFalse + +Reports `equals()` methods which will always return true or false. + +Equals methods should always report if some other object is equal to the current object. +See the Kotlin documentation for Any.equals(other: Any?): +https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/equals.html + +**Active by default**: Yes - Since v1.2.0 + +**Debt**: 20min + +#### Noncompliant Code: + +```kotlin +override fun equals(other: Any?): Boolean { + return true +} +``` + +#### Compliant Code: + +```kotlin +override fun equals(other: Any?): Boolean { + return this === other +} +``` + +### EqualsWithHashCodeExist + +When a class overrides the equals() method it should also override the hashCode() method. + +All hash-based collections depend on objects meeting the equals-contract. Two equal objects must produce the +same hashcode. When inheriting equals or hashcode, override the inherited and call the super method for +clarification. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +class Foo { + + override fun equals(other: Any?): Boolean { + return super.equals(other) + } +} +``` + +#### Compliant Code: + +```kotlin +class Foo { + + override fun equals(other: Any?): Boolean { + return super.equals(other) + } + + override fun hashCode(): Int { + return super.hashCode() + } +} +``` + +### ExitOutsideMain + +Flags use of System.exit() and Kotlin's exitProcess() when used outside the `main` function. This makes code more +difficult to test, causes unexpected behaviour on Android, and is a poor way to signal a failure in the program. In +almost all cases it is more appropriate to throw an exception. + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 10min + +#### Noncompliant Code: + +```kotlin +fun randomFunction() { + val result = doWork() + if (result == FAILURE) { + exitProcess(2) + } else { + exitProcess(0) + } +} +``` + +#### Compliant Code: + +```kotlin +fun main() { + val result = doWork() + if (result == FAILURE) { + exitProcess(2) + } else { + exitProcess(0) + } +} +``` + +### ExplicitGarbageCollectionCall + +Reports all calls to explicitly trigger the Garbage Collector. +Code should work independently of the garbage collector and should not require the GC to be triggered in certain +points in time. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 20min + +#### Noncompliant Code: + +```kotlin +System.gc() +Runtime.getRuntime().gc() +System.runFinalization() +``` + +### HasPlatformType + +Platform types must be declared explicitly in public APIs to prevent unexpected errors. + +**Active by default**: Yes - Since v1.21.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +class Person { + fun apiCall() = System.getProperty("propertyName") +} +``` + +#### Compliant Code: + +```kotlin +class Person { + fun apiCall(): String = System.getProperty("propertyName") +} +``` + +### IgnoredReturnValue + +This rule warns on instances where a function, annotated with either `@CheckReturnValue` or `@CheckResult`, +returns a value but that value is not used in any way. The Kotlin compiler gives no warning for this scenario +normally so that's the rationale behind this rule. + +fun returnsValue() = 42 +fun returnsNoValue() {} + +**Active by default**: Yes - Since v1.21.0 + +**Requires Type Resolution** + +**Debt**: 20min + +#### Configuration options: + +* ~~``restrictToAnnotatedMethods``~~ (default: ``true``) + + **Deprecated**: Use `restrictToConfig` instead + + if the rule should check only annotated methods + +* ``restrictToConfig`` (default: ``true``) + + If the rule should check only methods matching to configuration, or all methods + +* ``returnValueAnnotations`` (default: ``['*.CheckResult', '*.CheckReturnValue']``) + + List of glob patterns to be used as inspection annotation + +* ``ignoreReturnValueAnnotations`` (default: ``['*.CanIgnoreReturnValue']``) + + Annotations to skip this inspection + +* ``returnValueTypes`` (default: ``['kotlin.sequences.Sequence', 'kotlinx.coroutines.flow.*Flow', 'java.util.stream.*Stream']``) + + List of return types that should not be ignored + +* ``ignoreFunctionCall`` (default: ``[]``) + + List of function signatures which should be ignored by this rule. Specifying fully-qualified function signature with name only (i.e. `java.time.LocalDate.now`) will ignore all function calls matching the name. Specifying fully-qualified function signature with parameters (i.e. `java.time.LocalDate.now(java.time.Clock)`) will ignore only function calls matching the name and parameters exactly. + +#### Noncompliant Code: + +```kotlin +returnsValue() +``` + +#### Compliant Code: + +```kotlin +if (42 == returnsValue()) {} +val x = returnsValue() +``` + +### ImplicitDefaultLocale + +Prefer passing [java.util.Locale] explicitly than using implicit default value when formatting +strings or performing a case conversion. + +The default locale is almost always inappropriate for machine-readable text like HTTP headers. +For example, if locale with tag `ar-SA-u-nu-arab` is a current default then `%d` placeholders +will be evaluated to a number consisting of Eastern-Arabic (non-ASCII) digits. +[java.util.Locale.US] is recommended for machine-readable output. + +**Active by default**: Yes - Since v1.16.0 + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +String.format("Timestamp: %d", System.currentTimeMillis()) + +val str: String = getString() +str.toUpperCase() +str.toLowerCase() +``` + +#### Compliant Code: + +```kotlin +String.format(Locale.US, "Timestamp: %d", System.currentTimeMillis()) + +val str: String = getString() +str.toUpperCase(Locale.US) +str.toLowerCase(Locale.US) +``` + +### ImplicitUnitReturnType + +Functions using expression statements have an implicit return type. +Changing the type of the expression accidentally, changes the functions return type. +This may lead to backward incompatibility. +Use a block statement to make clear this function will never return a value. + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 5min + +#### Configuration options: + +* ``allowExplicitReturnType`` (default: ``true``) + + if functions with explicit 'Unit' return type should be allowed + +#### Noncompliant Code: + +```kotlin +fun errorProneUnit() = println("Hello Unit") +fun errorProneUnitWithParam(param: String) = param.run { println(this) } +fun String.errorProneUnitWithReceiver() = run { println(this) } +``` + +#### Compliant Code: + +```kotlin +fun blockStatementUnit() { + // code +} + +// explicit Unit is compliant by default; can be configured to enforce block statement +fun safeUnitReturn(): Unit = println("Hello Unit") +``` + +### InvalidRange + +Reports ranges which are empty. +This might be a bug if it is used for instance as a loop condition. This loop will never be triggered then. +This might be due to invalid ranges like (10..9) which will cause the loop to never be entered. + +**Active by default**: Yes - Since v1.2.0 + +**Debt**: 10min + +#### Noncompliant Code: + +```kotlin +for (i in 2..1) {} +for (i in 1 downTo 2) {} + +val range1 = 2 until 1 +val range2 = 2 until 2 +``` + +#### Compliant Code: + +```kotlin +for (i in 2..2) {} +for (i in 2 downTo 2) {} + +val range = 2 until 3 +``` + +### IteratorHasNextCallsNextMethod + +Verifies implementations of the Iterator interface. +The hasNext() method of an Iterator implementation should not have any side effects. +This rule reports implementations that call the next() method of the Iterator inside the hasNext() method. + +**Active by default**: Yes - Since v1.2.0 + +**Debt**: 10min + +#### Noncompliant Code: + +```kotlin +class MyIterator : Iterator { + + override fun hasNext(): Boolean { + return next() != null + } +} +``` + +### IteratorNotThrowingNoSuchElementException + +Reports implementations of the `Iterator` interface which do not throw a NoSuchElementException in the +implementation of the next() method. When there are no more elements to return an Iterator should throw a +NoSuchElementException. + +See: https://docs.oracle.com/javase/7/docs/api/java/util/Iterator.html#next() + +**Active by default**: Yes - Since v1.2.0 + +**Debt**: 10min + +#### Noncompliant Code: + +```kotlin +class MyIterator : Iterator { + + override fun next(): String { + return "" + } +} +``` + +#### Compliant Code: + +```kotlin +class MyIterator : Iterator { + + override fun next(): String { + if (!this.hasNext()) { + throw NoSuchElementException() + } + // ... + } +} +``` + +### LateinitUsage + +Turn on this rule to flag usages of the lateinit modifier. + +Using lateinit for property initialization can be error-prone and the actual initialization is not +guaranteed. Try using constructor injection or delegation to initialize properties. + +**Active by default**: No + +**Debt**: 20min + +#### Configuration options: + +* ~~``excludeAnnotatedProperties``~~ (default: ``[]``) + + **Deprecated**: Use `ignoreAnnotated` instead + + Allows you to provide a list of annotations that disable this check. + +* ``ignoreOnClassesPattern`` (default: ``''``) + + Allows you to disable the rule for a list of classes + +#### Noncompliant Code: + +```kotlin +class Foo { + private lateinit var i1: Int + lateinit var i2: Int +} +``` + +### MapGetWithNotNullAssertionOperator + +Reports calls of the map access methods `map[]` or `map.get()` with a not-null assertion operator `!!`. +This may result in a NullPointerException. +Preferred access methods are `map[]` without `!!`, `map.getValue()`, `map.getOrDefault()` or `map.getOrElse()`. + +Based on an IntelliJ IDEA inspection MapGetWithNotNullAssertionOperatorInspection. + +**Active by default**: Yes - Since v1.21.0 + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +val map = emptyMap() +map["key"]!! + +val map = emptyMap() +map.get("key")!! +``` + +#### Compliant Code: + +```kotlin +val map = emptyMap() +map["key"] + +val map = emptyMap() +map.getValue("key") + +val map = emptyMap() +map.getOrDefault("key", "") + +val map = emptyMap() +map.getOrElse("key", { "" }) +``` + +### MissingPackageDeclaration + +Reports when the package declaration is missing. + +**Active by default**: No + +**Debt**: 5min + +### ~~MissingWhenCase~~ + +Rule deprecated as compiler performs this check by default + +Turn on this rule to flag `when` expressions that do not check that all cases are covered when the subject is an enum +or sealed class and the `when` expression is used as a statement. + +When this happens it's unclear what was intended when an unhandled case is reached. It is better to be explicit and +either handle all cases or use a default `else` statement to cover the unhandled cases. + +**Active by default**: Yes - Since v1.2.0 + +**Requires Type Resolution** + +**Debt**: 20min + +#### Configuration options: + +* ``allowElseExpression`` (default: ``true``) + + whether `else` can be treated as a valid case for enums and sealed classes + +#### Noncompliant Code: + +```kotlin +enum class Color { + RED, + GREEN, + BLUE +} + +fun whenOnEnumFail(c: Color) { + when(c) { + Color.BLUE -> {} + Color.GREEN -> {} + } +} +``` + +#### Compliant Code: + +```kotlin +enum class Color { + RED, + GREEN, + BLUE +} + +fun whenOnEnumCompliant(c: Color) { + when(c) { + Color.BLUE -> {} + Color.GREEN -> {} + Color.RED -> {} + } +} + +fun whenOnEnumCompliant2(c: Color) { + when(c) { + Color.BLUE -> {} + else -> {} + } +} +``` + +### NullCheckOnMutableProperty + +Reports null-checks on mutable properties, as these properties' value can be +changed - and thus make the null-check invalid - after the execution of the +if-statement. + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 10min + +#### Noncompliant Code: + +```kotlin +class A(private var a: Int?) { +fun foo() { + if (a != null) { + println(2 + a!!) + } +} +} +``` + +#### Compliant Code: + +```kotlin +class A(private val a: Int?) { +fun foo() { + if (a != null) { + println(2 + a) + } +} +} +``` + +### NullableToStringCall + +Turn on this rule to flag 'toString' calls with a nullable receiver that may return the string "null". + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +fun foo(a: Any?): String { + return a.toString() +} + +fun bar(a: Any?): String { + return "$a" +} +``` + +#### Compliant Code: + +```kotlin +fun foo(a: Any?): String { + return a?.toString() ?: "-" +} + +fun bar(a: Any?): String { + return "${a ?: "-"}" +} +``` + +### ~~RedundantElseInWhen~~ + +Rule deprecated as compiler performs this check by default + +Turn on this rule to flag `when` expressions that contain a redundant `else` case. This occurs when it can be +verified that all cases are already covered when checking cases on an enum or sealed class. + +**Active by default**: Yes - Since v1.2.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +enum class Color { + RED, + GREEN, + BLUE +} + +fun whenOnEnumFail(c: Color) { + when(c) { + Color.BLUE -> {} + Color.GREEN -> {} + Color.RED -> {} + else -> {} + } +} +``` + +#### Compliant Code: + +```kotlin +enum class Color { + RED, + GREEN, + BLUE +} + +fun whenOnEnumCompliant(c: Color) { + when(c) { + Color.BLUE -> {} + Color.GREEN -> {} + else -> {} + } +} + +fun whenOnEnumCompliant2(c: Color) { + when(c) { + Color.BLUE -> {} + Color.GREEN -> {} + Color.RED -> {} + } +} +``` + +### UnconditionalJumpStatementInLoop + +Reports loops which contain jump statements that jump regardless of any conditions. +This implies that the loop is only executed once and thus could be rewritten without a +loop altogether. + +**Active by default**: No + +**Debt**: 10min + +#### Noncompliant Code: + +```kotlin +for (i in 1..2) break +``` + +#### Compliant Code: + +```kotlin +for (i in 1..2) { + if (i == 1) break +} +``` + +### UnnecessaryNotNullCheck + +Reports unnecessary not-null checks with `requireNotNull` or `checkNotNull` that can be removed by the user. + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +var string = "foo" +println(requireNotNull(string)) +``` + +#### Compliant Code: + +```kotlin +var string : String? = "foo" +println(requireNotNull(string)) +``` + +### UnnecessaryNotNullOperator + +Reports unnecessary not-null operator usage (!!) that can be removed by the user. + +**Active by default**: Yes - Since v1.16.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +val a = 1 +val b = a!! +``` + +#### Compliant Code: + +```kotlin +val a = 1 +val b = a +``` + +### UnnecessarySafeCall + +Reports unnecessary safe call operators (`?.`) that can be removed by the user. + +**Active by default**: Yes - Since v1.16.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +val a: String = "" +val b = a?.length +``` + +#### Compliant Code: + +```kotlin +val a: String? = null +val b = a?.length +``` + +### UnreachableCatchBlock + +Reports unreachable catch blocks. +Catch blocks can be unreachable if the exception has already been caught in the block above. + +**Active by default**: Yes - Since v1.21.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +fun test() { + try { + foo() + } catch (t: Throwable) { + bar() + } catch (e: Exception) { + // Unreachable + baz() + } +} +``` + +#### Compliant Code: + +```kotlin +fun test() { + try { + foo() + } catch (e: Exception) { + baz() + } catch (t: Throwable) { + bar() + } +} +``` + +### UnreachableCode + +Reports unreachable code. +Code can be unreachable because it is behind return, throw, continue or break expressions. +This unreachable code should be removed as it serves no purpose. + +**Active by default**: Yes - Since v1.0.0 + +**Requires Type Resolution** + +**Debt**: 10min + +#### Noncompliant Code: + +```kotlin +for (i in 1..2) { + break + println() // unreachable +} + +throw IllegalArgumentException() +println() // unreachable + +fun f() { + return + println() // unreachable +} +``` + +### UnsafeCallOnNullableType + +Reports unsafe calls on nullable types. These calls will throw a NullPointerException in case +the nullable value is null. Kotlin provides many ways to work with nullable types to increase +null safety. Guard the code appropriately to prevent NullPointerExceptions. + +**Active by default**: Yes - Since v1.2.0 + +**Requires Type Resolution** + +**Debt**: 20min + +#### Noncompliant Code: + +```kotlin +fun foo(str: String?) { + println(str!!.length) +} +``` + +#### Compliant Code: + +```kotlin +fun foo(str: String?) { + println(str?.length) +} +``` + +### UnsafeCast + +Reports casts that will never succeed. + +**Active by default**: Yes - Since v1.16.0 + +**Requires Type Resolution** + +**Debt**: 20min + +**Aliases**: UNCHECKED_CAST + +#### Noncompliant Code: + +```kotlin +fun foo(s: String) { + println(s as Int) +} + +fun bar(s: String) { + println(s as? Int) +} +``` + +#### Compliant Code: + +```kotlin +fun foo(s: Any) { + println(s as Int) +} +``` + +### UnusedUnaryOperator + +This rule detects unused unary operators. + +**Active by default**: Yes - Since v1.21.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +val x = 1 + 2 + + 3 + 4 +println(x) // 3 +``` + +#### Compliant Code: + +```kotlin +val x = 1 + 2 + 3 + 4 +println(x) // 10 +``` + +### UselessPostfixExpression + +This rule reports postfix expressions (++, --) which are unused and thus unnecessary. +This leads to confusion as a reader of the code might think the value will be incremented/decremented. +However, the value is replaced with the original value which might lead to bugs. + +**Active by default**: Yes - Since v1.21.0 + +**Debt**: 20min + +#### Noncompliant Code: + +```kotlin +var i = 0 +i = i-- +i = 1 + i++ +i = i++ + 1 + +fun foo(): Int { + var i = 0 + // ... + return i++ +} +``` + +#### Compliant Code: + +```kotlin +var i = 0 +i-- +i = i + 2 +i = i + 2 + +fun foo(): Int { + var i = 0 + // ... + i++ + return i +} +``` + +### WrongEqualsTypeParameter + +Reports equals() methods which take in a wrongly typed parameter. +Correct implementations of the equals() method should only take in a parameter of type Any? +See: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/equals.html + +**Active by default**: Yes - Since v1.2.0 + +**Debt**: 10min + +#### Noncompliant Code: + +```kotlin +class Foo { + + fun equals(other: String): Boolean { + return super.equals(other) + } +} +``` + +#### Compliant Code: + +```kotlin +class Foo { + + fun equals(other: Any?): Boolean { + return super.equals(other) + } +} +``` diff --git a/website/versioned_docs/version-1.22.0/rules/ruleauthors.md b/website/versioned_docs/version-1.22.0/rules/ruleauthors.md new file mode 100644 index 00000000000..0dce878068c --- /dev/null +++ b/website/versioned_docs/version-1.22.0/rules/ruleauthors.md @@ -0,0 +1,29 @@ +--- +title: Ruleauthors Rule Set +sidebar: home_sidebar +keywords: [rules, ruleauthors] +permalink: ruleauthors.html +toc: true +folder: documentation +--- +The rule authors ruleset provides rules that ensures good practices when writing detekt rules + +### UseEntityAtName + +If a rule [report]s issues using [Entity.from] with [KtNamedDeclaration.getNameIdentifier], +then it can be replaced with [Entity.atName] for more semantic code and better baseline support. + +**Active by default**: Yes - Since v1.22.0 + +**Debt**: 5min + +### ViolatesTypeResolutionRequirements + +If a rule uses the property [BaseRule.bindingContext] should be annotated with `@RequiresTypeResolution`. +And if the rule doesn't use that property it shouldn't be annotated with it. + +**Active by default**: Yes - Since v1.22.0 + +**Requires Type Resolution** + +**Debt**: 5min diff --git a/website/versioned_docs/version-1.22.0/rules/style.md b/website/versioned_docs/version-1.22.0/rules/style.md new file mode 100644 index 00000000000..b9ea308ba96 --- /dev/null +++ b/website/versioned_docs/version-1.22.0/rules/style.md @@ -0,0 +1,2522 @@ +--- +title: Style Rule Set +sidebar: home_sidebar +keywords: [rules, style] +permalink: style.html +toc: true +folder: documentation +--- +The Style ruleset provides rules that assert the style of the code. +This will help keep code in line with the given +code style guidelines. + +### AlsoCouldBeApply + +Detects when an `also` block contains only `it`-started expressions. + +By refactoring the `also` block to an `apply` block makes it so that all `it`s can be removed +thus making the block more concise and easier to read. + +**Active by default**: No + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +Buzz().also { +it.init() +it.block() +} +``` + +#### Compliant Code: + +```kotlin +Buzz().apply { +init() +block() +} + +// Also compliant +fun foo(a: Int): Int { +return a.also { println(it) } +} +``` + +### CanBeNonNullable + +This rule inspects variables marked as nullable and reports which could be +declared as non-nullable instead. + +It's preferred to not have functions that do "nothing". +A function that does nothing when the value is null hides the logic, +so it should not allow null values in the first place. +It is better to move the null checks up around the calls, +instead of having it inside the function. + +This could lead to less nullability overall in the codebase. + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 10min + +#### Noncompliant Code: + +```kotlin +class A { + var a: Int? = 5 + + fun foo() { + a = 6 + } +} + +class A { + val a: Int? + get() = 5 +} + +fun foo(a: Int?) { + val b = a!! + 2 +} + +fun foo(a: Int?) { + if (a != null) { + println(a) + } +} + +fun foo(a: Int?) { + if (a == null) return + println(a) +} +``` + +#### Compliant Code: + +```kotlin +class A { + var a: Int = 5 + + fun foo() { + a = 6 + } +} + +class A { + val a: Int + get() = 5 +} + +fun foo(a: Int) { + val b = a + 2 +} + +fun foo(a: Int) { + println(a) +} +``` + +### CascadingCallWrapping + +Requires that all chained calls are placed on a new line if a preceding one is. + +**Active by default**: No + +**Debt**: 5min + +#### Configuration options: + +* ``includeElvis`` (default: ``true``) + + require trailing elvis expressions to be wrapped on a new line + +#### Noncompliant Code: + +```kotlin +foo() +.bar().baz() +``` + +#### Compliant Code: + +```kotlin +foo().bar().baz() + +foo() +.bar() +.baz() +``` + +### ClassOrdering + +This rule ensures class contents are ordered as follows as recommended by the Kotlin +[Coding Conventions](https://kotlinlang.org/docs/coding-conventions.html#class-layout): +- Property declarations and initializer blocks +- Secondary constructors +- Method declarations +- Companion object + +**Active by default**: No + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +class OutOfOrder { + companion object { + const val IMPORTANT_VALUE = 3 + } + + fun returnX(): Int { + return x + } + + private val x = 2 +} +``` + +#### Compliant Code: + +```kotlin +class InOrder { + private val x = 2 + + fun returnX(): Int { + return x + } + + companion object { + const val IMPORTANT_VALUE = 3 + } +} +``` + +### CollapsibleIfStatements + +This rule detects `if` statements which can be collapsed. This can reduce nesting and help improve readability. + +However, carefully consider whether merging the if statements actually improves readability, as collapsing the +statements may hide some edge cases from the reader. + +**Active by default**: No + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +val i = 1 +if (i > 0) { + if (i < 5) { + println(i) + } +} +``` + +#### Compliant Code: + +```kotlin +val i = 1 +if (i > 0 && i < 5) { + println(i) +} +``` + +### DataClassContainsFunctions + +This rule reports functions inside data classes which have not been marked as a conversion function. + +Data classes should mainly be used to store data. This rule assumes that they should not contain any extra functions +aside functions that help with converting objects from/to one another. +Data classes will automatically have a generated `equals`, `toString` and `hashCode` function by the compiler. + +**Active by default**: No + +**Debt**: 20min + +#### Configuration options: + +* ``conversionFunctionPrefix`` (default: ``['to']``) + + allowed conversion function names + +#### Noncompliant Code: + +```kotlin +data class DataClassWithFunctions(val i: Int) { + fun foo() { } +} +``` + +### DataClassShouldBeImmutable + +This rule reports mutable properties inside data classes. + +Data classes should mainly be used to store immutable data. This rule assumes that they should not contain any +mutable properties. + +**Active by default**: No + +**Debt**: 20min + +#### Noncompliant Code: + +```kotlin +data class MutableDataClass(var i: Int) { + var s: String? = null +} +``` + +#### Compliant Code: + +```kotlin +data class ImmutableDataClass( + val i: Int, + val s: String? +) +``` + +### DestructuringDeclarationWithTooManyEntries + +Destructuring declarations with too many entries are hard to read and understand. +To increase readability they should be refactored to reduce the number of entries or avoid using a destructuring +declaration. + +**Active by default**: Yes - Since v1.21.0 + +**Debt**: 10min + +#### Configuration options: + +* ``maxDestructuringEntries`` (default: ``3``) + + maximum allowed elements in a destructuring declaration + +#### Noncompliant Code: + +```kotlin +data class TooManyElements(val a: Int, val b: Int, val c: Int, val d: Int) +val (a, b, c, d) = TooManyElements(1, 2, 3, 4) +``` + +#### Compliant Code: + +```kotlin +data class FewerElements(val a: Int, val b: Int, val c: Int) +val (a, b, c) = TooManyElements(1, 2, 3) +``` + +### EqualsNullCall + +To compare an object with `null` prefer using `==`. This rule detects and reports instances in the code where the +`equals()` method is used to compare a value with `null`. + +**Active by default**: Yes - Since v1.2.0 + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +fun isNull(str: String) = str.equals(null) +``` + +#### Compliant Code: + +```kotlin +fun isNull(str: String) = str == null +``` + +### EqualsOnSignatureLine + +Requires that the equals sign, when used for an expression style function, is on the same line as the +rest of the function signature. + +**Active by default**: No + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +fun stuff(): Int + = 5 + +fun foo(): Int where V : Int + = 5 +``` + +#### Compliant Code: + +```kotlin +fun stuff() = 5 + +fun stuff() = + foo.bar() + +fun foo(): Int where V : Int = 5 +``` + +### ExplicitCollectionElementAccessMethod + +In Kotlin functions `get` or `set` can be replaced with the shorter operator — `[]`, +see [Indexed access operator](https://kotlinlang.org/docs/operator-overloading.html#indexed-access-operator). +Prefer the usage of the indexed access operator `[]` for map or list element access or insert methods. + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +val map = mutableMapOf() +map.put("key", "value") +val value = map.get("key") +``` + +#### Compliant Code: + +```kotlin +val map = mutableMapOf() +map["key"] = "value" +val value = map["key"] +``` + +### ExplicitItLambdaParameter + +Lambda expressions are one of the core features of the language. They often include very small chunks of +code using only one parameter. In this cases Kotlin can supply the implicit `it` parameter +to make code more concise. It fits most use cases, but when faced larger or nested chunks of code, +you might want to add an explicit name for the parameter. Naming it just `it` is meaningless and only +makes your code misleading, especially when dealing with nested functions. + +**Active by default**: Yes - Since v1.21.0 + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +a?.let { it -> it.plus(1) } +foo.flatMapObservable { it -> Observable.fromIterable(it) } +listOfPairs.map(::second).forEach { it -> + it.execute() +} +collection.zipWithNext { it, next -> Pair(it, next) } +``` + +#### Compliant Code: + +```kotlin +a?.let { it.plus(1) } // Much better to use implicit it +foo.flatMapObservable(Observable::fromIterable) // Here we can have a method reference + +// For multiline blocks it is usually better come up with a clear and more meaningful name +listOfPairs.map(::second).forEach { apiRequest -> + apiRequest.execute() +} + +// Lambdas with multiple parameter should be named clearly, using it for one of them can be confusing +collection.zipWithNext { prev, next -> + Pair(prev, next) +} +``` + +### ExpressionBodySyntax + +Functions which only contain a `return` statement can be collapsed to an expression body. This shortens and +cleans up the code. + +**Active by default**: No + +**Debt**: 5min + +#### Configuration options: + +* ``includeLineWrapping`` (default: ``false``) + + include return statements with line wraps in it + +#### Noncompliant Code: + +```kotlin +fun stuff(): Int { + return 5 +} +``` + +#### Compliant Code: + +```kotlin +fun stuff() = 5 + +fun stuff() { + return + moreStuff() + .getStuff() + .stuffStuff() +} +``` + +### ForbiddenComment + +This rule allows to set a list of comments which are forbidden in the codebase and should only be used during +development. Offending code comments will then be reported. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 10min + +#### Configuration options: + +* ``values`` (default: ``['FIXME:', 'STOPSHIP:', 'TODO:']``) + + forbidden comment strings + +* ``allowedPatterns`` (default: ``''``) + + ignores comments which match the specified regular expression. For example `Ticket|Task`. + +* ``customMessage`` (default: ``''``) + + error message which overrides the default one + +#### Noncompliant Code: + +```kotlin +val a = "" // TODO: remove please +// FIXME: this is a hack +fun foo() { } +// STOPSHIP: +``` + +### ForbiddenImport + +This rule allows to set a list of forbidden imports. This can be used to discourage the use of unstable, experimental +or deprecated APIs. Detekt will then report all imports that are forbidden. + +**Active by default**: No + +**Debt**: 10min + +#### Configuration options: + +* ``imports`` (default: ``[]``) + + imports which should not be used + +* ``forbiddenPatterns`` (default: ``''``) + + reports imports which match the specified regular expression. For example `net.*R`. + +#### Noncompliant Code: + +```kotlin +import kotlin.jvm.JvmField +import kotlin.SinceKotlin +``` + +### ForbiddenMethodCall + +This rule allows to set a list of forbidden methods or constructors. This can be used to discourage the use +of unstable, experimental or deprecated methods, especially for methods imported from external libraries. +Detekt will then report all method or constructor invocations that are forbidden. + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 10min + +#### Configuration options: + +* ``methods`` (default: ``['kotlin.io.print', 'kotlin.io.println']``) + + List of fully qualified method signatures which are forbidden. Methods can be defined without full signature (i.e. `java.time.LocalDate.now`) which will report calls of all methods with this name or with full signature (i.e. `java.time.LocalDate(java.time.Clock)`) which would report only call with this concrete signature. If you want to forbid an extension function like `fun String.hello(a: Int)` you should add the receiver parameter as the first parameter like this: `hello(kotlin.String, kotlin.Int)`. To forbid constructor calls you need to define them with ``, for example `java.util.Date.`. To forbid calls involving type parameters, omit them, for example `fun hello(args: Array)` is referred to as simply `hello(kotlin.Array)` (also the signature for vararg parameters). To forbid methods from the companion object reference the Companion class, for example as `TestClass.Companion.hello()` (even if it is marked `@JvmStatic`). + +#### Noncompliant Code: + +```kotlin +import java.lang.System +fun main() { + System.gc() + System::gc +} +``` + +### ForbiddenSuppress + +This rule allows to set a list of rules whose suppression is forbidden. +This can be used to discourage the abuse of the `Suppress` and `SuppressWarnings` annotations. +Detekt will report suppression of all forbidden rules. +This rule is not capable of reporting suppression of itself, as that's a language feature with precedence. + +**Active by default**: No + +**Debt**: 10min + +#### Configuration options: + +* ``rules`` (default: ``[]``) + + Rules whose suppression is forbidden. + +#### Noncompliant Code: + +```kotlin +package foo + +// When the rule "MaximumLineLength" is forbidden +@Suppress("MaximumLineLength", "UNCHECKED_CAST") +class Bar +``` + +#### Compliant Code: + +```kotlin +package foo + +// When the rule "MaximumLineLength" is forbidden +@Suppress("UNCHECKED_CAST") +class Bar +``` + +### ForbiddenVoid + +This rule detects usages of `Void` and reports them as forbidden. +The Kotlin type `Unit` should be used instead. This type corresponds to the `Void` class in Java +and has only one value - the `Unit` object. + +**Active by default**: Yes - Since v1.21.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Configuration options: + +* ``ignoreOverridden`` (default: ``false``) + + ignores void types in signatures of overridden functions + +* ``ignoreUsageInGenerics`` (default: ``false``) + + ignore void types as generic arguments + +#### Noncompliant Code: + +```kotlin +runnable: () -> Void +var aVoid: Void? = null +``` + +#### Compliant Code: + +```kotlin +runnable: () -> Unit +Void::class +``` + +### FunctionOnlyReturningConstant + +A function that only returns a single constant can be misleading. Instead, prefer declaring the constant +as a `const val`. + +**Active by default**: Yes - Since v1.2.0 + +**Debt**: 10min + +#### Configuration options: + +* ``ignoreOverridableFunction`` (default: ``true``) + + if overriden functions should be ignored + +* ``ignoreActualFunction`` (default: ``true``) + + if actual functions should be ignored + +* ``excludedFunctions`` (default: ``[]``) + + excluded functions + +* ~~``excludeAnnotatedFunction``~~ (default: ``[]``) + + **Deprecated**: Use `ignoreAnnotated` instead + + allows to provide a list of annotations that disable this check + +#### Noncompliant Code: + +```kotlin +fun functionReturningConstantString() = "1" +``` + +#### Compliant Code: + +```kotlin +const val constantString = "1" +``` + +### LoopWithTooManyJumpStatements + +Loops which contain multiple `break` or `continue` statements are hard to read and understand. +To increase readability they should be refactored into simpler loops. + +**Active by default**: Yes - Since v1.2.0 + +**Debt**: 10min + +#### Configuration options: + +* ``maxJumpCount`` (default: ``1``) + + maximum allowed jumps in a loop + +#### Noncompliant Code: + +```kotlin +val strs = listOf("foo, bar") +for (str in strs) { + if (str == "bar") { + break + } else { + continue + } +} +``` + +### MagicNumber + +This rule detects and reports usages of magic numbers in the code. Prefer defining constants with clear names +describing what the magic number means. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 10min + +#### Configuration options: + +* ``ignoreNumbers`` (default: ``['-1', '0', '1', '2']``) + + numbers which do not count as magic numbers + +* ``ignoreHashCodeFunction`` (default: ``true``) + + whether magic numbers in hashCode functions should be ignored + +* ``ignorePropertyDeclaration`` (default: ``false``) + + whether magic numbers in property declarations should be ignored + +* ``ignoreLocalVariableDeclaration`` (default: ``false``) + + whether magic numbers in local variable declarations should be ignored + +* ``ignoreConstantDeclaration`` (default: ``true``) + + whether magic numbers in constant declarations should be ignored + +* ``ignoreCompanionObjectPropertyDeclaration`` (default: ``true``) + + whether magic numbers in companion object declarations should be ignored + +* ``ignoreAnnotation`` (default: ``false``) + + whether magic numbers in annotations should be ignored + +* ``ignoreNamedArgument`` (default: ``true``) + + whether magic numbers in named arguments should be ignored + +* ``ignoreEnums`` (default: ``false``) + + whether magic numbers in enums should be ignored + +* ``ignoreRanges`` (default: ``false``) + + whether magic numbers in ranges should be ignored + +* ``ignoreExtensionFunctions`` (default: ``true``) + + whether magic numbers as subject of an extension function should be ignored + +#### Noncompliant Code: + +```kotlin +class User { + + fun checkName(name: String) { + if (name.length > 42) { + throw IllegalArgumentException("username is too long") + } + // ... + } +} +``` + +#### Compliant Code: + +```kotlin +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 + } +} +``` + +### MandatoryBracesIfStatements + +This rule detects multi-line `if` statements which do not have braces. +Adding braces would improve readability and avoid possible errors. + +**Active by default**: No + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +val i = 1 +if (i > 0) + println(i) +``` + +#### Compliant Code: + +```kotlin +val x = if (condition) 5 else 4 +``` + +### MandatoryBracesLoops + +This rule detects multi-line `for` and `while` loops which do not have braces. +Adding braces would improve readability and avoid possible errors. + +**Active by default**: No + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +for (i in 0..10) + println(i) + +while (true) + println("Hello, world") + +do + println("Hello, world") +while (true) +``` + +#### Compliant Code: + +```kotlin +for (i in 0..10) { + println(i) +} + +for (i in 0..10) println(i) + +while (true) { + println("Hello, world") +} + +while (true) println("Hello, world") + +do { + println("Hello, world") +} while (true) + +do println("Hello, world") while (true) +``` + +### MaxChainedCallsOnSameLine + +Limits the number of chained calls which can be placed on a single line. + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 5min + +#### Configuration options: + +* ``maxChainedCalls`` (default: ``5``) + + maximum chained calls allowed on a single line + +#### Noncompliant Code: + +```kotlin +a().b().c().d().e().f() +``` + +#### Compliant Code: + +```kotlin +a().b().c() +.d().e().f() +``` + +### MaxLineLength + +This rule reports lines of code which exceed a defined maximum line length. + +Long lines might be hard to read on smaller screens or printouts. Additionally, having a maximum line length +in the codebase will help make the code more uniform. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +#### Configuration options: + +* ``maxLineLength`` (default: ``120``) + + maximum line length + +* ``excludePackageStatements`` (default: ``true``) + + if package statements should be ignored + +* ``excludeImportStatements`` (default: ``true``) + + if import statements should be ignored + +* ``excludeCommentStatements`` (default: ``false``) + + if comment statements should be ignored + +* ``excludeRawStrings`` (default: ``true``) + + if raw strings should be ignored + +### MayBeConst + +This rule identifies and reports properties (`val`) that may be `const val` instead. +Using `const val` can lead to better performance of the resulting bytecode as well as better interoperability with +Java. + +**Active by default**: Yes - Since v1.2.0 + +**Debt**: 5min + +**Aliases**: MayBeConstant + +#### Noncompliant Code: + +```kotlin +val myConstant = "abc" +``` + +#### Compliant Code: + +```kotlin +const val MY_CONSTANT = "abc" +``` + +### ModifierOrder + +This rule reports cases in the code where modifiers are not in the correct order. The default modifier order is +taken from: [Modifiers order](https://kotlinlang.org/docs/coding-conventions.html#modifiers-order) + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +lateinit internal val str: String +``` + +#### Compliant Code: + +```kotlin +internal lateinit val str: String +``` + +### MultilineLambdaItParameter + +Lambda expressions are very useful in a lot of cases, and they often include very small chunks of +code using only one parameter. In this cases Kotlin can supply the implicit `it` parameter +to make code more concise. However, when you are dealing with lambdas that contain multiple statements, +you might end up with code that is hard to read if you don't specify a readable, descriptive parameter name +explicitly. + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +val digits = 1234.let { + println(it) + listOf(it) +} + +val digits = 1234.let { it -> + println(it) + listOf(it) +} + +val flat = listOf(listOf(1), listOf(2)).mapIndexed { index, it -> + println(it) + it + index +} +``` + +#### Compliant Code: + +```kotlin +val digits = 1234.let { explicitParameterName -> + println(explicitParameterName) + listOf(explicitParameterName) +} + +val lambda = { item: Int, that: String -> + println(item) + item.toString() + that +} + +val digits = 1234.let { listOf(it) } +val digits = 1234.let { + listOf(it) +} +val digits = 1234.let { it -> listOf(it) } +val digits = 1234.let { it -> + listOf(it) +} +val digits = 1234.let { explicit -> listOf(explicit) } +val digits = 1234.let { explicit -> + listOf(explicit) +} +``` + +### MultilineRawStringIndentation + +This rule ensures that raw strings have a consistent indentation. + +The content of a multi line raw string should have the same indentation as the enclosing expression plus the +configured indentSize. The closing triple-quotes (`"""`) must have the same indentation as the enclosing expression. + +**Active by default**: No + +**Debt**: 5min + +#### Configuration options: + +* ``indentSize`` (default: ``4``) + + indentation size + +#### Noncompliant Code: + +```kotlin +val a = """ +Hello World! +How are you? +""".trimMargin() + +val a = """ + Hello World! + How are you? + """.trimMargin() +``` + +#### Compliant Code: + +```kotlin +val a = """ + Hello World! + How are you? +""".trimMargin() + +val a = """ + Hello World! + How are you? +""".trimMargin() +``` + +### NestedClassesVisibility + +Nested classes inherit their visibility from the parent class +and are often used to implement functionality local to the class it is nested in. +These nested classes can't have a higher visibility than their parent. +However, the visibility can be further restricted by using a private modifier for instance. +In internal classes the _explicit_ public modifier for nested classes is misleading and thus unnecessary, +because the nested class still has an internal visibility. + +**Active by default**: Yes - Since v1.16.0 + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +internal class Outer { + // explicit public modifier still results in an internal nested class + public class Nested +} +``` + +#### Compliant Code: + +```kotlin +internal class Outer { + class Nested1 + internal class Nested2 +} +``` + +### NewLineAtEndOfFile + +This rule reports files which do not end with a line separator. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +### NoTabs + +This rule reports if tabs are used in Kotlin files. +According to +[Google's Kotlin style guide](https://android.github.io/kotlin-guides/style.html#whitespace-characters) +the only whitespace chars that are allowed in a source file are the line terminator sequence +and the ASCII horizontal space character (0x20). Strings containing tabs are allowed. + +**Active by default**: No + +**Debt**: 5min + +### NullableBooleanCheck + +Detects nullable boolean checks which use an elvis expression `?:` rather than equals `==`. + +Per the [Kotlin coding conventions](https://kotlinlang.org/docs/coding-conventions.html#nullable-boolean-values-in-conditions) +converting a nullable boolean property to non-null should be done via `!= false` or `== true` +rather than `?: true` or `?: false` (respectively). + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +value ?: true +value ?: false +``` + +#### Compliant Code: + +```kotlin +value != false +value == true +``` + +### ObjectLiteralToLambda + +An anonymous object that does nothing other than the implementation of a single method +can be used as a lambda. + +See [SAM conversions](https://kotlinlang.org/docs/java-interop.html#sam-conversions), +[Functional (SAM) interfaces](https://kotlinlang.org/docs/fun-interfaces.html) + +**Active by default**: Yes - Since v1.21.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +object : Foo { + override fun bar() { + } +} +``` + +#### Compliant Code: + +```kotlin +Foo { +} +``` + +### OptionalAbstractKeyword + +This rule reports `abstract` modifiers which are unnecessary and can be removed. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +abstract interface Foo { // abstract keyword not needed + + abstract fun x() // abstract keyword not needed + abstract var y: Int // abstract keyword not needed +} +``` + +#### Compliant Code: + +```kotlin +interface Foo { + + fun x() + var y: Int +} +``` + +### OptionalUnit + +It is not necessary to define a return type of `Unit` on functions or to specify a lone Unit statement. +This rule detects and reports instances where the `Unit` return type is specified on functions and the occurrences +of a lone Unit statement. + +**Active by default**: No + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +fun foo(): Unit { + return Unit +} +fun foo() = Unit + +fun doesNothing() { + Unit +} +``` + +#### Compliant Code: + +```kotlin +fun foo() { } + +// overridden no-op functions are allowed +override fun foo() = Unit +``` + +### OptionalWhenBraces + +This rule reports unnecessary braces in when expressions. These optional braces should be removed. + +**Active by default**: No + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +val i = 1 +when (i) { + 1 -> { println("one") } // unnecessary curly braces since there is only one statement + else -> println("else") +} +``` + +#### Compliant Code: + +```kotlin +val i = 1 +when (i) { + 1 -> println("one") + else -> println("else") +} +``` + +### PreferToOverPairSyntax + +This rule detects the usage of the Pair constructor to create pairs of values. + +Using <value1> to <value2> is preferred. + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +val pair = Pair(1, 2) +``` + +#### Compliant Code: + +```kotlin +val pair = 1 to 2 +``` + +### ProtectedMemberInFinalClass + +Kotlin classes are `final` by default. Thus classes which are not marked as `open` should not contain any `protected` +members. Consider using `private` or `internal` modifiers instead. + +**Active by default**: Yes - Since v1.2.0 + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +class ProtectedMemberInFinalClass { + protected var i = 0 +} +``` + +#### Compliant Code: + +```kotlin +class ProtectedMemberInFinalClass { + private var i = 0 +} +``` + +### RedundantExplicitType + +Local properties do not need their type to be explicitly provided when the inferred type matches the explicit type. + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +fun function() { + val x: String = "string" +} +``` + +#### Compliant Code: + +```kotlin +fun function() { + val x = "string" +} +``` + +### RedundantHigherOrderMapUsage + +Redundant maps add complexity to the code and accomplish nothing. They should be removed or replaced with the proper +operator. + +**Active by default**: Yes - Since v1.21.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +fun foo(list: List): List { + return list + .filter { it > 5 } + .map { it } +} + +fun bar(list: List): List { + return list + .filter { it > 5 } + .map { + doSomething(it) + it + } +} + +fun baz(set: Set): List { + return set.map { it } +} +``` + +#### Compliant Code: + +```kotlin +fun foo(list: List): List { + return list + .filter { it > 5 } +} + +fun bar(list: List): List { + return list + .filter { it > 5 } + .onEach { + doSomething(it) + } +} + +fun baz(set: Set): List { + return set.toList() +} +``` + +### RedundantVisibilityModifierRule + +This rule checks for redundant visibility modifiers. +One exemption is the +[explicit API mode](https://kotlinlang.org/docs/whatsnew14.html#explicit-api-mode-for-library-authors) +In this mode, the visibility modifier should be defined explicitly even if it is public. +Hence, the rule ignores the visibility modifiers in explicit API mode. + +**Active by default**: No + +**Debt**: 5min + +**Aliases**: RedundantVisibilityModifier + +#### Noncompliant Code: + +```kotlin +public interface Foo { // public per default + + public fun bar() // public per default +} +``` + +#### Compliant Code: + +```kotlin +interface Foo { + + fun bar() +} +``` + +### ReturnCount + +Restrict the number of return methods allowed in methods. + +Having many exit points in a function can be confusing and impacts readability of the +code. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 10min + +#### Configuration options: + +* ``max`` (default: ``2``) + + define the maximum number of return statements allowed per function + +* ``excludedFunctions`` (default: ``['equals']``) + + define a list of function names to be ignored by this check + +* ``excludeLabeled`` (default: ``false``) + + if labeled return statements should be ignored + +* ``excludeReturnFromLambda`` (default: ``true``) + + if labeled return from a lambda should be ignored + +* ``excludeGuardClauses`` (default: ``false``) + + if true guard clauses at the beginning of a method should be ignored + +#### Noncompliant Code: + +```kotlin +fun foo(i: Int): String { + when (i) { + 1 -> return "one" + 2 -> return "two" + else -> return "other" + } +} +``` + +#### Compliant Code: + +```kotlin +fun foo(i: Int): String { + return when (i) { + 1 -> "one" + 2 -> "two" + else -> "other" + } +} +``` + +### SafeCast + +This rule inspects casts and reports casts which could be replaced with safe casts instead. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +fun numberMagic(number: Number) { + val i = if (number is Int) number else null + // ... +} +``` + +#### Compliant Code: + +```kotlin +fun numberMagic(number: Number) { + val i = number as? Int + // ... +} +``` + +### SerialVersionUIDInSerializableClass + +Classes which implement the `Serializable` interface should also correctly declare a `serialVersionUID`. +This rule verifies that a `serialVersionUID` was correctly defined. + +**Active by default**: Yes - Since v1.16.0 + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +class IncorrectSerializable : Serializable { + + companion object { + val serialVersionUID = 1 // wrong declaration for UID + } +} +``` + +#### Compliant Code: + +```kotlin +class CorrectSerializable : Serializable { + + companion object { + const val serialVersionUID = 1L + } +} +``` + +### SpacingBetweenPackageAndImports + +This rule verifies spacing between package and import statements as well as between import statements and class +declarations. + +**Active by default**: No + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +package foo +import a.b +class Bar { } +``` + +#### Compliant Code: + +```kotlin +package foo + +import a.b + +class Bar { } +``` + +### ThrowsCount + +Functions should have clear `throw` statements. Functions with many `throw` statements can be harder to read and lead +to confusion. Instead, prefer limiting the number of `throw` statements in a function. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 10min + +#### Configuration options: + +* ``max`` (default: ``2``) + + maximum amount of throw statements in a method + +* ``excludeGuardClauses`` (default: ``false``) + + if set to true, guard clauses do not count towards the allowed throws count + +#### Noncompliant Code: + +```kotlin +fun foo(i: Int) { + when (i) { + 1 -> throw IllegalArgumentException() + 2 -> throw IllegalArgumentException() + 3 -> throw IllegalArgumentException() + } +} +``` + +#### Compliant Code: + +```kotlin +fun foo(i: Int) { + when (i) { + 1,2,3 -> throw IllegalArgumentException() + } +} +``` + +### TrailingWhitespace + +This rule reports lines that end with a whitespace. + +**Active by default**: No + +**Debt**: 5min + +### TrimMultilineRawString + +All the Raw strings that have more than one line should be followed by `trimMargin()` or `trimIndent()`. + +**Active by default**: No + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +""" +Hello World! +How are you? +""" +``` + +#### Compliant Code: + +```kotlin +""" +| Hello World! +| How are you? +""".trimMargin() + +""" +Hello World! +How are you? +""".trimIndent() + +"""Hello World! How are you?""" +``` + +### UnderscoresInNumericLiterals + +This rule detects and reports long base 10 numbers which should be separated with underscores +for readability. For `Serializable` classes or objects, the field `serialVersionUID` is +explicitly ignored. For floats and doubles, anything to the right of the decimal point is ignored. + +**Active by default**: No + +**Debt**: 5min + +#### Configuration options: + +* ~~``acceptableDecimalLength``~~ (default: ``5``) + + **Deprecated**: Use `acceptableLength` instead + + Length under which base 10 numbers are not required to have underscores + +* ``acceptableLength`` (default: ``4``) + + Maximum number of consecutive digits that a numeric literal can have without using an underscore + +* ``allowNonStandardGrouping`` (default: ``false``) + + If set to false, groups of exactly three digits must be used. If set to true, 100_00 is allowed. + +#### Noncompliant Code: + +```kotlin +const val DEFAULT_AMOUNT = 1000000 +``` + +#### Compliant Code: + +```kotlin +const val DEFAULT_AMOUNT = 1_000_000 +``` + +### UnnecessaryAbstractClass + +This rule inspects `abstract` classes. In case an `abstract class` does not have any concrete members it should be +refactored into an interface. Abstract classes which do not define any `abstract` members should instead be +refactored into concrete classes. + +**Active by default**: Yes - Since v1.2.0 + +**Debt**: 5min + +#### Configuration options: + +* ~~``excludeAnnotatedClasses``~~ (default: ``[]``) + + **Deprecated**: Use `ignoreAnnotated` instead + + Allows you to provide a list of annotations that disable this check. + +#### Noncompliant Code: + +```kotlin +abstract class OnlyAbstractMembersInAbstractClass { // violation: no concrete members + + abstract val i: Int + abstract fun f() +} + +abstract class OnlyConcreteMembersInAbstractClass { // violation: no abstract members + + val i: Int = 0 + fun f() { } +} +``` + +### UnnecessaryAnnotationUseSiteTarget + +This rule inspects the use of the Annotation use-site Target. In case that the use-site Target is not needed it can +be removed. For more information check the kotlin documentation: +[Annotation use-site targets](https://kotlinlang.org/docs/annotations.html#annotation-use-site-targets) + +**Active by default**: No + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +@property:Inject private val foo: String = "bar" // violation: unnecessary @property: + +class Module(@param:Inject private val foo: String) // violation: unnecessary @param: +``` + +#### Compliant Code: + +```kotlin +class Module(@Inject private val foo: String) +``` + +### UnnecessaryApply + +`apply` expressions are used frequently, but sometimes their usage should be replaced with +an ordinary method/extension function call to reduce visual complexity + +**Active by default**: Yes - Since v1.16.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +config.apply { version = "1.2" } // can be replaced with `config.version = "1.2"` +config?.apply { environment = "test" } // can be replaced with `config?.environment = "test"` +config?.apply { println(version) } // `apply` can be replaced by `let` +``` + +#### Compliant Code: + +```kotlin +config.apply { + version = "1.2" + environment = "test" +} +``` + +### UnnecessaryBackticks + +This rule reports unnecessary backticks. + +**Active by default**: No + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +class `HelloWorld` +``` + +#### Compliant Code: + +```kotlin +class HelloWorld +``` + +### UnnecessaryFilter + +Unnecessary filters add complexity to the code and accomplish nothing. They should be removed. + +**Active by default**: Yes - Since v1.21.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +val x = listOf(1, 2, 3) + .filter { it > 1 } + .count() + +val x = listOf(1, 2, 3) + .filter { it > 1 } + .isEmpty() +``` + +#### Compliant Code: + +```kotlin +val x = listOf(1, 2, 3) + .count { it > 2 } +} + +val x = listOf(1, 2, 3) + .none { it > 1 } +``` + +### UnnecessaryInheritance + +This rule reports unnecessary super types. Inheriting from `Any` or `Object` is unnecessary and should simply be +removed. + +**Active by default**: Yes - Since v1.2.0 + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +class A : Any() +class B : Object() +``` + +### UnnecessaryInnerClass + +This rule reports unnecessary inner classes. Nested classes that do not access members from the outer class do +not require the `inner` qualifier. + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +class A { + val foo = "BAR" + + inner class B { + val fizz = "BUZZ" + + fun printFizz() { + println(fizz) + } + } +} +``` + +### UnnecessaryLet + +`let` expressions are used extensively in our code for null-checking and chaining functions, +but sometimes their usage should be replaced with an ordinary method/extension function call +to reduce visual complexity. + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +a.let { print(it) } // can be replaced with `print(a)` +a.let { it.plus(1) } // can be replaced with `a.plus(1)` +a?.let { it.plus(1) } // can be replaced with `a?.plus(1)` +a?.let { that -> that.plus(1) }?.let { it.plus(1) } // can be replaced with `a?.plus(1)?.plus(1)` +a.let { 1.plus(1) } // can be replaced with `1.plus(1)` +a?.let { 1.plus(1) } // can be replaced with `if (a != null) 1.plus(1)` +``` + +#### Compliant Code: + +```kotlin +a?.let { print(it) } +a?.let { 1.plus(it) } ?.let { msg -> print(msg) } +a?.let { it.plus(it) } +val b = a?.let { 1.plus(1) } +``` + +### UnnecessaryParentheses + +This rule reports unnecessary parentheses around expressions. +These unnecessary parentheses can safely be removed. + +Added in v1.0.0.RC4 + +**Active by default**: No + +**Debt**: 5min + +#### Configuration options: + +* ``allowForUnclearPrecedence`` (default: ``false``) + + allow parentheses when not strictly required but precedence may be unclear, such as `(a && b) || c` + +#### Noncompliant Code: + +```kotlin +val local = (5 + 3) + +if ((local == 8)) { } + +fun foo() { + function({ input -> println(input) }) +} +``` + +#### Compliant Code: + +```kotlin +val local = 5 + 3 + +if (local == 8) { } + +fun foo() { + function { input -> println(input) } +} +``` + +### UntilInsteadOfRangeTo + +Reports calls to '..' operator instead of calls to 'until'. +'until' is applicable in cases where the upper range value is described as +some value subtracted by 1. 'until' helps to prevent off-by-one errors. + +**Active by default**: No + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +for (i in 0 .. 10 - 1) {} +val range = 0 .. 10 - 1 +``` + +#### Compliant Code: + +```kotlin +for (i in 0 until 10) {} +val range = 0 until 10 +``` + +### UnusedImports + +This rule reports unused imports. Unused imports are dead code and should be removed. +Exempt from this rule are imports resulting from references to elements within KDoc and +from destructuring declarations (componentN imports). + +**Active by default**: No + +**Debt**: 5min + +### UnusedPrivateClass + +Reports unused private classes. If private classes are unused they should be removed. Otherwise, this dead code +can lead to confusion and potential bugs. + +**Active by default**: Yes - Since v1.2.0 + +**Debt**: 5min + +**Aliases**: unused + +### UnusedPrivateMember + +Reports unused private properties, function parameters and functions. +If these private elements are unused they should be removed. Otherwise, this dead code +can lead to confusion and potential bugs. + +**Active by default**: Yes - Since v1.16.0 + +**Debt**: 5min + +**Aliases**: UNUSED_VARIABLE, UNUSED_PARAMETER, unused + +#### Configuration options: + +* ``allowedNames`` (default: ``'(_|ignored|expected|serialVersionUID)'``) + + unused private member names matching this regex are ignored + +### UseAnyOrNoneInsteadOfFind + +Turn on this rule to flag `find` calls for null check that can be replaced with a `any` or `none` call. + +**Active by default**: Yes - Since v1.21.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +listOf(1, 2, 3).find { it == 4 } != null +listOf(1, 2, 3).find { it == 4 } == null +``` + +#### Compliant Code: + +```kotlin +listOf(1, 2, 3).any { it == 4 } +listOf(1, 2, 3).none { it == 4 } +``` + +### UseArrayLiteralsInAnnotations + +This rule detects annotations which use the arrayOf(...) syntax instead of the array literal [...] syntax. +The latter should be preferred as it is more readable. + +**Active by default**: Yes - Since v1.21.0 + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +@PositiveCase(arrayOf("...")) +``` + +#### Compliant Code: + +```kotlin +@NegativeCase(["..."]) +``` + +### UseCheckNotNull + +Turn on this rule to flag `check` calls for not-null check that can be replaced with a `checkNotNull` call. + +**Active by default**: Yes - Since v1.21.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +check(x != null) +``` + +#### Compliant Code: + +```kotlin +checkNotNull(x) +``` + +### UseCheckOrError + +Kotlin provides a concise way to check invariants as well as pre- and post-conditions. +Prefer them instead of manually throwing an IllegalStateException. + +**Active by default**: Yes - Since v1.21.0 + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +if (value == null) throw IllegalStateException("value should not be null") +if (value < 0) throw IllegalStateException("value is $value but should be at least 0") +when(a) { + 1 -> doSomething() + else -> throw IllegalStateException("Unexpected value") +} +``` + +#### Compliant Code: + +```kotlin +checkNotNull(value) { "value should not be null" } +check(value >= 0) { "value is $value but should be at least 0" } +when(a) { + 1 -> doSomething() + else -> error("Unexpected value") +} +``` + +### UseDataClass + +Classes that simply hold data should be refactored into a `data class`. Data classes are specialized to hold data +and generate `hashCode`, `equals` and `toString` implementations as well. + +Read more about [data classes](https://kotlinlang.org/docs/data-classes.html) + +**Active by default**: No + +**Debt**: 5min + +#### Configuration options: + +* ~~``excludeAnnotatedClasses``~~ (default: ``[]``) + + **Deprecated**: Use `ignoreAnnotated` instead + + allows to provide a list of annotations that disable this check + +* ``allowVars`` (default: ``false``) + + allows to relax this rule in order to exclude classes that contains one (or more) vars + +#### Noncompliant Code: + +```kotlin +class DataClassCandidate(val i: Int) { + val i2: Int = 0 +} +``` + +#### Compliant Code: + +```kotlin +data class DataClass(val i: Int, val i2: Int) + +// classes with delegating interfaces are compliant +interface I +class B() : I +class A(val b: B) : I by b +``` + +### UseEmptyCounterpart + +Instantiation of an object's "empty" state should use the object's "empty" initializer for clarity purposes. + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +arrayOf() +listOf() // or listOfNotNull() +mapOf() +sequenceOf() +setOf() +``` + +#### Compliant Code: + +```kotlin +emptyArray() +emptyList() +emptyMap() +emptySequence() +emptySet() +``` + +### UseIfEmptyOrIfBlank + +This rule detects `isEmpty` or `isBlank` calls to assign a default value. They can be replaced with `ifEmpty` or +`ifBlank` calls. + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +fun test(list: List, s: String) { + val a = if (list.isEmpty()) listOf(1) else list + val b = if (list.isNotEmpty()) list else listOf(2) + val c = if (s.isBlank()) "foo" else s + val d = if (s.isNotBlank()) s else "bar" +} +``` + +#### Compliant Code: + +```kotlin +fun test(list: List, s: String) { + val a = list.ifEmpty { listOf(1) } + val b = list.ifEmpty { listOf(2) } + val c = s.ifBlank { "foo" } + val d = s.ifBlank { "bar" } +} +``` + +### UseIfInsteadOfWhen + +Binary expressions are better expressed using an `if` expression than a `when` expression. + +See [if versus when](https://kotlinlang.org/docs/coding-conventions.html#if-versus-when) + +**Active by default**: No + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +when (x) { + null -> true + else -> false +} +``` + +#### Compliant Code: + +```kotlin +if (x == null) true else false +``` + +### UseIsNullOrEmpty + +This rule detects null or empty checks that can be replaced with `isNullOrEmpty()` call. + +**Active by default**: Yes - Since v1.21.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +fun foo(x: List?) { + if (x == null || x.isEmpty()) return +} +fun bar(x: List?) { + if (x == null || x.count() == 0) return +} +fun baz(x: List?) { + if (x == null || x.size == 0) return +} +``` + +#### Compliant Code: + +```kotlin +if (x.isNullOrEmpty()) return +``` + +### UseOrEmpty + +This rule detects `?: emptyList()` that can be replaced with `orEmpty()` call. + +**Active by default**: Yes - Since v1.21.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +fun test(x: List?, s: String?) { + val a = x ?: emptyList() + val b = s ?: "" +} +``` + +#### Compliant Code: + +```kotlin +fun test(x: List?, s: String?) { + val a = x.orEmpty() + val b = s.orEmpty() +} +``` + +### UseRequire + +Kotlin provides a much more concise way to check preconditions than to manually throw an +IllegalArgumentException. + +**Active by default**: Yes - Since v1.21.0 + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +if (value == null) throw IllegalArgumentException("value should not be null") +if (value < 0) throw IllegalArgumentException("value is $value but should be at least 0") +``` + +#### Compliant Code: + +```kotlin +requireNotNull(value) { "value should not be null" } +require(value >= 0) { "value is $value but should be at least 0" } +``` + +### UseRequireNotNull + +Turn on this rule to flag `require` calls for not-null check that can be replaced with a `requireNotNull` call. + +**Active by default**: Yes - Since v1.21.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +require(x != null) +``` + +#### Compliant Code: + +```kotlin +requireNotNull(x) +``` + +### UseSumOfInsteadOfFlatMapSize + +Turn on this rule to flag `flatMap` and `size/count` calls that can be replaced with a `sumOf` call. + +**Active by default**: No + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +class Foo(val foo: List) +list.flatMap { it.foo }.size +list.flatMap { it.foo }.count() +list.flatMap { it.foo }.count { it > 2 } +listOf(listOf(1), listOf(2, 3)).flatten().size +``` + +#### Compliant Code: + +```kotlin +list.sumOf { it.foo.size } +list.sumOf { it.foo.count() } +list.sumOf { it.foo.count { foo -> foo > 2 } } +listOf(listOf(1), listOf(2, 3)).sumOf { it.size } +``` + +### UselessCallOnNotNull + +The Kotlin stdlib provides some functions that are designed to operate on references that may be null. These +functions can also be called on non-nullable references or on collections or sequences that are known to be empty - +the calls are redundant in this case and can be removed or should be changed to a call that does not check whether +the value is null or not. + +**Active by default**: Yes - Since v1.2.0 + +**Requires Type Resolution** + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +val testList = listOf("string").orEmpty() +val testList2 = listOf("string").orEmpty().map { _ } +val testList3 = listOfNotNull("string") +val testString = ""?.isNullOrBlank() +``` + +#### Compliant Code: + +```kotlin +val testList = listOf("string") +val testList2 = listOf("string").map { } +val testList3 = listOf("string") +val testString = ""?.isBlank() +``` + +### UtilityClassWithPublicConstructor + +A class which only contains utility variables and functions with no concrete implementation can be refactored +into an `object` or a class with a non-public constructor. +Furthermore, this rule reports utility classes which are not final. + +**Active by default**: Yes - Since v1.2.0 + +**Debt**: 5min + +#### Noncompliant Code: + +```kotlin +class UtilityClassViolation { + + // public constructor here + constructor() { + // ... + } + + companion object { + val i = 0 + } +} + +open class UtilityClassViolation private constructor() { + + // ... +} +``` + +#### Compliant Code: + +```kotlin +class UtilityClass { + + private constructor() { + // ... + } + + companion object { + val i = 0 + } +} +object UtilityClass { + + val i = 0 +} +``` + +### VarCouldBeVal + +Reports var declarations (both local variables and private class properties) that could be val, +as they are not re-assigned. Val declarations are assign-once (read-only), which makes understanding +the current state easier. + +**Active by default**: Yes - Since v1.16.0 + +**Requires Type Resolution** + +**Debt**: 5min + +**Aliases**: CanBeVal + +#### Configuration options: + +* ``ignoreLateinitVar`` (default: ``false``) + + Whether to ignore uninitialized lateinit vars + +#### Noncompliant Code: + +```kotlin +fun example() { + var i = 1 // violation: this variable is never re-assigned + val j = i + 1 +} +``` + +#### Compliant Code: + +```kotlin +fun example() { + val i = 1 + val j = i + 1 +} +``` + +### WildcardImport + +Wildcard imports should be replaced with imports using fully qualified class names. This helps increase clarity of +which classes are imported and helps prevent naming conflicts. + +Library updates can introduce naming clashes with your own classes which might result in compilation errors. + +**NOTE**: This rule has a twin implementation NoWildcardImports in the formatting rule set (a wrapped KtLint rule). +When suppressing an issue of WildcardImport in the baseline file, make sure to suppress the corresponding NoWildcardImports issue. + +**Active by default**: Yes - Since v1.0.0 + +**Debt**: 5min + +#### Configuration options: + +* ``excludeImports`` (default: ``['java.util.*']``) + + Define a list of package names that should be allowed to be imported with wildcard imports. + +#### Noncompliant Code: + +```kotlin +import io.gitlab.arturbosch.detekt.* + +class DetektElements { + val element1 = DetektElement1() + val element2 = DetektElement2() +} +``` + +#### Compliant Code: + +```kotlin +import io.gitlab.arturbosch.detekt.DetektElement1 +import io.gitlab.arturbosch.detekt.DetektElement2 + +class DetektElements { + val element1 = DetektElement1() + val element2 = DetektElement2() +} +``` diff --git a/website/versioned_sidebars/version-1.22.0-sidebars.json b/website/versioned_sidebars/version-1.22.0-sidebars.json new file mode 100644 index 00000000000..771aeb7601c --- /dev/null +++ b/website/versioned_sidebars/version-1.22.0-sidebars.json @@ -0,0 +1,24 @@ +{ + "defaultSidebar": [ + { + "type": "autogenerated", + "dirName": "." + }, + { + "type": "category", + "label": "Changelogs", + "items": [ + { + "type": "link", + "label": "1.x Changelog", + "href": "/changelog" + }, + { + "type": "link", + "label": "0.x Changelog", + "href": "/changelog-pre-stable" + } + ] + } + ] +} diff --git a/website/versions.json b/website/versions.json index 008de4b0aa7..510ea32345f 100644 --- a/website/versions.json +++ b/website/versions.json @@ -1,3 +1,4 @@ [ + "1.22.0", "1.21.0" ]