From 21e49b60151600f98d56c6f9b264d110bfb17cbe Mon Sep 17 00:00:00 2001 From: "Vitaly V. Pinchuk" Date: Tue, 12 Jul 2022 17:19:41 +0300 Subject: [PATCH] [WIP] Add config generator for custom rules --- .github/workflows/codecoverage.yaml | 4 +- .github/workflows/deploy-snapshot.yaml | 6 +- .../detekt-with-type-resolution.yaml | 10 +- .github/workflows/pre-merge.yaml | 22 +- .github/workflows/website.yaml | 8 +- README.md | 6 +- build-logic/src/main/kotlin/Versions.kt | 2 +- build-logic/src/main/kotlin/module.gradle.kts | 1 - code-coverage-report/build.gradle.kts | 3 +- config/detekt/custom-rules-config.yml | 81 ++ config/detekt/detekt.yml | 812 ++++++++++++++-- detekt-api/build.gradle.kts | 4 + detekt-api/detekt.yml | 896 ++++++++++++++++++ .../gitlab/arturbosch/detekt/api/Location.kt | 15 +- .../detekt/api/AnnotationExcluderSpec.kt | 8 +- .../detekt/api/internal/PathMatchersSpec.kt | 4 +- .../gitlab/arturbosch/detekt/cli/CliArgs.kt | 7 + .../gitlab/arturbosch/detekt/cli/CliRunner.kt | 2 + .../io/gitlab/arturbosch/detekt/cli/Main.kt | 2 + .../cli/runners/CustomConfigExporter.kt | 24 + detekt-core/build.gradle.kts | 1 + .../arturbosch/detekt/core/BindingContext.kt | 1 + .../core/tooling/CustomConfigProvider.kt | 20 + ...kt.tooling.api.CustomConfigurationProvider | 1 + .../main/resources/default-detekt-config.yml | 15 +- .../detekt/core/config/YamlConfigSpec.kt | 12 +- .../detekt/formatting/FormattingRule.kt | 26 +- .../detekt/formatting/KtLintMultiRule.kt | 2 + .../wrappers/AnnotationOnSeparateLine.kt | 4 +- .../formatting/wrappers/AnnotationSpacing.kt | 4 +- .../wrappers/ArgumentListWrapping.kt | 6 +- .../formatting/wrappers/CommentWrapping.kt | 2 - .../formatting/wrappers/EnumEntryNameCase.kt | 4 +- .../formatting/wrappers/FinalNewline.kt | 2 - .../formatting/wrappers/ImportOrdering.kt | 2 - .../detekt/formatting/wrappers/Indentation.kt | 2 - .../formatting/wrappers/KdocWrapping.kt | 2 - .../formatting/wrappers/MaximumLineLength.kt | 2 - .../formatting/wrappers/MultiLineIfElse.kt | 4 +- .../NoBlankLinesInChainedMethodCalls.kt | 18 + .../wrappers/NoEmptyFirstLineInMethodBlock.kt | 4 +- .../formatting/wrappers/NoWildcardImports.kt | 2 - .../detekt/formatting/wrappers/PackageName.kt | 4 +- .../wrappers/ParameterListWrapping.kt | 2 - .../wrappers/SpacingAroundAngleBrackets.kt | 4 +- .../wrappers/SpacingAroundDoubleColon.kt | 4 +- .../wrappers/SpacingAroundUnaryOperator.kt | 4 +- ...acingBetweenDeclarationsWithAnnotations.kt | 4 +- .../SpacingBetweenDeclarationsWithComments.kt | 4 +- .../formatting/wrappers/TrailingComma.kt | 2 - ...ecessaryParenthesesBeforeTrailingLambda.kt | 2 - .../detekt/formatting/wrappers/Wrapping.kt | 12 + .../src/main/resources/config/config.yml | 28 +- .../detekt/formatting/IndentationSpec.kt | 2 +- .../detekt/formatting/WrappingSpec.kt | 2 +- detekt-generator/build.gradle.kts | 11 +- .../detekt/generator/CustomConfig.kt | 24 + .../detekt/generator/DetektPrinter.kt | 6 + .../arturbosch/detekt/generator/Generator.kt | 14 + .../detekt/generator/GeneratorArgs.kt | 55 +- .../arturbosch/detekt/generator/Main.kt | 2 +- .../printer/defaultconfig/ConfigPrinter.kt | 7 + detekt-gradle-plugin/build.gradle.kts | 12 +- .../arturbosch/detekt/DetektAndroidSpec.kt | 32 +- .../gitlab/arturbosch/detekt/DetektJvmSpec.kt | 8 +- .../detekt/DetektMultiplatformSpec.kt | 5 +- .../arturbosch/detekt/GradleVersionSpec.kt | 6 +- .../io/gitlab/arturbosch/detekt/JvmSpec.kt | 4 +- .../detekt/DetektGenerateCustomConfigTask.kt | 87 ++ .../gitlab/arturbosch/detekt/DetektPlugin.kt | 14 + .../detekt/internal/DetektMultiplatform.kt | 3 +- .../arturbosch/detekt/invoke/CliArgument.kt | 5 + .../gitlab/arturbosch/detekt/DetektJvmSpec.kt | 1 - .../arturbosch/detekt/DetektPlainSpec.kt | 2 - .../detekt/testkit/DslGradleRunner.kt | 5 +- .../io/github/detekt/metrics/LinesOfCode.kt | 13 +- .../detekt/parser/KotlinEnvironmentUtils.kt | 2 + detekt-psi-utils/api/detekt-psi-utils.api | 3 + detekt-psi-utils/build.gradle.kts | 4 + .../kotlin/io/github/detekt/psi/KtFiles.kt | 11 + .../arturbosch/detekt/rules/KtModifierList.kt | 9 +- .../arturbosch/detekt/rules/Traversing.kt | 6 +- .../rules/complexity/ComplexMethodSpec.kt | 6 +- .../detekt/rules/complexity/LargeClassSpec.kt | 2 +- .../complexity/NestedScopeFunctionsSpec.kt | 2 +- .../documentation/UndocumentedPublicClass.kt | 5 +- .../UndocumentedPublicFunction.kt | 12 +- .../UndocumentedPublicProperty.kt | 9 +- .../UndocumentedPublicClassSpec.kt | 20 + .../UndocumentedPublicFunctionSpec.kt | 24 + .../UndocumentedPublicPropertySpec.kt | 24 + .../rules/empty/EmptyFunctionBlockSpec.kt | 8 +- .../detekt/rules/bugs/ExitOutsideMain.kt | 2 +- .../detekt/rules/bugs/UnusedUnaryOperator.kt | 8 +- .../rules/bugs/CastToNullableTypeSpec.kt | 2 +- .../bugs/DoubleMutabilityForCollectionSpec.kt | 66 +- .../ElseCaseInsteadOfExhaustiveWhenSpec.kt | 4 +- .../rules/bugs/IgnoredReturnValueSpec.kt | 20 +- .../detekt/rules/bugs/MissingWhenCaseSpec.kt | 37 +- .../rules/bugs/NullableToStringCallSpec.kt | 1 - .../rules/bugs/RedundantElseInWhenSpec.kt | 5 +- .../rules/bugs/UnnecessarySafeCallSpec.kt | 4 +- .../rules/bugs/UnreachableCatchBlockSpec.kt | 6 +- .../rules/bugs/UnusedUnaryOperatorSpec.kt | 20 +- .../ThrowingNewInstanceOfSameExceptionSpec.kt | 4 +- .../rules/naming/BooleanPropertyNaming.kt | 3 +- .../detekt/rules/naming/FunctionNamingSpec.kt | 8 +- .../naming/MatchingDeclarationNameSpec.kt | 14 +- .../rules/naming/NoNameShadowingSpec.kt | 2 +- .../rules/naming/ObjectPropertyNamingSpec.kt | 4 +- .../detekt/rules/naming/VariableNamingSpec.kt | 4 +- .../ExplicitCollectionElementAccessMethod.kt | 2 +- .../detekt/rules/style/ForbiddenImport.kt | 35 +- .../detekt/rules/style/ForbiddenMethodCall.kt | 52 +- .../detekt/rules/style/NewLineAtEndOfFile.kt | 6 +- .../rules/style/RedundantExplicitType.kt | 2 + .../detekt/rules/style/ReturnCount.kt | 4 +- .../detekt/rules/style/StyleGuideProvider.kt | 1 + .../rules/style/TrimMultilineRawString.kt | 74 ++ .../rules/style/UnnecessaryParentheses.kt | 102 +- .../detekt/rules/style/UnusedImports.kt | 40 +- .../rules/style/UselessCallOnNotNull.kt | 2 +- .../detekt/rules/style/ForbiddenImportSpec.kt | 50 +- .../rules/style/ForbiddenMethodCallSpec.kt | 104 +- .../rules/style/ForbiddenSuppressSpec.kt | 12 +- .../FunctionOnlyReturningConstantSpec.kt | 8 +- .../detekt/rules/style/MagicNumberSpec.kt | 32 +- .../detekt/rules/style/MaxLineLengthSpec.kt | 2 +- .../detekt/rules/style/MayBeConstSpec.kt | 22 +- .../rules/style/NewLineAtEndOfFileSpec.kt | 2 +- .../rules/style/ObjectLiteralToLambdaSpec.kt | 24 +- .../rules/style/OptionalWhenBracesSpec.kt | 4 +- .../style/ProtectedMemberInFinalClassSpec.kt | 18 +- .../style/RedundantHigherOrderMapUsageSpec.kt | 4 +- .../detekt/rules/style/ReturnCountSpec.kt | 2 +- .../rules/style/TrimMultilineRawStringSpec.kt | 87 ++ .../style/UnnecessaryAbstractClassSpec.kt | 8 +- .../rules/style/UnnecessaryParenthesesSpec.kt | 245 ++++- .../detekt/rules/style/UnusedImportsSpec.kt | 32 + .../rules/style/UnusedPrivateClassSpec.kt | 8 +- .../rules/style/UnusedPrivateMemberSpec.kt | 8 +- .../detekt/rules/style/UseCheckOrErrorSpec.kt | 14 +- .../rules/style/UseIfEmptyOrIfBlankSpec.kt | 8 +- .../rules/style/UseIsNullOrEmptySpec.kt | 2 +- .../detekt/rules/style/UseOrEmptySpec.kt | 2 +- .../detekt/rules/style/UseRequireSpec.kt | 8 +- detekt-test-utils/build.gradle.kts | 4 + .../detekt/test/utils/KtTestCompiler.kt | 2 + detekt-test/api/detekt-test.api | 8 + detekt-test/build.gradle.kts | 4 + .../detekt/test/FindingsAssertions.kt | 33 +- .../arturbosch/detekt/test/TestConfig.kt | 5 + detekt-tooling/api/detekt-tooling.api | 12 + detekt-tooling/build.gradle.kts | 4 + .../api/CustomConfigurationProvider.kt | 18 + gradle.properties | 3 +- gradle/libs.versions.toml | 14 +- gradle/wrapper/gradle-wrapper.properties | 2 +- scripts/github-milestone-report.main.kts | 3 +- settings.gradle.kts | 2 +- website/docs/gettingstarted/gradle.mdx | 2 +- website/docs/introduction/changelog 1.x.x.md | 43 +- website/docs/introduction/compatibility.md | 1 + website/package.json | 6 +- website/src/remark/detektVersionReplace.js | 2 +- website/yarn.lock | 388 ++++---- 166 files changed, 3569 insertions(+), 817 deletions(-) create mode 100644 config/detekt/custom-rules-config.yml create mode 100644 detekt-api/detekt.yml create mode 100644 detekt-cli/src/main/kotlin/io/gitlab/arturbosch/detekt/cli/runners/CustomConfigExporter.kt create mode 100644 detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/tooling/CustomConfigProvider.kt create mode 100644 detekt-core/src/main/resources/META-INF/services/io.github.detekt.tooling.api.CustomConfigurationProvider create mode 100644 detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/NoBlankLinesInChainedMethodCalls.kt create mode 100644 detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/CustomConfig.kt create mode 100644 detekt-gradle-plugin/src/main/kotlin/io/gitlab/arturbosch/detekt/DetektGenerateCustomConfigTask.kt create mode 100644 detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/TrimMultilineRawString.kt create mode 100644 detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/TrimMultilineRawStringSpec.kt create mode 100644 detekt-tooling/src/main/kotlin/io/github/detekt/tooling/api/CustomConfigurationProvider.kt diff --git a/.github/workflows/codecoverage.yaml b/.github/workflows/codecoverage.yaml index 489a5ae25517..346b3f7b17dc 100644 --- a/.github/workflows/codecoverage.yaml +++ b/.github/workflows/codecoverage.yaml @@ -23,13 +23,13 @@ jobs: uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3 - name: Setup Java - uses: actions/setup-java@16cca5479d7c6b6843f6a6515640ba33c6501543 # tag=v3 + uses: actions/setup-java@2c7a4878f5d120bd643426d54ae1209b29cc01a3 # tag=v3 with: java-version: 17 distribution: 'temurin' - name: Generate Coverage Report - uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1 # tag=v2 + uses: gradle/gradle-build-action@cd3cedc781988c804f626f4cd2dc51d0bdf02a12 # tag=v2 with: arguments: jacocoMergedReport diff --git a/.github/workflows/deploy-snapshot.yaml b/.github/workflows/deploy-snapshot.yaml index d479b37b321c..7f4f2b620235 100644 --- a/.github/workflows/deploy-snapshot.yaml +++ b/.github/workflows/deploy-snapshot.yaml @@ -20,18 +20,18 @@ jobs: uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3 - name: Setup Java - uses: actions/setup-java@16cca5479d7c6b6843f6a6515640ba33c6501543 # tag=v3 + uses: actions/setup-java@2c7a4878f5d120bd643426d54ae1209b29cc01a3 # tag=v3 with: java-version: 17 distribution: 'temurin' - name: Build detekt - uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1 # tag=v2 + uses: gradle/gradle-build-action@cd3cedc781988c804f626f4cd2dc51d0bdf02a12 # tag=v2 with: arguments: build - name: Deploy Snapshot - uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1 # tag=v2 + uses: gradle/gradle-build-action@cd3cedc781988c804f626f4cd2dc51d0bdf02a12 # tag=v2 env: ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }} ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }} diff --git a/.github/workflows/detekt-with-type-resolution.yaml b/.github/workflows/detekt-with-type-resolution.yaml index bc31220a1313..99f241be5a59 100644 --- a/.github/workflows/detekt-with-type-resolution.yaml +++ b/.github/workflows/detekt-with-type-resolution.yaml @@ -28,18 +28,18 @@ jobs: uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3 - name: Setup Java - uses: actions/setup-java@16cca5479d7c6b6843f6a6515640ba33c6501543 # tag=v3 + uses: actions/setup-java@2c7a4878f5d120bd643426d54ae1209b29cc01a3 # tag=v3 with: java-version: 17 distribution: 'temurin' - name: Run detekt-cli with argsfile - uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1 # tag=v2 + uses: gradle/gradle-build-action@cd3cedc781988c804f626f4cd2dc51d0bdf02a12 # tag=v2 with: arguments: :detekt-cli:runWithArgsFile - name: Upload SARIF to Github using the upload-sarif action - uses: github/codeql-action/upload-sarif@3f62b754e23e0dd60f91b744033e1dc1654c0ec6 # tag=v2 + uses: github/codeql-action/upload-sarif@3e7e3b32d0fb8283594bb0a76cc60a00918b0969 # tag=v2 if: ${{ always() }} with: sarif_file: build/detekt-report.sarif @@ -53,11 +53,11 @@ jobs: uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3 - name: Setup Java - uses: actions/setup-java@16cca5479d7c6b6843f6a6515640ba33c6501543 # tag=v3 + uses: actions/setup-java@2c7a4878f5d120bd643426d54ae1209b29cc01a3 # tag=v3 with: java-version: 17 distribution: 'temurin' - name: Run analysis - uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1 # tag=v2 + uses: gradle/gradle-build-action@cd3cedc781988c804f626f4cd2dc51d0bdf02a12 # tag=v2 with: arguments: detektMain detektTest diff --git a/.github/workflows/pre-merge.yaml b/.github/workflows/pre-merge.yaml index 9510dbf07691..d6a2c3b930f5 100644 --- a/.github/workflows/pre-merge.yaml +++ b/.github/workflows/pre-merge.yaml @@ -32,12 +32,12 @@ jobs: - name: Checkout Repo uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3 - name: Setup Java - uses: actions/setup-java@16cca5479d7c6b6843f6a6515640ba33c6501543 # tag=v3 + uses: actions/setup-java@2c7a4878f5d120bd643426d54ae1209b29cc01a3 # tag=v3 with: java-version: ${{ matrix.jdk }} distribution: 'temurin' - name: Build detekt - uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1 # tag=v2 + uses: gradle/gradle-build-action@cd3cedc781988c804f626f4cd2dc51d0bdf02a12 # tag=v2 with: arguments: build -x detekt - uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # tag=v3 @@ -46,15 +46,15 @@ jobs: path: '**.hprof' if-no-files-found: ignore - name: Run detekt-cli --help - uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1 # tag=v2 + uses: gradle/gradle-build-action@cd3cedc781988c804f626f4cd2dc51d0bdf02a12 # tag=v2 with: arguments: :detekt-cli:runWithHelpFlag - name: Run detekt-cli with argsfile - uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1 # tag=v2 + uses: gradle/gradle-build-action@cd3cedc781988c804f626f4cd2dc51d0bdf02a12 # tag=v2 with: arguments: :detekt-cli:runWithArgsFile - name: Try to publish to Maven Local - uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1 # tag=v2 + uses: gradle/gradle-build-action@cd3cedc781988c804f626f4cd2dc51d0bdf02a12 # tag=v2 with: arguments: publishToMavenLocal @@ -65,12 +65,12 @@ jobs: - name: Checkout Repo uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3 - name: Setup Java - uses: actions/setup-java@16cca5479d7c6b6843f6a6515640ba33c6501543 # tag=v3 + uses: actions/setup-java@2c7a4878f5d120bd643426d54ae1209b29cc01a3 # tag=v3 with: java-version: 17 distribution: 'temurin' - name: Verify Generated Detekt Config File - uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1 # tag=v2 + uses: gradle/gradle-build-action@cd3cedc781988c804f626f4cd2dc51d0bdf02a12 # tag=v2 with: arguments: verifyGeneratorOutput @@ -81,12 +81,12 @@ jobs: - name: Checkout Repo uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3 - name: Setup Java - uses: actions/setup-java@16cca5479d7c6b6843f6a6515640ba33c6501543 # tag=v3 + uses: actions/setup-java@2c7a4878f5d120bd643426d54ae1209b29cc01a3 # tag=v3 with: java-version: 17 distribution: 'temurin' - name: Build and compile test snippets - uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1 # tag=v2 + uses: gradle/gradle-build-action@cd3cedc781988c804f626f4cd2dc51d0bdf02a12 # tag=v2 with: arguments: test -Pcompile-test-snippets=true @@ -97,11 +97,11 @@ jobs: - name: Checkout Repo uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3 - name: Setup Java - uses: actions/setup-java@16cca5479d7c6b6843f6a6515640ba33c6501543 # tag=v3 + uses: actions/setup-java@2c7a4878f5d120bd643426d54ae1209b29cc01a3 # tag=v3 with: java-version: 17 distribution: 'temurin' - name: Run with allWarningsAsErrors - uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1 # tag=v2 + uses: gradle/gradle-build-action@cd3cedc781988c804f626f4cd2dc51d0bdf02a12 # tag=v2 with: arguments: build -x detekt -PwarningsAsErrors=true diff --git a/.github/workflows/website.yaml b/.github/workflows/website.yaml index f46bda61d3e3..73ba0bce51d6 100644 --- a/.github/workflows/website.yaml +++ b/.github/workflows/website.yaml @@ -23,20 +23,20 @@ jobs: uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3 - name: Setup Java - uses: actions/setup-java@16cca5479d7c6b6843f6a6515640ba33c6501543 # tag=v3 + uses: actions/setup-java@2c7a4878f5d120bd643426d54ae1209b29cc01a3 # tag=v3 with: java-version: 17 distribution: 'temurin' - name: Setup Node - uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd # tag=v3 + uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 # tag=v3 with: node-version: '16' cache: 'yarn' cache-dependency-path: 'website/yarn.lock' - name: Build Detekt Documentation - uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1 # tag=v2 + uses: gradle/gradle-build-action@cd3cedc781988c804f626f4cd2dc51d0bdf02a12 # tag=v2 with: arguments: :detekt-generator:generateDocumentation @@ -49,7 +49,7 @@ jobs: run: yarn build - name: Deploy Github Pages (only on main) - uses: JamesIves/github-pages-deploy-action@e6d003d0839927f5a4b998bfd92ed8e448fde37a # tag=v4 + uses: JamesIves/github-pages-deploy-action@13046b614c663b56cba4dda3f30b9736a748b80d # tag=v4 if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} with: branch: gh-pages diff --git a/README.md b/README.md index d85a7516c41c..9f3531157725 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Join the chat at #detekt on KotlinLang](https://img.shields.io/badge/%23detekt-on_slack-red.svg?logo=slack)](https://kotlinlang.slack.com/archives/C88E12QH4) [![Visit the website at detekt.dev/](https://img.shields.io/badge/visit-website-red.svg?logo=firefox)](https://detekt.dev/) [![Maven Central](https://img.shields.io/maven-central/v/io.gitlab.arturbosch.detekt/detekt-cli?label=MavenCentral&logo=apache-maven)](https://search.maven.org/artifact/io.gitlab.arturbosch.detekt/detekt-cli) -[![Gradle Plugin](https://img.shields.io/maven-metadata/v/https/plugins.gradle.org/m2/io/gitlab/arturbosch/detekt/io.gitlab.arturbosch.detekt.gradle.plugin/maven-metadata.xml.svg?label=Gradle&logo=gradle)](https://plugins.gradle.org/plugin/io.gitlab.arturbosch.detekt) +[![Gradle Plugin](https://img.shields.io/maven-central/v/io.gitlab.arturbosch.detekt/detekt-gradle-plugin?label=Gradle&logo=gradle)](https://plugins.gradle.org/plugin/io.gitlab.arturbosch.detekt) [![Revved up by Gradle Enterprise](https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.detekt.dev/scans) ![Pre Merge Checks](https://github.com/detekt/detekt/workflows/Pre%20Merge%20Checks/badge.svg?branch=main) @@ -106,11 +106,11 @@ If you want to use a SNAPSHOT version, you can find more info on [this documenta #### Requirements -Gradle 6.1+ is the minimum requirement. However, the recommended versions together with the other tools recommended versions are: +Gradle 6.7.1+ is the minimum requirement. However, the recommended versions together with the other tools recommended versions are: | Detekt Version | Gradle | Kotlin | AGP | Java Target Level | JDK Max Version | | -------------- | ------- | -------- | ------- | ----------------- | --------------- | -| `1.20.0` | `7.4.2` | `1.6.20` | `7.1.3` | `1.8` | `17` | +| `1.21.0` | `7.5` | `1.6.21` | `7.2.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 f9ab2f076e22..9039e486f26b 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.21.0-RC2" + const val DETEKT: String = "1.21.0" const val SNAPSHOT_NAME: String = "main" const val JVM_TARGET: String = "1.8" diff --git a/build-logic/src/main/kotlin/module.gradle.kts b/build-logic/src/main/kotlin/module.gradle.kts index 9b34d930814c..d466c2dca02d 100644 --- a/build-logic/src/main/kotlin/module.gradle.kts +++ b/build-logic/src/main/kotlin/module.gradle.kts @@ -55,7 +55,6 @@ tasks.withType().configureEach { freeCompilerArgs = listOf( "-progressive", "-Xsuppress-version-warnings", - "-opt-in=kotlin.RequiresOptIn" ) // Note: Currently there are warnings for detekt-gradle-plugin that seemingly can't be fixed // until Gradle releases an update (https://github.com/gradle/gradle/issues/16345) diff --git a/code-coverage-report/build.gradle.kts b/code-coverage-report/build.gradle.kts index 74bfd6018440..7277b577397a 100644 --- a/code-coverage-report/build.gradle.kts +++ b/code-coverage-report/build.gradle.kts @@ -51,7 +51,8 @@ tasks.check { configurations.allCodeCoverageReportClassDirectories.get().attributes { attributes.attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category::class, Category.LIBRARY)) attributes.attribute( - LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements::class, LibraryElements.CLASSES) + LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, + objects.named(LibraryElements::class, LibraryElements.CLASSES) ) } diff --git a/config/detekt/custom-rules-config.yml b/config/detekt/custom-rules-config.yml new file mode 100644 index 000000000000..a42bf2648b7d --- /dev/null +++ b/config/detekt/custom-rules-config.yml @@ -0,0 +1,81 @@ +complexity: + active: true + ComplexCondition: + active: true + threshold: 4 + ComplexInterface: + active: false + threshold: 10 + includeStaticDeclarations: false + includePrivateDeclarations: false + ComplexMethod: + active: true + threshold: 15 + ignoreSingleWhenExpression: false + ignoreSimpleWhenEntries: false + ignoreNestingFunctions: false + nestingFunctions: + - 'also' + - 'apply' + - 'forEach' + - 'isNotNull' + - 'ifNull' + - 'let' + - 'run' + - 'use' + - 'with' + LabeledExpression: + active: false + ignoredLabels: [] + LargeClass: + active: true + threshold: 600 + LongMethod: + active: true + threshold: 60 + LongParameterList: + active: true + functionThreshold: 6 + constructorThreshold: 7 + ignoreDefaultParameters: false + ignoreDataClasses: true + ignoreAnnotatedParameter: [] + MethodOverloading: + active: false + threshold: 6 + NamedArguments: + active: false + threshold: 3 + ignoreArgumentsMatchingNames: false + NestedBlockDepth: + active: true + threshold: 4 + NestedScopeFunctions: + active: false + threshold: 1 + functions: + - 'kotlin.apply' + - 'kotlin.run' + - 'kotlin.with' + - 'kotlin.let' + - 'kotlin.also' + ReplaceSafeCallChainWithRun: + active: false + StringLiteralDuplication: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + threshold: 3 + ignoreAnnotation: true + excludeStringsWithLessThan5Characters: true + ignoreStringsRegex: '$^' + TooManyFunctions: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + thresholdInFiles: 11 + thresholdInClasses: 11 + thresholdInInterfaces: 11 + thresholdInObjects: 11 + thresholdInEnums: 11 + ignoreDeprecated: false + ignorePrivate: false + ignoreOverridden: false diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 399eee6b89d8..ce2f723fbd72 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -1,263 +1,695 @@ +build: + maxIssues: 0 + excludeCorrectable: false + weights: + # complexity: 2 + # LongParameterList: 1 + # style: 1 + # comments: 1 + +config: + validation: true + warningsAsErrors: false + # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]' + excludes: '' + +processors: + active: true + exclude: + - 'DetektProgressListener' + # - 'KtFileCountProcessor' + # - 'PackageCountProcessor' + # - 'ClassCountProcessor' + # - 'FunctionCountProcessor' + # - 'PropertyCountProcessor' + # - 'ProjectComplexityProcessor' + # - 'ProjectCognitiveComplexityProcessor' + # - 'ProjectLLOCProcessor' + # - 'ProjectCLOCProcessor' + # - 'ProjectLOCProcessor' + # - 'ProjectSLOCProcessor' + # - 'LicenseHeaderLoaderExtension' + +console-reports: + active: true + exclude: + - 'ProjectStatisticsReport' + - 'ComplexityReport' + - 'NotificationReport' + - 'FindingsReport' + - 'FileBasedFindingsReport' + # - 'LiteFindingsReport' + +output-reports: + active: true + exclude: + # - 'TxtOutputReport' + # - 'XmlOutputReport' + # - 'HtmlOutputReport' + # - 'MdOutputReport' + comments: + active: true + AbsentOrWrongFileLicense: + active: false + licenseTemplateFile: 'license.template' + licenseTemplateIsRegex: false + CommentOverPrivateFunction: + active: false CommentOverPrivateProperty: - active: true + active: false + DeprecatedBlockTag: + active: false + EndOfSentenceFormat: + active: false + endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)' + KDocReferencesNonPublicProperty: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + OutdatedDocumentation: + active: false + matchTypeParameters: true + matchDeclarationsOrder: true + allowParamOnConstructorProperties: false UndocumentedPublicClass: - active: true - excludes: ['**/*.kt'] - includes: ['**/detekt-api/src/main/**/api/*.kt'] + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + searchInNestedClass: true + searchInInnerClass: true + searchInInnerObject: true + searchInInnerInterface: true UndocumentedPublicFunction: - active: true - excludes: ['**/*.kt'] - includes: ['**/detekt-api/src/main/**/api/*.kt'] + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + UndocumentedPublicProperty: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] complexity: - StringLiteralDuplication: + active: true + ComplexCondition: active: true - excludes: ['**/test/**', '**/*Test.kt', '**/*Spec.kt'] - threshold: 5 - ignoreAnnotation: true - excludeStringsWithLessThan5Characters: true - ignoreStringsRegex: '$^' + threshold: 4 ComplexInterface: - active: true + active: false threshold: 10 includeStaticDeclarations: false includePrivateDeclarations: false ComplexMethod: active: true - ignoreSingleWhenExpression: true + threshold: 15 + ignoreSingleWhenExpression: false + ignoreSimpleWhenEntries: false + ignoreNestingFunctions: false + nestingFunctions: + - 'also' + - 'apply' + - 'forEach' + - 'isNotNull' + - 'ifNull' + - 'let' + - 'run' + - 'use' + - 'with' + LabeledExpression: + active: false + ignoredLabels: [] LargeClass: active: true - excludes: ['**/test/**', '**/*.Test.kt', '**/*.Spec.kt'] + threshold: 600 + LongMethod: + active: true + threshold: 60 + LongParameterList: + active: true + functionThreshold: 6 + constructorThreshold: 7 + ignoreDefaultParameters: false + ignoreDataClasses: true + ignoreAnnotatedParameter: [] MethodOverloading: + active: false + threshold: 6 + NamedArguments: + active: false + threshold: 3 + ignoreArgumentsMatchingNames: false + NestedBlockDepth: active: true + threshold: 4 + NestedScopeFunctions: + active: false + threshold: 1 + functions: + - 'kotlin.apply' + - 'kotlin.run' + - 'kotlin.with' + - 'kotlin.let' + - 'kotlin.also' + ReplaceSafeCallChainWithRun: + active: false + StringLiteralDuplication: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + threshold: 3 + ignoreAnnotation: true + excludeStringsWithLessThan5Characters: true + ignoreStringsRegex: '$^' TooManyFunctions: - excludes: ['**/test/**', '**/functionalTest/**'] + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + thresholdInFiles: 11 + thresholdInClasses: 11 + thresholdInInterfaces: 11 + thresholdInObjects: 11 + thresholdInEnums: 11 + ignoreDeprecated: false + ignorePrivate: false + ignoreOverridden: false coroutines: active: true GlobalCoroutineUsage: + active: false + InjectDispatcher: active: true + dispatcherNames: + - 'IO' + - 'Default' + - 'Unconfined' RedundantSuspendModifier: active: true SleepInsteadOfDelay: active: true + SuspendFunWithCoroutineScopeReceiver: + active: false SuspendFunWithFlowReturnType: active: true -exceptions: - InstanceOfCheckForException: +empty-blocks: + active: true + EmptyCatchBlock: active: true - NotImplementedDeclaration: + allowedExceptionNameRegex: '_|(ignore|expected).*' + EmptyClassBlock: active: true - ObjectExtendsThrowable: + EmptyDefaultConstructor: active: true - RethrowCaughtException: + EmptyDoWhileBlock: active: true - ReturnFromFinally: + EmptyElseBlock: active: true - ThrowingExceptionFromFinally: + EmptyFinallyBlock: active: true - ThrowingExceptionInMain: + EmptyForBlock: active: true - ThrowingExceptionsWithoutMessageOrCause: + EmptyFunctionBlock: active: true - ThrowingNewInstanceOfSameException: + ignoreOverridden: false + EmptyIfBlock: + active: true + EmptyInitBlock: + active: true + EmptyKtFile: + active: true + EmptySecondaryConstructor: + active: true + EmptyTryBlock: + active: true + EmptyWhenBlock: + active: true + EmptyWhileBlock: active: true -formatting: +exceptions: active: true - android: false - autoCorrect: true - AnnotationSpacing: + ExceptionRaisedInUnexpectedLocation: active: true - EnumEntryNameCase: + methodNames: + - 'equals' + - 'finalize' + - 'hashCode' + - 'toString' + InstanceOfCheckForException: active: true - MaximumLineLength: + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + NotImplementedDeclaration: active: false - MultiLineIfElse: - active: true - NoEmptyFirstLineInMethodBlock: + ObjectExtendsThrowable: + active: false + PrintStackTrace: active: true - PackageName: + RethrowCaughtException: active: true - SpacingAroundAngleBrackets: + ReturnFromFinally: active: true - SpacingAroundDoubleColon: + ignoreLabeled: false + SwallowedException: active: true - SpacingAroundUnaryOperator: + ignoredExceptionTypes: + - 'InterruptedException' + - 'MalformedURLException' + - 'NumberFormatException' + - 'ParseException' + allowedExceptionNameRegex: '_|(ignore|expected).*' + ThrowingExceptionFromFinally: active: true - SpacingBetweenDeclarationsWithAnnotations: + ThrowingExceptionInMain: + active: false + ThrowingExceptionsWithoutMessageOrCause: active: true - SpacingBetweenDeclarationsWithComments: + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + exceptions: + - 'ArrayIndexOutOfBoundsException' + - 'Exception' + - 'IllegalArgumentException' + - 'IllegalMonitorStateException' + - 'IllegalStateException' + - 'IndexOutOfBoundsException' + - 'NullPointerException' + - 'RuntimeException' + - 'Throwable' + ThrowingNewInstanceOfSameException: active: true + TooGenericExceptionCaught: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + exceptionNames: + - 'ArrayIndexOutOfBoundsException' + - 'Error' + - 'Exception' + - 'IllegalMonitorStateException' + - 'IndexOutOfBoundsException' + - 'NullPointerException' + - 'RuntimeException' + - 'Throwable' + allowedExceptionNameRegex: '_|(ignore|expected).*' + TooGenericExceptionThrown: + active: true + exceptionNames: + - 'Error' + - 'Exception' + - 'RuntimeException' + - 'Throwable' naming: + active: true + BooleanPropertyNaming: + active: false + allowedPattern: '^(is|has|are)' + ignoreOverridden: true ClassNaming: - excludes: ['**/*Spec.kt'] + active: true + classPattern: '[A-Z][a-zA-Z0-9]*' + ConstructorParameterNaming: + active: true + parameterPattern: '[a-z][A-Za-z0-9]*' + privateParameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + ignoreOverridden: true + EnumNaming: + active: true + enumEntryPattern: '[A-Z][_a-zA-Z0-9]*' + ForbiddenClassName: + active: false + forbiddenName: [] + FunctionMaxLength: + active: false + maximumFunctionNameLength: 30 + FunctionMinLength: + active: false + minimumFunctionNameLength: 3 FunctionNaming: active: true - excludes: - - '**/test/**' - - '**/androidTest/**' - - '**/commonTest/**' - - '**/functionalTest/**' - - '**/jvmTest/**' - - '**/jsTest/**' - - '**/iosTest/**' - TopLevelPropertyNaming: - constantPattern: '[a-z][_A-Za-z0-9]*|[A-Z][_A-Z0-9]*' + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + functionPattern: '[a-z][a-zA-Z0-9]*' + excludeClassPattern: '$^' + ignoreOverridden: true + FunctionParameterNaming: + active: true + parameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + ignoreOverridden: true InvalidPackageDeclaration: active: true - excludes: ['**/build-logic/**/*.kt', '**/*.kts'] + rootPackage: '' + requireRootInDeclaration: false + LambdaParameterNaming: + active: false + parameterPattern: '[a-z][A-Za-z0-9]*|_' + MatchingDeclarationName: + active: true + mustBeFirst: true + MemberNameEqualsClassName: + active: true + ignoreOverridden: true NoNameShadowing: active: true NonBooleanPropertyPrefixedWithIs: + active: false + ObjectPropertyNaming: active: true - VariableMaxLength: + constantPattern: '[A-Za-z][_A-Za-z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*' + PackageNaming: active: true + packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*' + TopLevelPropertyNaming: + active: true + constantPattern: '[A-Z][_A-Z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*' + VariableMaxLength: + active: false + maximumVariableNameLength: 64 VariableMinLength: + active: false + minimumVariableNameLength: 1 + VariableNaming: active: true + variablePattern: '[a-z][A-Za-z0-9]*' + privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + ignoreOverridden: true performance: + active: true + ArrayPrimitive: + active: true + CouldBeSequence: + active: false + threshold: 3 + ForEachOnRange: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] SpreadOperator: - excludes: ['**/test/**', '**/functionalTest/**'] + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + UnnecessaryTemporaryInstantiation: + active: true potential-bugs: + active: true AvoidReferentialEquality: active: true + forbiddenTypePatterns: + - 'kotlin.String' + CastToNullableType: + active: false + Deprecation: + active: false DontDowncastCollectionTypes: - active: true - DoubleMutabilityForCollection: active: false + DoubleMutabilityForCollection: + active: true + mutableTypes: + - 'kotlin.collections.MutableList' + - 'kotlin.collections.MutableMap' + - 'kotlin.collections.MutableSet' + - 'java.util.ArrayList' + - 'java.util.LinkedHashSet' + - 'java.util.HashSet' + - 'java.util.LinkedHashMap' + - 'java.util.HashMap' + DuplicateCaseInWhenExpression: + active: true ElseCaseInsteadOfExhaustiveWhen: + active: false + EqualsAlwaysReturnsTrueOrFalse: + active: true + EqualsWithHashCodeExist: active: true ExitOutsideMain: active: false + ExplicitGarbageCollectionCall: + active: true HasPlatformType: active: true IgnoredReturnValue: active: true + restrictToAnnotatedMethods: true + returnValueAnnotations: + - '*.CheckResult' + - '*.CheckReturnValue' + ignoreReturnValueAnnotations: + - '*.CanIgnoreReturnValue' + ignoreFunctionCall: [] + ImplicitDefaultLocale: + active: true ImplicitUnitReturnType: + active: false + allowExplicitReturnType: true + InvalidRange: + active: true + IteratorHasNextCallsNextMethod: + active: true + IteratorNotThrowingNoSuchElementException: active: true + LateinitUsage: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + ignoreOnClassesPattern: '' MapGetWithNotNullAssertionOperator: active: true + MissingPackageDeclaration: + active: false + excludes: ['**/*.kts'] + MissingWhenCase: + active: true + allowElseExpression: true + NullCheckOnMutableProperty: + active: false + NullableToStringCall: + active: false + RedundantElseInWhen: + active: true UnconditionalJumpStatementInLoop: + active: false + UnnecessaryNotNullOperator: + active: true + UnnecessarySafeCall: active: true UnreachableCatchBlock: active: true + UnreachableCode: + active: true + UnsafeCallOnNullableType: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] UnsafeCast: active: true - excludes: ['**/test/**', '**/*.Test.kt', '**/*.Spec.kt'] + UnusedUnaryOperator: + active: true UselessPostfixExpression: active: true + WrongEqualsTypeParameter: + active: true style: + active: true CanBeNonNullable: - active: true + active: false CascadingCallWrapping: - active: true + active: false + includeElvis: true ClassOrdering: - active: true + active: false CollapsibleIfStatements: - active: true + active: false + DataClassContainsFunctions: + active: false + conversionFunctionPrefix: 'to' + DataClassShouldBeImmutable: + active: false DestructuringDeclarationWithTooManyEntries: active: true - EqualsOnSignatureLine: + maxDestructuringEntries: 3 + EqualsNullCall: active: true + EqualsOnSignatureLine: + active: false ExplicitCollectionElementAccessMethod: - active: true + active: false ExplicitItLambdaParameter: active: true + ExpressionBodySyntax: + active: false + includeLineWrapping: false ForbiddenComment: active: true values: - - 'TODO:' - 'FIXME:' - 'STOPSHIP:' - - '@author' - - '@requiresTypeResolution' - excludes: ['**/detekt-rules-style/**/ForbiddenComment.kt'] + - 'TODO:' + allowedPatterns: '' + customMessage: '' ForbiddenImport: - active: true - imports: - - 'org.assertj.core.api.Assertions' - - 'org.junit.jupiter.api.Assertions*' + active: false + imports: [] + forbiddenPatterns: '' ForbiddenMethodCall: - active: true + active: false methods: - 'kotlin.io.print' - 'kotlin.io.println' - - 'java.net.URL.openStream' - - 'java.lang.Class.getResourceAsStream' - - 'java.lang.ClassLoader.getResourceAsStream' + ForbiddenPublicDataClass: + active: true + excludes: ['**'] + ignorePackages: + - '*.internal' + - '*.internal.*' + ForbiddenSuppress: + active: false + rules: [] ForbiddenVoid: active: true + ignoreOverridden: false + ignoreUsageInGenerics: false + FunctionOnlyReturningConstant: + active: true + ignoreOverridableFunction: true + ignoreActualFunction: true + excludedFunctions: '' LibraryCodeMustSpecifyReturnType: active: true - excludes: ['**/*.kt'] - includes: ['**/detekt-api/src/main/**/api/*.kt'] + excludes: ['**'] + LibraryEntitiesShouldNotBePublic: + active: true + excludes: ['**'] + LoopWithTooManyJumpStatements: + active: true + maxJumpCount: 1 MagicNumber: - excludes: ['**/test/**', '**/*Test.kt', '**/*Spec.kt'] - ignorePropertyDeclaration: true - ignoreAnnotation: true - ignoreEnums: true + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**', '**/*.kts'] ignoreNumbers: - '-1' - '0' - '1' - '2' - - '100' - - '1000' + ignoreHashCodeFunction: true + ignorePropertyDeclaration: false + ignoreLocalVariableDeclaration: false + ignoreConstantDeclaration: true + ignoreCompanionObjectPropertyDeclaration: true + ignoreAnnotation: false + ignoreNamedArgument: true + ignoreEnums: false + ignoreRanges: false + ignoreExtensionFunctions: true + MandatoryBracesIfStatements: + active: false MandatoryBracesLoops: - active: true + active: false + MaxChainedCallsOnSameLine: + active: false + maxChainedCalls: 5 MaxLineLength: active: true - excludes: ['**/test/**', '**/*Test.kt', '**/*Spec.kt'] - excludeCommentStatements: true + maxLineLength: 120 + excludePackageStatements: true + excludeImportStatements: true + excludeCommentStatements: false + MayBeConst: + active: true + ModifierOrder: + active: true + MultilineLambdaItParameter: + active: false NestedClassesVisibility: active: true + NewLineAtEndOfFile: + active: true + NoTabs: + active: false + NullableBooleanCheck: + active: false ObjectLiteralToLambda: active: true + OptionalAbstractKeyword: + active: true + OptionalUnit: + active: false + OptionalWhenBraces: + active: false PreferToOverPairSyntax: + active: false + ProtectedMemberInFinalClass: active: true RedundantExplicitType: - active: true + active: false RedundantHigherOrderMapUsage: active: true RedundantVisibilityModifierRule: - active: true + active: false ReturnCount: active: true - excludeGuardClauses: true + max: 2 + excludedFunctions: 'equals' + excludeLabeled: false + excludeReturnFromLambda: true + excludeGuardClauses: false + SafeCast: + active: true + SerialVersionUIDInSerializableClass: + active: true SpacingBetweenPackageAndImports: + active: false + ThrowsCount: active: true + max: 2 + excludeGuardClauses: false + TrailingWhitespace: + active: false UnderscoresInNumericLiterals: + active: false + acceptableLength: 4 + allowNonStandardGrouping: false + UnnecessaryAbstractClass: active: true UnnecessaryAnnotationUseSiteTarget: + active: false + UnnecessaryApply: active: true + UnnecessaryBackticks: + active: false UnnecessaryFilter: active: true - UnnecessaryLet: + UnnecessaryInheritance: active: true UnnecessaryInnerClass: - active: true - ignoreAnnotated: ['Nested'] + active: false + UnnecessaryLet: + active: false + UnnecessaryParentheses: + active: false UntilInsteadOfRangeTo: - active: true + active: false UnusedImports: - active: false # formatting already have this rule enabled + active: false + UnusedPrivateClass: + active: true UnusedPrivateMember: active: true - allowedNames: '(_|ignored|expected)' + allowedNames: '(_|ignored|expected|serialVersionUID)' UseAnyOrNoneInsteadOfFind: active: true + UseArrayLiteralsInAnnotations: + active: true + UseCheckNotNull: + active: true UseCheckOrError: active: true + UseDataClass: + active: false + allowVars: false UseEmptyCounterpart: - active: true + active: false UseIfEmptyOrIfBlank: - active: true + active: false + UseIfInsteadOfWhen: + active: false UseIsNullOrEmpty: active: true UseOrEmpty: @@ -266,6 +698,186 @@ style: active: true UseRequireNotNull: active: true + UselessCallOnNotNull: + active: true + UtilityClassWithPublicConstructor: + active: true VarCouldBeVal: active: true - ignoreAnnotated: ['Parameter'] + ignoreLateinitVar: false + WildcardImport: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + excludeImports: + - 'java.util.*' + +formatting: + active: true + android: false + autoCorrect: true + AnnotationOnSeparateLine: + active: false + autoCorrect: true + AnnotationSpacing: + active: false + autoCorrect: true + ArgumentListWrapping: + active: false + autoCorrect: true + indentSize: 4 + maxLineLength: 120 + BlockCommentInitialStarAlignment: + active: false + autoCorrect: true + ChainWrapping: + active: true + autoCorrect: true + CommentSpacing: + active: true + autoCorrect: true + CommentWrapping: + active: false + autoCorrect: true + indentSize: 4 + DiscouragedCommentLocation: + active: false + autoCorrect: true + EnumEntryNameCase: + active: false + autoCorrect: true + Filename: + active: true + FinalNewline: + active: true + autoCorrect: true + insertFinalNewLine: true + FunKeywordSpacing: + active: false + autoCorrect: true + FunctionTypeReferenceSpacing: + active: false + autoCorrect: true + ImportOrdering: + active: true + autoCorrect: true + layout: '*,java.**,javax.**,kotlin.**,^' + Indentation: + active: true + autoCorrect: true + indentSize: 4 + KdocWrapping: + active: false + autoCorrect: true + indentSize: 4 + MaximumLineLength: + active: true + maxLineLength: 120 + ignoreBackTickedIdentifier: false + ModifierListSpacing: + active: false + autoCorrect: true + ModifierOrdering: + active: true + autoCorrect: true + MultiLineIfElse: + active: false + autoCorrect: true + NoBlankLineBeforeRbrace: + active: true + autoCorrect: true + NoConsecutiveBlankLines: + active: true + autoCorrect: true + NoEmptyClassBody: + active: true + autoCorrect: true + NoEmptyFirstLineInMethodBlock: + active: false + autoCorrect: true + NoLineBreakAfterElse: + active: true + autoCorrect: true + NoLineBreakBeforeAssignment: + active: true + autoCorrect: true + NoMultipleSpaces: + active: true + autoCorrect: true + NoSemicolons: + active: true + autoCorrect: true + NoTrailingSpaces: + active: true + autoCorrect: true + NoUnitReturn: + active: true + autoCorrect: true + NoUnusedImports: + active: true + autoCorrect: true + NoWildcardImports: + active: true + packagesToUseImportOnDemandProperty: 'java.util.*,kotlinx.android.synthetic.**' + PackageName: + active: false + autoCorrect: true + ParameterListWrapping: + active: true + autoCorrect: true + maxLineLength: 120 + SpacingAroundAngleBrackets: + active: false + autoCorrect: true + SpacingAroundColon: + active: true + autoCorrect: true + SpacingAroundComma: + active: true + autoCorrect: true + SpacingAroundCurly: + active: true + autoCorrect: true + SpacingAroundDot: + active: true + autoCorrect: true + SpacingAroundDoubleColon: + active: false + autoCorrect: true + SpacingAroundKeyword: + active: true + autoCorrect: true + SpacingAroundOperators: + active: true + autoCorrect: true + SpacingAroundParens: + active: true + autoCorrect: true + SpacingAroundRangeOperator: + active: true + autoCorrect: true + SpacingAroundUnaryOperator: + active: false + autoCorrect: true + SpacingBetweenDeclarationsWithAnnotations: + active: false + autoCorrect: true + SpacingBetweenDeclarationsWithComments: + active: false + autoCorrect: true + StringTemplate: + active: true + autoCorrect: true + TrailingComma: + active: false + autoCorrect: true + allowTrailingComma: false + allowTrailingCommaOnCallSite: false + TypeArgumentListSpacing: + active: false + autoCorrect: true + UnnecessaryParenthesesBeforeTrailingLambda: + active: false + autoCorrect: true + Wrapping: + active: true + autoCorrect: true diff --git a/detekt-api/build.gradle.kts b/detekt-api/build.gradle.kts index 1005ae138f74..4452ad608b7e 100644 --- a/detekt-api/build.gradle.kts +++ b/detekt-api/build.gradle.kts @@ -35,6 +35,10 @@ tasks.dokkaHtml { notCompatibleWithConfigurationCache("https://github.com/Kotlin/dokka/issues/1217") } +tasks.apiDump { + notCompatibleWithConfigurationCache("https://github.com/Kotlin/binary-compatibility-validator/issues/95") +} + apiValidation { ignoredPackages.add("io.gitlab.arturbosch.detekt.api.internal") } diff --git a/detekt-api/detekt.yml b/detekt-api/detekt.yml new file mode 100644 index 000000000000..75726c62c216 --- /dev/null +++ b/detekt-api/detekt.yml @@ -0,0 +1,896 @@ +build: + maxIssues: 0 + excludeCorrectable: false + weights: + # complexity: 2 + # LongParameterList: 1 + # style: 1 + # comments: 1 + +config: + validation: true + warningsAsErrors: false + # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]' + excludes: '' + +processors: + active: true + exclude: + - 'DetektProgressListener' + # - 'KtFileCountProcessor' + # - 'PackageCountProcessor' + # - 'ClassCountProcessor' + # - 'FunctionCountProcessor' + # - 'PropertyCountProcessor' + # - 'ProjectComplexityProcessor' + # - 'ProjectCognitiveComplexityProcessor' + # - 'ProjectLLOCProcessor' + # - 'ProjectCLOCProcessor' + # - 'ProjectLOCProcessor' + # - 'ProjectSLOCProcessor' + # - 'LicenseHeaderLoaderExtension' + +console-reports: + active: true + exclude: + - 'ProjectStatisticsReport' + - 'ComplexityReport' + - 'NotificationReport' + - 'FindingsReport' + - 'FileBasedFindingsReport' + # - 'LiteFindingsReport' + +output-reports: + active: true + exclude: + # - 'TxtOutputReport' + # - 'XmlOutputReport' + # - 'HtmlOutputReport' + # - 'MdOutputReport' + +comments: + active: true + AbsentOrWrongFileLicense: + active: false + licenseTemplateFile: 'license.template' + licenseTemplateIsRegex: false + CommentOverPrivateFunction: + active: false + CommentOverPrivateProperty: + active: false + DeprecatedBlockTag: + active: false + EndOfSentenceFormat: + active: false + endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)' + KDocReferencesNonPublicProperty: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + OutdatedDocumentation: + active: false + matchTypeParameters: true + matchDeclarationsOrder: true + allowParamOnConstructorProperties: false + UndocumentedPublicClass: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + searchInNestedClass: true + searchInInnerClass: true + searchInInnerObject: true + searchInInnerInterface: true + searchInProtectedClass: false + UndocumentedPublicFunction: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + searchProtectedFunction: false + UndocumentedPublicProperty: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + searchProtectedProperty: false + +complexity: + active: true + ComplexCondition: + active: true + threshold: 4 + ComplexInterface: + active: false + threshold: 10 + includeStaticDeclarations: false + includePrivateDeclarations: false + ComplexMethod: + active: true + threshold: 15 + ignoreSingleWhenExpression: false + ignoreSimpleWhenEntries: false + ignoreNestingFunctions: false + nestingFunctions: + - 'also' + - 'apply' + - 'forEach' + - 'isNotNull' + - 'ifNull' + - 'let' + - 'run' + - 'use' + - 'with' + LabeledExpression: + active: false + ignoredLabels: [] + LargeClass: + active: true + threshold: 600 + LongMethod: + active: true + threshold: 60 + LongParameterList: + active: true + functionThreshold: 6 + constructorThreshold: 7 + ignoreDefaultParameters: false + ignoreDataClasses: true + ignoreAnnotatedParameter: [] + MethodOverloading: + active: false + threshold: 6 + NamedArguments: + active: false + threshold: 3 + ignoreArgumentsMatchingNames: false + NestedBlockDepth: + active: true + threshold: 4 + NestedScopeFunctions: + active: false + threshold: 1 + functions: + - 'kotlin.apply' + - 'kotlin.run' + - 'kotlin.with' + - 'kotlin.let' + - 'kotlin.also' + ReplaceSafeCallChainWithRun: + active: false + StringLiteralDuplication: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + threshold: 3 + ignoreAnnotation: true + excludeStringsWithLessThan5Characters: true + ignoreStringsRegex: '$^' + TooManyFunctions: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + thresholdInFiles: 11 + thresholdInClasses: 11 + thresholdInInterfaces: 11 + thresholdInObjects: 11 + thresholdInEnums: 11 + ignoreDeprecated: false + ignorePrivate: false + ignoreOverridden: false + +coroutines: + active: true + GlobalCoroutineUsage: + active: false + InjectDispatcher: + active: true + dispatcherNames: + - 'IO' + - 'Default' + - 'Unconfined' + RedundantSuspendModifier: + active: true + SleepInsteadOfDelay: + active: true + SuspendFunWithCoroutineScopeReceiver: + active: false + SuspendFunWithFlowReturnType: + active: true + +empty-blocks: + active: true + EmptyCatchBlock: + active: true + allowedExceptionNameRegex: '_|(ignore|expected).*' + EmptyClassBlock: + active: true + EmptyDefaultConstructor: + active: true + EmptyDoWhileBlock: + active: true + EmptyElseBlock: + active: true + EmptyFinallyBlock: + active: true + EmptyForBlock: + active: true + EmptyFunctionBlock: + active: true + ignoreOverridden: false + EmptyIfBlock: + active: true + EmptyInitBlock: + active: true + EmptyKtFile: + active: true + EmptySecondaryConstructor: + active: true + EmptyTryBlock: + active: true + EmptyWhenBlock: + active: true + EmptyWhileBlock: + active: true + +exceptions: + active: true + ExceptionRaisedInUnexpectedLocation: + active: true + methodNames: + - 'equals' + - 'finalize' + - 'hashCode' + - 'toString' + InstanceOfCheckForException: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + NotImplementedDeclaration: + active: false + ObjectExtendsThrowable: + active: false + PrintStackTrace: + active: true + RethrowCaughtException: + active: true + ReturnFromFinally: + active: true + ignoreLabeled: false + SwallowedException: + active: true + ignoredExceptionTypes: + - 'InterruptedException' + - 'MalformedURLException' + - 'NumberFormatException' + - 'ParseException' + allowedExceptionNameRegex: '_|(ignore|expected).*' + ThrowingExceptionFromFinally: + active: true + ThrowingExceptionInMain: + active: false + ThrowingExceptionsWithoutMessageOrCause: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + exceptions: + - 'ArrayIndexOutOfBoundsException' + - 'Exception' + - 'IllegalArgumentException' + - 'IllegalMonitorStateException' + - 'IllegalStateException' + - 'IndexOutOfBoundsException' + - 'NullPointerException' + - 'RuntimeException' + - 'Throwable' + ThrowingNewInstanceOfSameException: + active: true + TooGenericExceptionCaught: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + exceptionNames: + - 'ArrayIndexOutOfBoundsException' + - 'Error' + - 'Exception' + - 'IllegalMonitorStateException' + - 'IndexOutOfBoundsException' + - 'NullPointerException' + - 'RuntimeException' + - 'Throwable' + allowedExceptionNameRegex: '_|(ignore|expected).*' + TooGenericExceptionThrown: + active: true + exceptionNames: + - 'Error' + - 'Exception' + - 'RuntimeException' + - 'Throwable' + +naming: + active: true + BooleanPropertyNaming: + active: false + allowedPattern: '^(is|has|are)' + ignoreOverridden: true + ClassNaming: + active: true + classPattern: '[A-Z][a-zA-Z0-9]*' + ConstructorParameterNaming: + active: true + parameterPattern: '[a-z][A-Za-z0-9]*' + privateParameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + ignoreOverridden: true + EnumNaming: + active: true + enumEntryPattern: '[A-Z][_a-zA-Z0-9]*' + ForbiddenClassName: + active: false + forbiddenName: [] + FunctionMaxLength: + active: false + maximumFunctionNameLength: 30 + FunctionMinLength: + active: false + minimumFunctionNameLength: 3 + FunctionNaming: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + functionPattern: '[a-z][a-zA-Z0-9]*' + excludeClassPattern: '$^' + ignoreOverridden: true + FunctionParameterNaming: + active: true + parameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + ignoreOverridden: true + InvalidPackageDeclaration: + active: true + rootPackage: '' + requireRootInDeclaration: false + LambdaParameterNaming: + active: false + parameterPattern: '[a-z][A-Za-z0-9]*|_' + MatchingDeclarationName: + active: true + mustBeFirst: true + MemberNameEqualsClassName: + active: true + ignoreOverridden: true + NoNameShadowing: + active: true + NonBooleanPropertyPrefixedWithIs: + active: false + ObjectPropertyNaming: + active: true + constantPattern: '[A-Za-z][_A-Za-z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*' + PackageNaming: + active: true + packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*' + TopLevelPropertyNaming: + active: true + constantPattern: '[A-Z][_A-Z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*' + VariableMaxLength: + active: false + maximumVariableNameLength: 64 + VariableMinLength: + active: false + minimumVariableNameLength: 1 + VariableNaming: + active: true + variablePattern: '[a-z][A-Za-z0-9]*' + privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + ignoreOverridden: true + +performance: + active: true + ArrayPrimitive: + active: true + CouldBeSequence: + active: false + threshold: 3 + ForEachOnRange: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + SpreadOperator: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + UnnecessaryTemporaryInstantiation: + active: true + +potential-bugs: + active: true + AvoidReferentialEquality: + active: true + forbiddenTypePatterns: + - 'kotlin.String' + CastToNullableType: + active: false + Deprecation: + active: false + DontDowncastCollectionTypes: + active: false + DoubleMutabilityForCollection: + active: true + mutableTypes: + - 'kotlin.collections.MutableList' + - 'kotlin.collections.MutableMap' + - 'kotlin.collections.MutableSet' + - 'java.util.ArrayList' + - 'java.util.LinkedHashSet' + - 'java.util.HashSet' + - 'java.util.LinkedHashMap' + - 'java.util.HashMap' + DuplicateCaseInWhenExpression: + active: true + ElseCaseInsteadOfExhaustiveWhen: + active: false + EqualsAlwaysReturnsTrueOrFalse: + active: true + EqualsWithHashCodeExist: + active: true + ExitOutsideMain: + active: false + ExplicitGarbageCollectionCall: + active: true + HasPlatformType: + active: true + IgnoredReturnValue: + active: true + restrictToAnnotatedMethods: true + returnValueAnnotations: + - '*.CheckResult' + - '*.CheckReturnValue' + ignoreReturnValueAnnotations: + - '*.CanIgnoreReturnValue' + ignoreFunctionCall: [] + ImplicitDefaultLocale: + active: true + ImplicitUnitReturnType: + active: false + allowExplicitReturnType: true + InvalidRange: + active: true + IteratorHasNextCallsNextMethod: + active: true + IteratorNotThrowingNoSuchElementException: + active: true + LateinitUsage: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + ignoreOnClassesPattern: '' + MapGetWithNotNullAssertionOperator: + active: true + MissingPackageDeclaration: + active: false + excludes: ['**/*.kts'] + MissingWhenCase: + active: true + allowElseExpression: true + NullCheckOnMutableProperty: + active: false + NullableToStringCall: + active: false + RedundantElseInWhen: + active: true + UnconditionalJumpStatementInLoop: + active: false + UnnecessaryNotNullOperator: + active: true + UnnecessarySafeCall: + active: true + UnreachableCatchBlock: + active: true + UnreachableCode: + active: true + UnsafeCallOnNullableType: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + UnsafeCast: + active: true + UnusedUnaryOperator: + active: true + UselessPostfixExpression: + active: true + WrongEqualsTypeParameter: + active: true + +style: + active: true + CanBeNonNullable: + active: false + CascadingCallWrapping: + active: false + includeElvis: true + ClassOrdering: + active: false + CollapsibleIfStatements: + active: false + DataClassContainsFunctions: + active: false + conversionFunctionPrefix: 'to' + DataClassShouldBeImmutable: + active: false + DestructuringDeclarationWithTooManyEntries: + active: true + maxDestructuringEntries: 3 + EqualsNullCall: + active: true + EqualsOnSignatureLine: + active: false + ExplicitCollectionElementAccessMethod: + active: false + ExplicitItLambdaParameter: + active: true + ExpressionBodySyntax: + active: false + includeLineWrapping: false + ForbiddenComment: + active: true + values: + - 'FIXME:' + - 'STOPSHIP:' + - 'TODO:' + allowedPatterns: '' + customMessage: '' + ForbiddenImport: + active: false + imports: [] + forbiddenPatterns: '' + ForbiddenMethodCall: + active: false + methods: + - reason: 'print does not allow you to configure the output stream. Use a logger instead.' + value: 'kotlin.io.print' + - reason: 'println does not allow you to configure the output stream. Use a logger instead.' + value: 'kotlin.io.println' + ForbiddenPublicDataClass: + active: true + excludes: ['**'] + ignorePackages: + - '*.internal' + - '*.internal.*' + ForbiddenSuppress: + active: false + rules: [] + ForbiddenVoid: + active: true + ignoreOverridden: false + ignoreUsageInGenerics: false + FunctionOnlyReturningConstant: + active: true + ignoreOverridableFunction: true + ignoreActualFunction: true + excludedFunctions: '' + LibraryCodeMustSpecifyReturnType: + active: true + excludes: ['**'] + LibraryEntitiesShouldNotBePublic: + active: true + excludes: ['**'] + LoopWithTooManyJumpStatements: + active: true + maxJumpCount: 1 + MagicNumber: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**', '**/*.kts'] + ignoreNumbers: + - '-1' + - '0' + - '1' + - '2' + ignoreHashCodeFunction: true + ignorePropertyDeclaration: false + ignoreLocalVariableDeclaration: false + ignoreConstantDeclaration: true + ignoreCompanionObjectPropertyDeclaration: true + ignoreAnnotation: false + ignoreNamedArgument: true + ignoreEnums: false + ignoreRanges: false + ignoreExtensionFunctions: true + MandatoryBracesIfStatements: + active: false + MandatoryBracesLoops: + active: false + MaxChainedCallsOnSameLine: + active: false + maxChainedCalls: 5 + MaxLineLength: + active: true + maxLineLength: 120 + excludePackageStatements: true + excludeImportStatements: true + excludeCommentStatements: false + MayBeConst: + active: true + ModifierOrder: + active: true + MultilineLambdaItParameter: + active: false + NestedClassesVisibility: + active: true + NewLineAtEndOfFile: + active: true + NoTabs: + active: false + NullableBooleanCheck: + active: false + ObjectLiteralToLambda: + active: true + OptionalAbstractKeyword: + active: true + OptionalUnit: + active: false + OptionalWhenBraces: + active: false + PreferToOverPairSyntax: + active: false + ProtectedMemberInFinalClass: + active: true + RedundantExplicitType: + active: false + RedundantHigherOrderMapUsage: + active: true + RedundantVisibilityModifierRule: + active: false + ReturnCount: + active: true + max: 2 + excludedFunctions: + - 'equals' + excludeLabeled: false + excludeReturnFromLambda: true + excludeGuardClauses: false + SafeCast: + active: true + SerialVersionUIDInSerializableClass: + active: true + SpacingBetweenPackageAndImports: + active: false + ThrowsCount: + active: true + max: 2 + excludeGuardClauses: false + TrailingWhitespace: + active: false + TrimMultilineRawString: + active: false + UnderscoresInNumericLiterals: + active: false + acceptableLength: 4 + allowNonStandardGrouping: false + UnnecessaryAbstractClass: + active: true + UnnecessaryAnnotationUseSiteTarget: + active: false + UnnecessaryApply: + active: true + UnnecessaryBackticks: + active: false + UnnecessaryFilter: + active: true + UnnecessaryInheritance: + active: true + UnnecessaryInnerClass: + active: false + UnnecessaryLet: + active: false + UnnecessaryParentheses: + active: false + allowForUnclearPrecedence: false + UntilInsteadOfRangeTo: + active: false + UnusedImports: + active: false + UnusedPrivateClass: + active: true + UnusedPrivateMember: + active: true + allowedNames: '(_|ignored|expected|serialVersionUID)' + UseAnyOrNoneInsteadOfFind: + active: true + UseArrayLiteralsInAnnotations: + active: true + UseCheckNotNull: + active: true + UseCheckOrError: + active: true + UseDataClass: + active: false + allowVars: false + UseEmptyCounterpart: + active: false + UseIfEmptyOrIfBlank: + active: false + UseIfInsteadOfWhen: + active: false + UseIsNullOrEmpty: + active: true + UseOrEmpty: + active: true + UseRequire: + active: true + UseRequireNotNull: + active: true + UselessCallOnNotNull: + active: true + UtilityClassWithPublicConstructor: + active: true + VarCouldBeVal: + active: true + ignoreLateinitVar: false + WildcardImport: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + excludeImports: + - 'java.util.*' + +formatting: + active: true + android: false + autoCorrect: true + AnnotationOnSeparateLine: + active: true + autoCorrect: true + AnnotationSpacing: + active: true + autoCorrect: true + ArgumentListWrapping: + active: true + autoCorrect: true + indentSize: 4 + maxLineLength: 120 + BlockCommentInitialStarAlignment: + active: false + autoCorrect: true + ChainWrapping: + active: true + autoCorrect: true + CommentSpacing: + active: true + autoCorrect: true + CommentWrapping: + active: false + autoCorrect: true + indentSize: 4 + DiscouragedCommentLocation: + active: false + autoCorrect: true + EnumEntryNameCase: + active: true + autoCorrect: true + Filename: + active: true + FinalNewline: + active: true + autoCorrect: true + insertFinalNewLine: true + FunKeywordSpacing: + active: false + autoCorrect: true + FunctionTypeReferenceSpacing: + active: false + autoCorrect: true + ImportOrdering: + active: true + autoCorrect: true + layout: '*,java.**,javax.**,kotlin.**,^' + Indentation: + active: true + autoCorrect: true + indentSize: 4 + KdocWrapping: + active: false + autoCorrect: true + indentSize: 4 + MaximumLineLength: + active: true + maxLineLength: 120 + ignoreBackTickedIdentifier: false + ModifierListSpacing: + active: false + autoCorrect: true + ModifierOrdering: + active: true + autoCorrect: true + MultiLineIfElse: + active: true + autoCorrect: true + NoBlankLineBeforeRbrace: + active: true + autoCorrect: true + NoBlankLinesInChainedMethodCalls: + active: true + autoCorrect: true + NoConsecutiveBlankLines: + active: true + autoCorrect: true + NoEmptyClassBody: + active: true + autoCorrect: true + NoEmptyFirstLineInMethodBlock: + active: true + autoCorrect: true + NoLineBreakAfterElse: + active: true + autoCorrect: true + NoLineBreakBeforeAssignment: + active: true + autoCorrect: true + NoMultipleSpaces: + active: true + autoCorrect: true + NoSemicolons: + active: true + autoCorrect: true + NoTrailingSpaces: + active: true + autoCorrect: true + NoUnitReturn: + active: true + autoCorrect: true + NoUnusedImports: + active: true + autoCorrect: true + NoWildcardImports: + active: true + packagesToUseImportOnDemandProperty: 'java.util.*,kotlinx.android.synthetic.**' + PackageName: + active: true + autoCorrect: true + ParameterListWrapping: + active: true + autoCorrect: true + maxLineLength: 120 + SpacingAroundAngleBrackets: + active: true + autoCorrect: true + SpacingAroundColon: + active: true + autoCorrect: true + SpacingAroundComma: + active: true + autoCorrect: true + SpacingAroundCurly: + active: true + autoCorrect: true + SpacingAroundDot: + active: true + autoCorrect: true + SpacingAroundDoubleColon: + active: true + autoCorrect: true + SpacingAroundKeyword: + active: true + autoCorrect: true + SpacingAroundOperators: + active: true + autoCorrect: true + SpacingAroundParens: + active: true + autoCorrect: true + SpacingAroundRangeOperator: + active: true + autoCorrect: true + SpacingAroundUnaryOperator: + active: true + autoCorrect: true + SpacingBetweenDeclarationsWithAnnotations: + active: true + autoCorrect: true + SpacingBetweenDeclarationsWithComments: + active: true + autoCorrect: true + StringTemplate: + active: true + autoCorrect: true + TrailingComma: + active: false + autoCorrect: true + allowTrailingComma: false + allowTrailingCommaOnCallSite: false + TypeArgumentListSpacing: + active: false + autoCorrect: true + UnnecessaryParenthesesBeforeTrailingLambda: + active: false + autoCorrect: true + Wrapping: + active: true + autoCorrect: true + indentSize: 4 diff --git a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/Location.kt b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/Location.kt index a95648addb83..99bed074c653 100644 --- a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/Location.kt +++ b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/Location.kt @@ -1,10 +1,10 @@ package io.gitlab.arturbosch.detekt.api import io.github.detekt.psi.FilePath +import io.github.detekt.psi.getLineAndColumnInPsiFile import io.github.detekt.psi.toFilePath import org.jetbrains.kotlin.com.intellij.openapi.util.TextRange import org.jetbrains.kotlin.com.intellij.psi.PsiElement -import org.jetbrains.kotlin.diagnostics.DiagnosticUtils import org.jetbrains.kotlin.diagnostics.PsiDiagnosticUtils import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.psiUtil.endOffset @@ -14,7 +14,10 @@ import java.nio.file.Paths /** * Specifies a position within a source code fragment. */ -data class Location @Deprecated("Consider relative path by passing a [FilePath]") @JvmOverloads constructor( +data class Location +@Deprecated("Consider relative path by passing a [FilePath]") +@JvmOverloads +constructor( val source: SourceLocation, val text: TextLocation, @Deprecated( @@ -99,12 +102,8 @@ data class Location @Deprecated("Consider relative path by passing a [FilePath]" ) private fun lineAndColumn(element: PsiElement, range: TextRange): PsiDiagnosticUtils.LineAndColumn { - return try { - DiagnosticUtils.getLineAndColumnInPsiFile(element.containingFile, range) - } catch (@Suppress("SwallowedException", "TooGenericExceptionCaught") e: IndexOutOfBoundsException) { - // #3317 If any rule mutates the PsiElement, searching the original PsiElement may throw exception. - PsiDiagnosticUtils.LineAndColumn(-1, -1, null) - } + return getLineAndColumnInPsiFile(element.containingFile, range) + ?: PsiDiagnosticUtils.LineAndColumn(-1, -1, null) } } } diff --git a/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/AnnotationExcluderSpec.kt b/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/AnnotationExcluderSpec.kt index 6da78a438adb..c0abcf85551e 100644 --- a/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/AnnotationExcluderSpec.kt +++ b/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/AnnotationExcluderSpec.kt @@ -27,7 +27,9 @@ class AnnotationExcluderSpec(private val env: KotlinCoreEnvironment) { """.trimIndent() ) - @ParameterizedTest(name = "Given {0} is excluded when the {1} is found then the excluder returns {2} without type solving") + @ParameterizedTest( + name = "Given {0} is excluded when the {1} is found then the excluder returns {2} without type solving" + ) @CsvFileSource(resources = ["/annotation_excluder.csv"]) fun `all cases`(exclusion: String, annotation: String, shouldExclude: Boolean) { val (file, ktAnnotation) = createKtFile(annotation) @@ -36,7 +38,9 @@ class AnnotationExcluderSpec(private val env: KotlinCoreEnvironment) { assertThat(excluder.shouldExclude(listOf(ktAnnotation))).isEqualTo(shouldExclude) } - @ParameterizedTest(name = "Given {0} is excluded when the {1} is found then the excluder returns {2} with type solving") + @ParameterizedTest( + name = "Given {0} is excluded when the {1} is found then the excluder returns {2} with type solving" + ) @CsvFileSource(resources = ["/annotation_excluder.csv"]) fun `all cases - Type Solving`(exclusion: String, annotation: String, shouldExclude: Boolean) { val (file, ktAnnotation) = createKtFile(annotation) diff --git a/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/internal/PathMatchersSpec.kt b/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/internal/PathMatchersSpec.kt index 4ecd997ded56..173a02776d2a 100644 --- a/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/internal/PathMatchersSpec.kt +++ b/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/internal/PathMatchersSpec.kt @@ -41,7 +41,9 @@ class PathMatchersSpec { fun `should work as a regex path matcher when syntax not specified`() { assertThatThrownBy { pathMatcher("regex:.*/detekt/api/.*") } .isInstanceOf(IllegalArgumentException::class.java) - .hasMessage("Only globbing patterns are supported as they are treated os-independently by the PathMatcher api.") + .hasMessage( + "Only globbing patterns are supported as they are treated os-independently by the PathMatcher api." + ) } } } diff --git a/detekt-cli/src/main/kotlin/io/gitlab/arturbosch/detekt/cli/CliArgs.kt b/detekt-cli/src/main/kotlin/io/gitlab/arturbosch/detekt/cli/CliArgs.kt index b223993bf1c3..663cac6e0064 100644 --- a/detekt-cli/src/main/kotlin/io/gitlab/arturbosch/detekt/cli/CliArgs.kt +++ b/detekt-cli/src/main/kotlin/io/gitlab/arturbosch/detekt/cli/CliArgs.kt @@ -47,6 +47,13 @@ class CliArgs { ) var generateConfig: Boolean = false + @Parameter( + names = ["--generate-custom-config", "-gcc"], + description = "Generate config for user-defined rules. " + + "Path to user rules can be specified with --config option" + ) + var generateCustomConfig: Boolean = false + @Parameter( names = ["--plugins", "-p"], description = "Extra paths to plugin jars separated by ',' or ';'." diff --git a/detekt-cli/src/main/kotlin/io/gitlab/arturbosch/detekt/cli/CliRunner.kt b/detekt-cli/src/main/kotlin/io/gitlab/arturbosch/detekt/cli/CliRunner.kt index 9f17fa60be6f..bcdf1b6bf80b 100644 --- a/detekt-cli/src/main/kotlin/io/gitlab/arturbosch/detekt/cli/CliRunner.kt +++ b/detekt-cli/src/main/kotlin/io/gitlab/arturbosch/detekt/cli/CliRunner.kt @@ -7,6 +7,7 @@ import io.github.detekt.tooling.internal.DefaultAnalysisResult import io.github.detekt.tooling.internal.EmptyContainer import io.gitlab.arturbosch.detekt.cli.runners.AstPrinter import io.gitlab.arturbosch.detekt.cli.runners.ConfigExporter +import io.gitlab.arturbosch.detekt.cli.runners.CustomConfigExporter import io.gitlab.arturbosch.detekt.cli.runners.Runner import io.gitlab.arturbosch.detekt.cli.runners.VersionPrinter @@ -21,6 +22,7 @@ class CliRunner : DetektCli { val specialRunner = when { arguments.showVersion -> VersionPrinter(outputChannel) arguments.generateConfig -> ConfigExporter(arguments, outputChannel) + arguments.generateCustomConfig -> CustomConfigExporter(arguments, outputChannel) arguments.printAst -> AstPrinter(arguments, outputChannel) else -> null } diff --git a/detekt-cli/src/main/kotlin/io/gitlab/arturbosch/detekt/cli/Main.kt b/detekt-cli/src/main/kotlin/io/gitlab/arturbosch/detekt/cli/Main.kt index 18d211adca9c..6765f64828f9 100644 --- a/detekt-cli/src/main/kotlin/io/gitlab/arturbosch/detekt/cli/Main.kt +++ b/detekt-cli/src/main/kotlin/io/gitlab/arturbosch/detekt/cli/Main.kt @@ -9,6 +9,7 @@ import io.github.detekt.tooling.api.exitCode import io.github.detekt.tooling.internal.NotApiButProbablyUsedByUsers import io.gitlab.arturbosch.detekt.cli.runners.AstPrinter import io.gitlab.arturbosch.detekt.cli.runners.ConfigExporter +import io.gitlab.arturbosch.detekt.cli.runners.CustomConfigExporter import io.gitlab.arturbosch.detekt.cli.runners.Executable import io.gitlab.arturbosch.detekt.cli.runners.Runner import io.gitlab.arturbosch.detekt.cli.runners.VersionPrinter @@ -55,6 +56,7 @@ fun buildRunner( return when { arguments.showVersion -> VersionPrinter(outputPrinter) arguments.generateConfig -> ConfigExporter(arguments, outputPrinter) + arguments.generateCustomConfig -> CustomConfigExporter(arguments, outputPrinter) arguments.printAst -> AstPrinter(arguments, outputPrinter) else -> Runner(arguments, outputPrinter, errorPrinter) } diff --git a/detekt-cli/src/main/kotlin/io/gitlab/arturbosch/detekt/cli/runners/CustomConfigExporter.kt b/detekt-cli/src/main/kotlin/io/gitlab/arturbosch/detekt/cli/runners/CustomConfigExporter.kt new file mode 100644 index 000000000000..fcd534e6c90c --- /dev/null +++ b/detekt-cli/src/main/kotlin/io/gitlab/arturbosch/detekt/cli/runners/CustomConfigExporter.kt @@ -0,0 +1,24 @@ +package io.gitlab.arturbosch.detekt.cli.runners + +import io.github.detekt.tooling.api.CustomConfigurationProvider +import io.gitlab.arturbosch.detekt.cli.CliArgs +import java.nio.file.Paths + +class CustomConfigExporter( + private val arguments: CliArgs, + private val outputPrinter: Appendable, +) : Executable { + + override fun execute() { + val configPath = Paths.get(arguments.config!!) + + val args = arrayOf( + "--input", + arguments.input!!, + "--config", + arguments.config!!, + ) + CustomConfigurationProvider.load().generate(args) + outputPrinter.appendLine("Successfully generated config for user-defined rules to ${configPath.toAbsolutePath()}") + } +} diff --git a/detekt-core/build.gradle.kts b/detekt-core/build.gradle.kts index 19e1355f421d..8ad7c4cb387b 100644 --- a/detekt-core/build.gradle.kts +++ b/detekt-core/build.gradle.kts @@ -15,6 +15,7 @@ dependencies { implementation(projects.detektReportXml) implementation(projects.detektReportSarif) implementation(projects.detektUtils) + implementation(projects.detektGenerator) testRuntimeOnly(projects.detektRules) testImplementation(projects.detektTest) diff --git a/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/BindingContext.kt b/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/BindingContext.kt index a19e1831a6e2..a60d35650355 100644 --- a/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/BindingContext.kt +++ b/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/BindingContext.kt @@ -33,6 +33,7 @@ internal fun generateBindingContext( val analyzer = AnalyzerWithCompilerReport( messageCollector, environment.configuration.languageVersionSettings, + false, ) analyzer.analyzeAndReport(files) { TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration( diff --git a/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/tooling/CustomConfigProvider.kt b/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/tooling/CustomConfigProvider.kt new file mode 100644 index 000000000000..cb26ff09af22 --- /dev/null +++ b/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/tooling/CustomConfigProvider.kt @@ -0,0 +1,20 @@ +package io.gitlab.arturbosch.detekt.core.tooling + +import io.github.detekt.tooling.api.CustomConfigurationProvider + +class CustomConfigProvider : CustomConfigurationProvider { + + override fun generate(args: Array) { +// val args = arrayOf( +// "--input", +// """ +// ../detekt-rules-complexity/, +// ../detekt-rules-coroutines/, +// ../detekt-rules-documentation/, +// """.trimIndent(), +// "--config", +// "/Users/vvp/Downloads/", +// ) + io.gitlab.arturbosch.detekt.generator.CustomConfig.main(args) + } +} diff --git a/detekt-core/src/main/resources/META-INF/services/io.github.detekt.tooling.api.CustomConfigurationProvider b/detekt-core/src/main/resources/META-INF/services/io.github.detekt.tooling.api.CustomConfigurationProvider new file mode 100644 index 000000000000..4aa6b4024446 --- /dev/null +++ b/detekt-core/src/main/resources/META-INF/services/io.github.detekt.tooling.api.CustomConfigurationProvider @@ -0,0 +1 @@ +io.gitlab.arturbosch.detekt.core.tooling.CustomConfigProvider diff --git a/detekt-core/src/main/resources/default-detekt-config.yml b/detekt-core/src/main/resources/default-detekt-config.yml index 462058ca590c..9f414ae5fe05 100644 --- a/detekt-core/src/main/resources/default-detekt-config.yml +++ b/detekt-core/src/main/resources/default-detekt-config.yml @@ -78,12 +78,15 @@ comments: searchInInnerClass: true searchInInnerObject: true searchInInnerInterface: true + searchInProtectedClass: false UndocumentedPublicFunction: active: false excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + searchProtectedFunction: false UndocumentedPublicProperty: active: false excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + searchProtectedProperty: false complexity: active: true @@ -531,8 +534,10 @@ style: ForbiddenMethodCall: active: false methods: - - 'kotlin.io.print' - - 'kotlin.io.println' + - reason: 'print does not allow you to configure the output stream. Use a logger instead.' + value: 'kotlin.io.print' + - reason: 'println does not allow you to configure the output stream. Use a logger instead.' + value: 'kotlin.io.println' ForbiddenPublicDataClass: active: true excludes: ['**'] @@ -626,7 +631,8 @@ style: ReturnCount: active: true max: 2 - excludedFunctions: 'equals' + excludedFunctions: + - 'equals' excludeLabeled: false excludeReturnFromLambda: true excludeGuardClauses: false @@ -642,6 +648,8 @@ style: excludeGuardClauses: false TrailingWhitespace: active: false + TrimMultilineRawString: + active: false UnderscoresInNumericLiterals: active: false acceptableLength: 4 @@ -664,6 +672,7 @@ style: active: false UnnecessaryParentheses: active: false + allowForUnclearPrecedence: false UntilInsteadOfRangeTo: active: false UnusedImports: diff --git a/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/config/YamlConfigSpec.kt b/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/config/YamlConfigSpec.kt index 8c45bc8c7623..b010d479e5e3 100644 --- a/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/config/YamlConfigSpec.kt +++ b/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/config/YamlConfigSpec.kt @@ -57,7 +57,9 @@ class YamlConfigSpec { @Suppress("UNUSED_VARIABLE") val ignored = config.valueOrDefault("style", "") } - .withMessage("Value \"{WildcardImport={active=true}, NoElseInWhenExpression={active=true}, MagicNumber={active=true, ignoreNumbers=[-1, 0, 1, 2]}}\" set for config parameter \"style\" is not of required type String.") + .withMessage( + "Value \"{WildcardImport={active=true}, NoElseInWhenExpression={active=true}, MagicNumber={active=true, ignoreNumbers=[-1, 0, 1, 2]}}\" set for config parameter \"style\" is not of required type String." + ) } } @@ -94,7 +96,9 @@ class YamlConfigSpec { .subConfig("Rule") .valueOrDefault("threshold", 6) } - .withMessage("Value \"v5.7\" set for config parameter \"RuleSet > Rule > threshold\" is not of required type Int.") + .withMessage( + "Value \"v5.7\" set for config parameter \"RuleSet > Rule > threshold\" is not of required type Int." + ) } @Test @@ -105,7 +109,9 @@ class YamlConfigSpec { .subConfig("Rule") .valueOrDefault("active", 1) } - .withMessage("Value \"[]\" set for config parameter \"RuleSet > Rule > active\" is not of required type Int.") + .withMessage( + "Value \"[]\" set for config parameter \"RuleSet > Rule > active\" is not of required type Int." + ) } } diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/FormattingRule.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/FormattingRule.kt index b01b80a9682a..defc385ea1e7 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/FormattingRule.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/FormattingRule.kt @@ -3,7 +3,7 @@ package io.gitlab.arturbosch.detekt.formatting import com.pinterest.ktlint.core.KtLint import com.pinterest.ktlint.core.Rule.VisitorModifier.RunAsLateAsPossible import com.pinterest.ktlint.core.Rule.VisitorModifier.RunOnRootNodeOnly -import com.pinterest.ktlint.core.api.FeatureInAlphaState +import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties.codeStyleSetProperty import com.pinterest.ktlint.core.api.UsesEditorConfigProperties import io.github.detekt.psi.fileName import io.github.detekt.psi.toFilePath @@ -27,7 +27,6 @@ import org.jetbrains.kotlin.psi.psiUtil.endOffset /** * Rule to detect formatting violations. */ -@OptIn(FeatureInAlphaState::class) abstract class FormattingRule(config: Config) : Rule(config) { abstract val wrapping: com.pinterest.ktlint.core.Rule @@ -53,15 +52,20 @@ abstract class FormattingRule(config: Config) : Rule(config) { override fun visit(root: KtFile) { this.root = root - root.node.putUserData(KtLint.ANDROID_USER_DATA_KEY, isAndroid) positionByOffset = KtLintLineColCalculator .calculateLineColByOffset(KtLintLineColCalculator.normalizeText(root.text)) - val editorConfigProperties = overrideEditorConfigProperties() + val editorConfigProperties = overrideEditorConfigProperties()?.toMutableMap() + ?: mutableMapOf() - if (!editorConfigProperties.isNullOrEmpty()) { + if (isAndroid) { + editorConfigProperties[codeStyleSetProperty] = "android" + } + + if (editorConfigProperties.isNotEmpty()) { val userData = (root.node.getUserData(KtLint.EDITOR_CONFIG_PROPERTIES_USER_DATA_KEY).orEmpty()) .toMutableMap() + editorConfigProperties.forEach { (editorConfigProperty, defaultValue) -> userData[editorConfigProperty.type.name] = Property.builder() .name(editorConfigProperty.type.name) @@ -81,18 +85,6 @@ abstract class FormattingRule(config: Config) : Rule(config) { return } - // KtLint 0.44.0 is assuming that KtLint.EDITOR_CONFIG_USER_DATA_KEY is available on all the nodes. - // If not, it crashes with a NPE. Here we're patching their behavior. - // This block is deprecated and will be removed in KtLint 0.46. But we have to suppress the - // deprecation warning because the ci runs with -Werror. - @Suppress("DEPRECATION") - if (node.getUserData(KtLint.EDITOR_CONFIG_USER_DATA_KEY) == null) { - node.putUserData( - KtLint.EDITOR_CONFIG_USER_DATA_KEY, - com.pinterest.ktlint.core.EditorConfig.Companion.fromMap(emptyMap()) - ) - } - wrapping.visit(node, autoCorrect) { offset, message, _ -> val (line, column) = positionByOffset(offset) val location = Location( diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/KtLintMultiRule.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/KtLintMultiRule.kt index 96fdaaa64e8b..2b7844d50917 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/KtLintMultiRule.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/KtLintMultiRule.kt @@ -24,6 +24,7 @@ import io.gitlab.arturbosch.detekt.formatting.wrappers.ModifierListSpacing import io.gitlab.arturbosch.detekt.formatting.wrappers.ModifierOrdering import io.gitlab.arturbosch.detekt.formatting.wrappers.MultiLineIfElse import io.gitlab.arturbosch.detekt.formatting.wrappers.NoBlankLineBeforeRbrace +import io.gitlab.arturbosch.detekt.formatting.wrappers.NoBlankLinesInChainedMethodCalls import io.gitlab.arturbosch.detekt.formatting.wrappers.NoConsecutiveBlankLines import io.gitlab.arturbosch.detekt.formatting.wrappers.NoEmptyClassBody import io.gitlab.arturbosch.detekt.formatting.wrappers.NoEmptyFirstLineInMethodBlock @@ -77,6 +78,7 @@ class KtLintMultiRule(config: Config = Config.empty) : MultiRule() { MaximumLineLength(config), ModifierOrdering(config), NoBlankLineBeforeRbrace(config), + NoBlankLinesInChainedMethodCalls(config), NoConsecutiveBlankLines(config), NoEmptyClassBody(config), NoLineBreakAfterElse(config), diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/AnnotationOnSeparateLine.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/AnnotationOnSeparateLine.kt index af06e4761e1b..afe438a83179 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/AnnotationOnSeparateLine.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/AnnotationOnSeparateLine.kt @@ -1,7 +1,8 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers -import com.pinterest.ktlint.ruleset.experimental.AnnotationRule +import com.pinterest.ktlint.ruleset.standard.AnnotationRule import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault import io.gitlab.arturbosch.detekt.api.internal.AutoCorrectable import io.gitlab.arturbosch.detekt.formatting.FormattingRule @@ -9,6 +10,7 @@ import io.gitlab.arturbosch.detekt.formatting.FormattingRule * See [ktlint-readme](https://github.com/pinterest/ktlint#standard-rules) for documentation. */ @AutoCorrectable(since = "1.0.0") +@ActiveByDefault(since = "1.22.0") class AnnotationOnSeparateLine(config: Config) : FormattingRule(config) { override val wrapping = AnnotationRule() diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/AnnotationSpacing.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/AnnotationSpacing.kt index 923341607f51..f2ef0f611884 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/AnnotationSpacing.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/AnnotationSpacing.kt @@ -1,7 +1,8 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers -import com.pinterest.ktlint.ruleset.experimental.AnnotationSpacingRule +import com.pinterest.ktlint.ruleset.standard.AnnotationSpacingRule import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault import io.gitlab.arturbosch.detekt.api.internal.AutoCorrectable import io.gitlab.arturbosch.detekt.formatting.FormattingRule @@ -9,6 +10,7 @@ import io.gitlab.arturbosch.detekt.formatting.FormattingRule * See [ktlint-readme](https://github.com/pinterest/ktlint#standard-rules) for documentation. */ @AutoCorrectable(since = "1.0.0") +@ActiveByDefault(since = "1.22.0") class AnnotationSpacing(config: Config) : FormattingRule(config) { override val wrapping = AnnotationSpacingRule() diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/ArgumentListWrapping.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/ArgumentListWrapping.kt index 018e4eb06f90..ac35986edb74 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/ArgumentListWrapping.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/ArgumentListWrapping.kt @@ -1,12 +1,12 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties -import com.pinterest.ktlint.core.api.FeatureInAlphaState import com.pinterest.ktlint.core.api.UsesEditorConfigProperties -import com.pinterest.ktlint.ruleset.experimental.ArgumentListWrappingRule +import com.pinterest.ktlint.ruleset.standard.ArgumentListWrappingRule import io.gitlab.arturbosch.detekt.api.Config import io.gitlab.arturbosch.detekt.api.config import io.gitlab.arturbosch.detekt.api.configWithAndroidVariants +import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault import io.gitlab.arturbosch.detekt.api.internal.AutoCorrectable import io.gitlab.arturbosch.detekt.api.internal.Configuration import io.gitlab.arturbosch.detekt.formatting.FormattingRule @@ -14,8 +14,8 @@ import io.gitlab.arturbosch.detekt.formatting.FormattingRule /** * See [ktlint-readme](https://github.com/pinterest/ktlint#standard-rules) for documentation. */ -@OptIn(FeatureInAlphaState::class) @AutoCorrectable(since = "1.0.0") +@ActiveByDefault(since = "1.22.0") class ArgumentListWrapping(config: Config) : FormattingRule(config) { override val wrapping = ArgumentListWrappingRule() diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/CommentWrapping.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/CommentWrapping.kt index 64aacb7409c0..923ba37cc350 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/CommentWrapping.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/CommentWrapping.kt @@ -1,7 +1,6 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties -import com.pinterest.ktlint.core.api.FeatureInAlphaState import com.pinterest.ktlint.core.api.UsesEditorConfigProperties import com.pinterest.ktlint.ruleset.experimental.CommentWrappingRule import io.gitlab.arturbosch.detekt.api.Config @@ -22,7 +21,6 @@ class CommentWrapping(config: Config) : FormattingRule(config) { @Configuration("indentation size") private val indentSize by config(4) - @OptIn(FeatureInAlphaState::class) override fun overrideEditorConfigProperties(): Map, String> = mapOf( DefaultEditorConfigProperties.indentSizeProperty to indentSize.toString(), diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/EnumEntryNameCase.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/EnumEntryNameCase.kt index b93e45b6e08a..5338c219fa67 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/EnumEntryNameCase.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/EnumEntryNameCase.kt @@ -1,7 +1,8 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers -import com.pinterest.ktlint.ruleset.experimental.EnumEntryNameCaseRule +import com.pinterest.ktlint.ruleset.standard.EnumEntryNameCaseRule import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault import io.gitlab.arturbosch.detekt.api.internal.AutoCorrectable import io.gitlab.arturbosch.detekt.formatting.FormattingRule @@ -9,6 +10,7 @@ import io.gitlab.arturbosch.detekt.formatting.FormattingRule * See [ktlint-readme](https://github.com/pinterest/ktlint#standard-rules) for documentation. */ @AutoCorrectable(since = "1.4.0") +@ActiveByDefault(since = "1.22.0") class EnumEntryNameCase(config: Config) : FormattingRule(config) { override val wrapping = EnumEntryNameCaseRule() diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/FinalNewline.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/FinalNewline.kt index 663c377faff4..810f0f1d7fac 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/FinalNewline.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/FinalNewline.kt @@ -1,7 +1,6 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties -import com.pinterest.ktlint.core.api.FeatureInAlphaState import com.pinterest.ktlint.core.api.UsesEditorConfigProperties import com.pinterest.ktlint.ruleset.standard.FinalNewlineRule import io.gitlab.arturbosch.detekt.api.Config @@ -27,7 +26,6 @@ class FinalNewline(config: Config) : FormattingRule(config) { @Configuration("report absence or presence of a newline") private val insertFinalNewLine by config(true) - @OptIn(FeatureInAlphaState::class) override fun overrideEditorConfigProperties(): Map, String> = mapOf(DefaultEditorConfigProperties.insertNewLineProperty to insertFinalNewLine.toString()) } diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/ImportOrdering.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/ImportOrdering.kt index e9fc4258f59a..845834bcadac 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/ImportOrdering.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/ImportOrdering.kt @@ -1,6 +1,5 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers -import com.pinterest.ktlint.core.api.FeatureInAlphaState import com.pinterest.ktlint.core.api.UsesEditorConfigProperties import com.pinterest.ktlint.ruleset.standard.ImportOrderingRule import io.gitlab.arturbosch.detekt.api.Config @@ -25,7 +24,6 @@ class ImportOrdering(config: Config) : FormattingRule(config) { @Configuration("the import ordering layout") private val layout: String by configWithAndroidVariants(IDEA_PATTERN, ASCII_PATTERN) - @OptIn(FeatureInAlphaState::class) override fun overrideEditorConfigProperties(): Map, String> = mapOf(ImportOrderingRule.ideaImportsLayoutProperty to layout) diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/Indentation.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/Indentation.kt index 20ba3726de74..c5c100c74c64 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/Indentation.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/Indentation.kt @@ -1,7 +1,6 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties -import com.pinterest.ktlint.core.api.FeatureInAlphaState import com.pinterest.ktlint.core.api.UsesEditorConfigProperties import com.pinterest.ktlint.ruleset.standard.IndentationRule import io.gitlab.arturbosch.detekt.api.Config @@ -31,7 +30,6 @@ class Indentation(config: Config) : FormattingRule(config) { @Suppress("UnusedPrivateMember") private val continuationIndentSize by config(4) - @OptIn(FeatureInAlphaState::class) override fun overrideEditorConfigProperties(): Map, String> = mapOf( DefaultEditorConfigProperties.indentSizeProperty to indentSize.toString(), diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/KdocWrapping.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/KdocWrapping.kt index 041e9a0de232..1d104cafd1cc 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/KdocWrapping.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/KdocWrapping.kt @@ -1,7 +1,6 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties -import com.pinterest.ktlint.core.api.FeatureInAlphaState import com.pinterest.ktlint.core.api.UsesEditorConfigProperties import com.pinterest.ktlint.ruleset.experimental.KdocWrappingRule import io.gitlab.arturbosch.detekt.api.Config @@ -22,7 +21,6 @@ class KdocWrapping(config: Config) : FormattingRule(config) { @Configuration("indentation size") private val indentSize by config(4) - @OptIn(FeatureInAlphaState::class) override fun overrideEditorConfigProperties(): Map, String> = mapOf( DefaultEditorConfigProperties.indentSizeProperty to indentSize.toString(), diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/MaximumLineLength.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/MaximumLineLength.kt index b60dda6aa15d..f8274d04bf91 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/MaximumLineLength.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/MaximumLineLength.kt @@ -1,7 +1,6 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties -import com.pinterest.ktlint.core.api.FeatureInAlphaState import com.pinterest.ktlint.core.api.UsesEditorConfigProperties import com.pinterest.ktlint.ruleset.standard.MaxLineLengthRule import io.gitlab.arturbosch.detekt.api.Config @@ -19,7 +18,6 @@ import io.gitlab.arturbosch.detekt.formatting.FormattingRule * auto-correct the issue. */ @ActiveByDefault(since = "1.0.0") -@OptIn(FeatureInAlphaState::class) class MaximumLineLength(config: Config) : FormattingRule(config) { override val wrapping = MaxLineLengthRule() diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/MultiLineIfElse.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/MultiLineIfElse.kt index 90fa0785abda..7fceed19463a 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/MultiLineIfElse.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/MultiLineIfElse.kt @@ -1,7 +1,8 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers -import com.pinterest.ktlint.ruleset.experimental.MultiLineIfElseRule +import com.pinterest.ktlint.ruleset.standard.MultiLineIfElseRule import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault import io.gitlab.arturbosch.detekt.api.internal.AutoCorrectable import io.gitlab.arturbosch.detekt.formatting.FormattingRule @@ -9,6 +10,7 @@ import io.gitlab.arturbosch.detekt.formatting.FormattingRule * See [ktlint-readme](https://github.com/pinterest/ktlint#standard-rules) for documentation. */ @AutoCorrectable(since = "1.0.0") +@ActiveByDefault(since = "1.22.0") class MultiLineIfElse(config: Config) : FormattingRule(config) { override val wrapping = MultiLineIfElseRule() diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/NoBlankLinesInChainedMethodCalls.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/NoBlankLinesInChainedMethodCalls.kt new file mode 100644 index 000000000000..dfc35b0cc4ef --- /dev/null +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/NoBlankLinesInChainedMethodCalls.kt @@ -0,0 +1,18 @@ +package io.gitlab.arturbosch.detekt.formatting.wrappers + +import com.pinterest.ktlint.ruleset.standard.NoBlankLinesInChainedMethodCallsRule +import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault +import io.gitlab.arturbosch.detekt.api.internal.AutoCorrectable +import io.gitlab.arturbosch.detekt.formatting.FormattingRule + +/** + * See [ktlint-website](https://ktlint.github.io) for documentation. + */ +@ActiveByDefault(since = "1.22.0") +@AutoCorrectable(since = "1.22.0") +class NoBlankLinesInChainedMethodCalls(config: Config) : FormattingRule(config) { + + override val wrapping = NoBlankLinesInChainedMethodCallsRule() + override val issue = issueFor("Detects blank lines in chained method rules.") +} diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/NoEmptyFirstLineInMethodBlock.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/NoEmptyFirstLineInMethodBlock.kt index 79031348219b..61e1aa05ef65 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/NoEmptyFirstLineInMethodBlock.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/NoEmptyFirstLineInMethodBlock.kt @@ -1,7 +1,8 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers -import com.pinterest.ktlint.ruleset.experimental.NoEmptyFirstLineInMethodBlockRule +import com.pinterest.ktlint.ruleset.standard.NoEmptyFirstLineInMethodBlockRule import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault import io.gitlab.arturbosch.detekt.api.internal.AutoCorrectable import io.gitlab.arturbosch.detekt.formatting.FormattingRule @@ -9,6 +10,7 @@ import io.gitlab.arturbosch.detekt.formatting.FormattingRule * See [ktlint-readme](https://github.com/pinterest/ktlint#standard-rules) for documentation. */ @AutoCorrectable(since = "1.4.0") +@ActiveByDefault(since = "1.22.0") class NoEmptyFirstLineInMethodBlock(config: Config) : FormattingRule(config) { override val wrapping = NoEmptyFirstLineInMethodBlockRule() diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/NoWildcardImports.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/NoWildcardImports.kt index 579a0fc48ce7..aea84fcdd37b 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/NoWildcardImports.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/NoWildcardImports.kt @@ -1,6 +1,5 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers -import com.pinterest.ktlint.core.api.FeatureInAlphaState import com.pinterest.ktlint.core.api.UsesEditorConfigProperties import com.pinterest.ktlint.ruleset.standard.NoWildcardImportsRule import io.gitlab.arturbosch.detekt.api.Config @@ -21,7 +20,6 @@ class NoWildcardImports(config: Config) : FormattingRule(config) { @Configuration("Defines allowed wildcard imports") private val packagesToUseImportOnDemandProperty by config(ALLOWED_WILDCARD_IMPORTS) - @OptIn(FeatureInAlphaState::class) override fun overrideEditorConfigProperties(): Map, String> = mapOf( NoWildcardImportsRule.packagesToUseImportOnDemandProperty to packagesToUseImportOnDemandProperty diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/PackageName.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/PackageName.kt index 2a9a8eb3983d..7d99d34e3905 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/PackageName.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/PackageName.kt @@ -1,7 +1,8 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers -import com.pinterest.ktlint.ruleset.experimental.PackageNameRule +import com.pinterest.ktlint.ruleset.standard.PackageNameRule import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault import io.gitlab.arturbosch.detekt.api.internal.AutoCorrectable import io.gitlab.arturbosch.detekt.formatting.FormattingRule @@ -9,6 +10,7 @@ import io.gitlab.arturbosch.detekt.formatting.FormattingRule * See [ktlint-readme](https://github.com/pinterest/ktlint#standard-rules) for documentation. */ @AutoCorrectable(since = "1.0.0") +@ActiveByDefault(since = "1.22.0") class PackageName(config: Config) : FormattingRule(config) { override val wrapping = PackageNameRule() diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/ParameterListWrapping.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/ParameterListWrapping.kt index 98e9bc5ca794..6a360326a8f0 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/ParameterListWrapping.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/ParameterListWrapping.kt @@ -1,7 +1,6 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties -import com.pinterest.ktlint.core.api.FeatureInAlphaState import com.pinterest.ktlint.core.api.UsesEditorConfigProperties import com.pinterest.ktlint.ruleset.standard.ParameterListWrappingRule import io.gitlab.arturbosch.detekt.api.Config @@ -30,7 +29,6 @@ class ParameterListWrapping(config: Config) : FormattingRule(config) { @Suppress("UnusedPrivateMember") private val indentSize by config(4) - @OptIn(FeatureInAlphaState::class) override fun overrideEditorConfigProperties(): Map, String> = mapOf( DefaultEditorConfigProperties.maxLineLengthProperty to maxLineLength.toString() diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/SpacingAroundAngleBrackets.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/SpacingAroundAngleBrackets.kt index 94e2c39ad4f9..67548b647c46 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/SpacingAroundAngleBrackets.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/SpacingAroundAngleBrackets.kt @@ -1,7 +1,8 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers -import com.pinterest.ktlint.ruleset.experimental.SpacingAroundAngleBracketsRule +import com.pinterest.ktlint.ruleset.standard.SpacingAroundAngleBracketsRule import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault import io.gitlab.arturbosch.detekt.api.internal.AutoCorrectable import io.gitlab.arturbosch.detekt.formatting.FormattingRule @@ -9,6 +10,7 @@ import io.gitlab.arturbosch.detekt.formatting.FormattingRule * See [ktlint-readme](https://github.com/pinterest/ktlint#spacing) for documentation. */ @AutoCorrectable(since = "1.16.0") +@ActiveByDefault(since = "1.22.0") class SpacingAroundAngleBrackets(config: Config) : FormattingRule(config) { override val wrapping = SpacingAroundAngleBracketsRule() diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/SpacingAroundDoubleColon.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/SpacingAroundDoubleColon.kt index 940a9c558ecd..87931efce23e 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/SpacingAroundDoubleColon.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/SpacingAroundDoubleColon.kt @@ -1,7 +1,8 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers -import com.pinterest.ktlint.ruleset.experimental.SpacingAroundDoubleColonRule +import com.pinterest.ktlint.ruleset.standard.SpacingAroundDoubleColonRule import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault import io.gitlab.arturbosch.detekt.api.internal.AutoCorrectable import io.gitlab.arturbosch.detekt.formatting.FormattingRule @@ -9,6 +10,7 @@ import io.gitlab.arturbosch.detekt.formatting.FormattingRule * See [ktlint-readme](https://github.com/pinterest/ktlint#spacing) for documentation. */ @AutoCorrectable(since = "1.10.0") +@ActiveByDefault(since = "1.22.0") class SpacingAroundDoubleColon(config: Config) : FormattingRule(config) { override val wrapping = SpacingAroundDoubleColonRule() diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/SpacingAroundUnaryOperator.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/SpacingAroundUnaryOperator.kt index b4612c1813bd..84d778004f83 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/SpacingAroundUnaryOperator.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/SpacingAroundUnaryOperator.kt @@ -1,7 +1,8 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers -import com.pinterest.ktlint.ruleset.experimental.SpacingAroundUnaryOperatorRule +import com.pinterest.ktlint.ruleset.standard.SpacingAroundUnaryOperatorRule import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault import io.gitlab.arturbosch.detekt.api.internal.AutoCorrectable import io.gitlab.arturbosch.detekt.formatting.FormattingRule @@ -9,6 +10,7 @@ import io.gitlab.arturbosch.detekt.formatting.FormattingRule * See [ktlint-readme](https://github.com/pinterest/ktlint#spacing) for documentation. */ @AutoCorrectable(since = "1.16.0") +@ActiveByDefault(since = "1.22.0") class SpacingAroundUnaryOperator(config: Config) : FormattingRule(config) { override val wrapping = SpacingAroundUnaryOperatorRule() diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/SpacingBetweenDeclarationsWithAnnotations.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/SpacingBetweenDeclarationsWithAnnotations.kt index 33c54e242845..5b3e0205b000 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/SpacingBetweenDeclarationsWithAnnotations.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/SpacingBetweenDeclarationsWithAnnotations.kt @@ -1,7 +1,8 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers -import com.pinterest.ktlint.ruleset.experimental.SpacingBetweenDeclarationsWithAnnotationsRule +import com.pinterest.ktlint.ruleset.standard.SpacingBetweenDeclarationsWithAnnotationsRule import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault import io.gitlab.arturbosch.detekt.api.internal.AutoCorrectable import io.gitlab.arturbosch.detekt.formatting.FormattingRule @@ -9,6 +10,7 @@ import io.gitlab.arturbosch.detekt.formatting.FormattingRule * See [ktlint-readme](https://github.com/pinterest/ktlint#spacing) for documentation. */ @AutoCorrectable(since = "1.10.0") +@ActiveByDefault(since = "1.22.0") class SpacingBetweenDeclarationsWithAnnotations(config: Config) : FormattingRule(config) { override val wrapping = SpacingBetweenDeclarationsWithAnnotationsRule() diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/SpacingBetweenDeclarationsWithComments.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/SpacingBetweenDeclarationsWithComments.kt index 2ccdbec98a16..8c1c72f68ef8 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/SpacingBetweenDeclarationsWithComments.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/SpacingBetweenDeclarationsWithComments.kt @@ -1,7 +1,8 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers -import com.pinterest.ktlint.ruleset.experimental.SpacingBetweenDeclarationsWithCommentsRule +import com.pinterest.ktlint.ruleset.standard.SpacingBetweenDeclarationsWithCommentsRule import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault import io.gitlab.arturbosch.detekt.api.internal.AutoCorrectable import io.gitlab.arturbosch.detekt.formatting.FormattingRule @@ -9,6 +10,7 @@ import io.gitlab.arturbosch.detekt.formatting.FormattingRule * See [ktlint-readme](https://github.com/pinterest/ktlint#spacing) for documentation. */ @AutoCorrectable(since = "1.10.0") +@ActiveByDefault(since = "1.22.0") class SpacingBetweenDeclarationsWithComments(config: Config) : FormattingRule(config) { override val wrapping = SpacingBetweenDeclarationsWithCommentsRule() diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/TrailingComma.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/TrailingComma.kt index e53432261151..6570b68facb9 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/TrailingComma.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/TrailingComma.kt @@ -1,6 +1,5 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers -import com.pinterest.ktlint.core.api.FeatureInAlphaState import com.pinterest.ktlint.core.api.UsesEditorConfigProperties import com.pinterest.ktlint.ruleset.experimental.trailingcomma.TrailingCommaRule import io.gitlab.arturbosch.detekt.api.Config @@ -12,7 +11,6 @@ import io.gitlab.arturbosch.detekt.formatting.FormattingRule /** * See [ktlint-readme](https://github.com/pinterest/ktlint#standard-rules) for documentation. */ -@OptIn(FeatureInAlphaState::class) @AutoCorrectable(since = "1.20.0") class TrailingComma(config: Config) : FormattingRule(config) { diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/UnnecessaryParenthesesBeforeTrailingLambda.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/UnnecessaryParenthesesBeforeTrailingLambda.kt index 1b95932dc14c..9a3cf745cebd 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/UnnecessaryParenthesesBeforeTrailingLambda.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/UnnecessaryParenthesesBeforeTrailingLambda.kt @@ -1,6 +1,5 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers -import com.pinterest.ktlint.core.api.FeatureInAlphaState import com.pinterest.ktlint.ruleset.experimental.UnnecessaryParenthesesBeforeTrailingLambdaRule import io.gitlab.arturbosch.detekt.api.Config import io.gitlab.arturbosch.detekt.api.internal.AutoCorrectable @@ -9,7 +8,6 @@ import io.gitlab.arturbosch.detekt.formatting.FormattingRule /** * See [ktlint-readme](https://github.com/pinterest/ktlint#experimental-rules) for documentation. */ -@OptIn(FeatureInAlphaState::class) @AutoCorrectable(since = "1.20.0") class UnnecessaryParenthesesBeforeTrailingLambda(config: Config) : FormattingRule(config) { diff --git a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/Wrapping.kt b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/Wrapping.kt index 62f2f1eb6534..fc2ba013c8f1 100644 --- a/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/Wrapping.kt +++ b/detekt-formatting/src/main/kotlin/io/gitlab/arturbosch/detekt/formatting/wrappers/Wrapping.kt @@ -1,10 +1,14 @@ package io.gitlab.arturbosch.detekt.formatting.wrappers +import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties +import com.pinterest.ktlint.core.api.UsesEditorConfigProperties import com.pinterest.ktlint.ruleset.standard.WrappingRule import io.gitlab.arturbosch.detekt.api.Config import io.gitlab.arturbosch.detekt.api.TextLocation +import io.gitlab.arturbosch.detekt.api.config import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault import io.gitlab.arturbosch.detekt.api.internal.AutoCorrectable +import io.gitlab.arturbosch.detekt.api.internal.Configuration import io.gitlab.arturbosch.detekt.formatting.FormattingRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode @@ -18,6 +22,9 @@ class Wrapping(config: Config) : FormattingRule(config) { override val wrapping = WrappingRule() override val issue = issueFor("Reports missing newlines (e.g. between parentheses of a multi-line function call") + @Configuration("indentation size") + private val indentSize by config(4) + /** * [Wrapping] has visitor modifier RunOnRootNodeOnly, so [node] is always the root file. * Override the parent implementation to highlight the entire file. @@ -26,4 +33,9 @@ class Wrapping(config: Config) : FormattingRule(config) { // Use offset + 1 since Wrapping always reports the location of missing new line. return TextLocation(offset, offset + 1) } + + override fun overrideEditorConfigProperties(): Map, String> = + mapOf( + DefaultEditorConfigProperties.indentSizeProperty to indentSize.toString(), + ) } diff --git a/detekt-formatting/src/main/resources/config/config.yml b/detekt-formatting/src/main/resources/config/config.yml index 739a22e28ce0..8a620c0ecdf0 100644 --- a/detekt-formatting/src/main/resources/config/config.yml +++ b/detekt-formatting/src/main/resources/config/config.yml @@ -3,13 +3,13 @@ formatting: android: false autoCorrect: true AnnotationOnSeparateLine: - active: false + active: true autoCorrect: true AnnotationSpacing: - active: false + active: true autoCorrect: true ArgumentListWrapping: - active: false + active: true autoCorrect: true indentSize: 4 maxLineLength: 120 @@ -30,7 +30,7 @@ formatting: active: false autoCorrect: true EnumEntryNameCase: - active: false + active: true autoCorrect: true Filename: active: true @@ -67,11 +67,14 @@ formatting: active: true autoCorrect: true MultiLineIfElse: - active: false + active: true autoCorrect: true NoBlankLineBeforeRbrace: active: true autoCorrect: true + NoBlankLinesInChainedMethodCalls: + active: true + autoCorrect: true NoConsecutiveBlankLines: active: true autoCorrect: true @@ -79,7 +82,7 @@ formatting: active: true autoCorrect: true NoEmptyFirstLineInMethodBlock: - active: false + active: true autoCorrect: true NoLineBreakAfterElse: active: true @@ -106,14 +109,14 @@ formatting: active: true packagesToUseImportOnDemandProperty: 'java.util.*,kotlinx.android.synthetic.**' PackageName: - active: false + active: true autoCorrect: true ParameterListWrapping: active: true autoCorrect: true maxLineLength: 120 SpacingAroundAngleBrackets: - active: false + active: true autoCorrect: true SpacingAroundColon: active: true @@ -128,7 +131,7 @@ formatting: active: true autoCorrect: true SpacingAroundDoubleColon: - active: false + active: true autoCorrect: true SpacingAroundKeyword: active: true @@ -143,13 +146,13 @@ formatting: active: true autoCorrect: true SpacingAroundUnaryOperator: - active: false + active: true autoCorrect: true SpacingBetweenDeclarationsWithAnnotations: - active: false + active: true autoCorrect: true SpacingBetweenDeclarationsWithComments: - active: false + active: true autoCorrect: true StringTemplate: active: true @@ -168,3 +171,4 @@ formatting: Wrapping: active: true autoCorrect: true + indentSize: 4 diff --git a/detekt-formatting/src/test/kotlin/io/gitlab/arturbosch/detekt/formatting/IndentationSpec.kt b/detekt-formatting/src/test/kotlin/io/gitlab/arturbosch/detekt/formatting/IndentationSpec.kt index 4132f564fc01..dde037d753e9 100644 --- a/detekt-formatting/src/test/kotlin/io/gitlab/arturbosch/detekt/formatting/IndentationSpec.kt +++ b/detekt-formatting/src/test/kotlin/io/gitlab/arturbosch/detekt/formatting/IndentationSpec.kt @@ -34,7 +34,7 @@ class IndentationSpec { @Test fun `places finding location to the indentation`() { subject.lint(code).assert() - .hasSourceLocation(2, 1) + .hasStartSourceLocation(2, 1) .hasTextLocations(13 to 14) } } diff --git a/detekt-formatting/src/test/kotlin/io/gitlab/arturbosch/detekt/formatting/WrappingSpec.kt b/detekt-formatting/src/test/kotlin/io/gitlab/arturbosch/detekt/formatting/WrappingSpec.kt index 47235379254a..fce2fa2241be 100644 --- a/detekt-formatting/src/test/kotlin/io/gitlab/arturbosch/detekt/formatting/WrappingSpec.kt +++ b/detekt-formatting/src/test/kotlin/io/gitlab/arturbosch/detekt/formatting/WrappingSpec.kt @@ -31,7 +31,7 @@ class WrappingSpec { subject.lint(code).assert() .hasSize(1) - .hasSourceLocation(1, 12) + .hasStartSourceLocation(1, 12) .hasTextLocations(11 to 12) } } diff --git a/detekt-generator/build.gradle.kts b/detekt-generator/build.gradle.kts index e1a96393dcba..8add8a7aa232 100644 --- a/detekt-generator/build.gradle.kts +++ b/detekt-generator/build.gradle.kts @@ -1,5 +1,3 @@ -import java.io.ByteArrayOutputStream - plugins { id("module") } @@ -68,16 +66,13 @@ val generateDocumentation by tasks.registering(JavaExec::class) { } val verifyGeneratorOutput by tasks.registering(Exec::class) { - notCompatibleWithConfigurationCache("cannot serialize object of type java.io.ByteArrayOutputStream") dependsOn(generateDocumentation) description = "Verifies that the default-detekt-config.yml is up-to-date" - val configDiff = ByteArrayOutputStream() - - commandLine = listOf("git", "diff", defaultConfigFile, deprecationFile) - standardOutput = configDiff + commandLine = listOf("git", "diff", "--quiet", defaultConfigFile, deprecationFile) + isIgnoreExitValue = true doLast { - if (configDiff.toString().isNotEmpty()) { + if (executionResult.get().exitValue == 1) { throw GradleException( "The default-detekt-config.yml is not up-to-date. " + "You can execute the generateDocumentation Gradle task " + diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/CustomConfig.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/CustomConfig.kt new file mode 100644 index 000000000000..aed6fc2abc7d --- /dev/null +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/CustomConfig.kt @@ -0,0 +1,24 @@ +package io.gitlab.arturbosch.detekt.generator + +import com.beust.jcommander.JCommander +import java.nio.file.Files +import kotlin.system.exitProcess + +object CustomConfig { + @Suppress("detekt.SpreadOperator") + @JvmStatic + fun main(args: Array) { + val options = GeneratorArgsReduced() + val parser = JCommander(options) + parser.parse(*args) + + if (options.help) { + parser.usage() + exitProcess(0) + } + + require(Files.isDirectory(options.configPath)) { "Config path must be a directory." } + + Generator(options).executeCustomConfig() + } +} diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/DetektPrinter.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/DetektPrinter.kt index a9dbb1e11f89..e3fca881122e 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/DetektPrinter.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/DetektPrinter.kt @@ -38,6 +38,12 @@ class DetektPrinter(private val arguments: GeneratorArgs) { } } + fun printCustomConfig(pages: List) { + yamlWriter.write(arguments.configPath, "custom-rules-config") { + ConfigPrinter.printCustomConfig(pages) + } + } + private fun markdownHeader(ruleSet: String): String { check(ruleSet.length > 1) { "Rule set name must be not empty or less than two symbols." } return """ diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/Generator.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/Generator.kt index af96985c7635..63270eb53909 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/Generator.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/Generator.kt @@ -39,4 +39,18 @@ class Generator( outPrinter.println("\nGenerated all detekt documentation in $time ms.") } + + fun executeCustomConfig() { + val parser = KtCompiler() + val time = measureTimeMillis { + val ktFiles = arguments.inputPath + .flatMap { parseAll(parser, it) } + + ktFiles.forEach(collector::visit) + + printer.printCustomConfig(collector.items) + } + + outPrinter.println("\nGenerated custom rules config in $time ms.") + } } diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/GeneratorArgs.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/GeneratorArgs.kt index 6a6b4fa6f0fb..85bf279acc15 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/GeneratorArgs.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/GeneratorArgs.kt @@ -5,7 +5,14 @@ import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths -class GeneratorArgs { +abstract class GeneratorArgs { + abstract val inputPath: List + abstract val configPath: Path + open val documentationPath: Path = Paths.get("") + open val cliOptionsPath: Path = Paths.get("") +} + +class GeneratorArgsFull : GeneratorArgs() { @Parameter( names = ["--input", "-i"], @@ -42,7 +49,7 @@ class GeneratorArgs { ) var help: Boolean = false - val inputPath: List by lazy { + override val inputPath: List by lazy { checkNotNull(input) { "Input parameter was not initialized by jcommander!" } .splitToSequence(",", ";") .map(String::trim) @@ -51,20 +58,58 @@ class GeneratorArgs { .onEach { require(Files.exists(it)) { "Input path must exist!" } } .toList() } - val documentationPath: Path + + override val documentationPath: Path get() = Paths.get( checkNotNull(documentation) { "Documentation output path was not initialized by jcommander!" } ) - val configPath: Path + override val configPath: Path get() = Paths.get(checkNotNull(config) { "Configuration output path was not initialized by jcommander!" }) - val cliOptionsPath: Path + override val cliOptionsPath: Path get() = Paths.get( checkNotNull(cliOptions) { "Cli options output path was not initialized by jcommander!" } ) } + +class GeneratorArgsReduced : GeneratorArgs() { + + @Parameter( + names = ["--input", "-i"], + required = true, + description = "Input paths to analyze." + ) + private var input: String? = null + + @Parameter( + names = ["--config", "-c"], + required = true, + description = "Output path for generated detekt config." + ) + private var config: String? = null + + @Parameter( + names = ["--help", "-h"], + help = true, + description = "Shows the usage." + ) + var help: Boolean = false + + override val inputPath: List by lazy { + checkNotNull(input) { "Input parameter was not initialized by jcommander!" } + .splitToSequence(",", ";") + .map(String::trim) + .filter { it.isNotEmpty() } + .map { first -> Paths.get(first) } + .onEach { require(Files.exists(it)) { "Input path must exist!" } } + .toList() + } + + override val configPath: Path + get() = Paths.get(checkNotNull(config) { "Configuration output path was not initialized by jcommander!" }) +} diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/Main.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/Main.kt index d4b72ee1189d..7b7594968a15 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/Main.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/Main.kt @@ -8,7 +8,7 @@ import kotlin.system.exitProcess @Suppress("detekt.SpreadOperator") fun main(args: Array) { - val options = GeneratorArgs() + val options = GeneratorArgsFull() val parser = JCommander(options) parser.parse(*args) diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/ConfigPrinter.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/ConfigPrinter.kt index 9131be470888..bf19004e2e19 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/ConfigPrinter.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/ConfigPrinter.kt @@ -24,6 +24,13 @@ object ConfigPrinter : DocumentationPrinter> { } } + fun printCustomConfig(item: List): String { + return yaml { + item.sortedBy { it.ruleSet.name } + .forEach { printRuleSetPage(it) } + } + } + private fun defaultBuildConfiguration(): String = """ build: maxIssues: 0 diff --git a/detekt-gradle-plugin/build.gradle.kts b/detekt-gradle-plugin/build.gradle.kts index 4d3d04b9aa06..ca0904b3c0c8 100644 --- a/detekt-gradle-plugin/build.gradle.kts +++ b/detekt-gradle-plugin/build.gradle.kts @@ -5,7 +5,7 @@ plugins { idea alias(libs.plugins.pluginPublishing) // We use this published version of the Detekt plugin to self analyse this project. - id("io.gitlab.arturbosch.detekt") version "1.20.0" + id("io.gitlab.arturbosch.detekt") version "1.21.0" id("org.gradle.test-retry") version "1.4.0" } @@ -59,6 +59,10 @@ val functionalTestImplementation: Configuration by configurations.getting configurations.compileOnly { extendsFrom(pluginCompileOnly) } +pluginCompileOnly.attributes { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category::class.java, "library")) +} + dependencies { compileOnly(libs.kotlin.gradlePluginApi) implementation(libs.sarif4k) @@ -71,7 +75,7 @@ dependencies { pluginCompileOnly(libs.kotlin.gradle) // We use this published version of the detekt-formatting to self analyse this project. - detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.20.0") + detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.21.0") } gradlePlugin { @@ -134,6 +138,10 @@ tasks { ideaModule { notCompatibleWithConfigurationCache("https://github.com/gradle/gradle/issues/13480") } + + publishPlugins { + notCompatibleWithConfigurationCache("https://github.com/gradle/gradle/issues/21283") + } } // Skip publishing of test fixture API & runtime variants diff --git a/detekt-gradle-plugin/src/functionalTest/kotlin/io/gitlab/arturbosch/detekt/DetektAndroidSpec.kt b/detekt-gradle-plugin/src/functionalTest/kotlin/io/gitlab/arturbosch/detekt/DetektAndroidSpec.kt index 5756abeb49a4..b7cffbb1f084 100644 --- a/detekt-gradle-plugin/src/functionalTest/kotlin/io/gitlab/arturbosch/detekt/DetektAndroidSpec.kt +++ b/detekt-gradle-plugin/src/functionalTest/kotlin/io/gitlab/arturbosch/detekt/DetektAndroidSpec.kt @@ -78,13 +78,23 @@ class DetektAndroidSpec { @DisplayName("task :app:detektTest") fun appDetektTest() { gradleRunner.runTasksAndCheckResult(":app:detektTest") { buildResult -> - assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-releaseUnitTest.xml """) - assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-debugUnitTest.xml """) - assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-debugAndroidTest.xml """) + assertThat(buildResult.output).containsPattern( + """--baseline \S*[/\\]detekt-baseline-releaseUnitTest.xml """ + ) + assertThat(buildResult.output).containsPattern( + """--baseline \S*[/\\]detekt-baseline-debugUnitTest.xml """ + ) + assertThat(buildResult.output).containsPattern( + """--baseline \S*[/\\]detekt-baseline-debugAndroidTest.xml """ + ) assertThat(buildResult.output).containsPattern("""--input \S*[/\\]app[/\\]src[/\\]test[/\\]java""") - assertThat(buildResult.output).containsPattern("""--input \S*[/\\]app[/\\]src[/\\]androidTest[/\\]java""") + assertThat(buildResult.output).containsPattern( + """--input \S*[/\\]app[/\\]src[/\\]androidTest[/\\]java""" + ) assertThat(buildResult.output).containsPattern("""--input \S*[/\\]app[/\\]src[/\\]test[/\\]kotlin""") - assertThat(buildResult.output).containsPattern("""--input \S*[/\\]app[/\\]src[/\\]androidTest[/\\]kotlin""") + assertThat(buildResult.output).containsPattern( + """--input \S*[/\\]app[/\\]src[/\\]androidTest[/\\]kotlin""" + ) assertThat(buildResult.output).contains("--report xml:") assertThat(buildResult.output).contains("--report sarif:") assertThat(buildResult.output).doesNotContain("--report txt:") @@ -190,9 +200,15 @@ class DetektAndroidSpec { @DisplayName("task :lib:detektTest") fun libDetektTest() { gradleRunner.runTasksAndCheckResult(":lib:detektTest") { buildResult -> - assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-releaseUnitTest.xml """) - assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-debugUnitTest.xml """) - assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-debugAndroidTest.xml """) + assertThat(buildResult.output).containsPattern( + """--baseline \S*[/\\]detekt-baseline-releaseUnitTest.xml """ + ) + assertThat(buildResult.output).containsPattern( + """--baseline \S*[/\\]detekt-baseline-debugUnitTest.xml """ + ) + assertThat(buildResult.output).containsPattern( + """--baseline \S*[/\\]detekt-baseline-debugAndroidTest.xml """ + ) assertThat(buildResult.output).contains("--report xml:") assertThat(buildResult.output).contains("--report sarif:") assertThat(buildResult.output).doesNotContain("--report txt:") diff --git a/detekt-gradle-plugin/src/functionalTest/kotlin/io/gitlab/arturbosch/detekt/DetektJvmSpec.kt b/detekt-gradle-plugin/src/functionalTest/kotlin/io/gitlab/arturbosch/detekt/DetektJvmSpec.kt index e84160584219..5e0b082d6a6c 100644 --- a/detekt-gradle-plugin/src/functionalTest/kotlin/io/gitlab/arturbosch/detekt/DetektJvmSpec.kt +++ b/detekt-gradle-plugin/src/functionalTest/kotlin/io/gitlab/arturbosch/detekt/DetektJvmSpec.kt @@ -40,7 +40,9 @@ class DetektJvmSpec { @Test fun `logs a warning`() { gradleRunner.runTasksAndCheckResult(":detektMain") { buildResult -> - assertThat(buildResult.output).contains("TXT report location set on detekt {} extension will be ignored for detektMain task.") + assertThat(buildResult.output).contains( + "TXT report location set on detekt {} extension will be ignored for detektMain task." + ) } } } @@ -72,7 +74,9 @@ class DetektJvmSpec { @Test fun `logs a warning`() { gradleRunner.runTasksAndCheckResult(":detektMain") { buildResult -> - assertThat(buildResult.output).doesNotContain("report location set on detekt {} extension will be ignored") + assertThat(buildResult.output).doesNotContain( + "report location set on detekt {} extension will be ignored" + ) } } } diff --git a/detekt-gradle-plugin/src/functionalTest/kotlin/io/gitlab/arturbosch/detekt/DetektMultiplatformSpec.kt b/detekt-gradle-plugin/src/functionalTest/kotlin/io/gitlab/arturbosch/detekt/DetektMultiplatformSpec.kt index e72d0197d726..9f1289d15aaa 100644 --- a/detekt-gradle-plugin/src/functionalTest/kotlin/io/gitlab/arturbosch/detekt/DetektMultiplatformSpec.kt +++ b/detekt-gradle-plugin/src/functionalTest/kotlin/io/gitlab/arturbosch/detekt/DetektMultiplatformSpec.kt @@ -257,7 +257,10 @@ class DetektMultiplatformSpec { @Nested @EnabledOnOs(MAC) - @EnabledIf("io.gitlab.arturbosch.detekt.DetektMultiplatformSpecKt#isXCodeInstalled", disabledReason = "XCode is not installed.") + @EnabledIf( + "io.gitlab.arturbosch.detekt.DetektMultiplatformSpecKt#isXCodeInstalled", + disabledReason = "XCode is not installed." + ) inner class `multiplatform projects - iOS target` { val gradleRunner = setupProject { diff --git a/detekt-gradle-plugin/src/functionalTest/kotlin/io/gitlab/arturbosch/detekt/GradleVersionSpec.kt b/detekt-gradle-plugin/src/functionalTest/kotlin/io/gitlab/arturbosch/detekt/GradleVersionSpec.kt index 211d91501d8a..f876aa45f08b 100644 --- a/detekt-gradle-plugin/src/functionalTest/kotlin/io/gitlab/arturbosch/detekt/GradleVersionSpec.kt +++ b/detekt-gradle-plugin/src/functionalTest/kotlin/io/gitlab/arturbosch/detekt/GradleVersionSpec.kt @@ -6,13 +6,13 @@ import org.gradle.testkit.runner.TaskOutcome import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.junit.jupiter.api.condition.EnabledForJreRange -import org.junit.jupiter.api.condition.JRE.JAVA_13 +import org.junit.jupiter.api.condition.JRE.JAVA_15 class GradleVersionSpec { @Test @DisplayName("Runs on version $gradleVersion") - @EnabledForJreRange(max = JAVA_13, disabledReason = "Gradle $gradleVersion unsupported on this Java version") + @EnabledForJreRange(max = JAVA_15, disabledReason = "Gradle $gradleVersion unsupported on this Java version") fun runsOnOldestSupportedGradleVersion() { val builder = DslTestBuilder.kotlin() val gradleRunner = builder.withGradleVersion(gradleVersion).build() @@ -22,6 +22,6 @@ class GradleVersionSpec { } companion object { - const val gradleVersion = "6.1" + const val gradleVersion = "6.7.1" } } diff --git a/detekt-gradle-plugin/src/functionalTest/kotlin/io/gitlab/arturbosch/detekt/JvmSpec.kt b/detekt-gradle-plugin/src/functionalTest/kotlin/io/gitlab/arturbosch/detekt/JvmSpec.kt index 307946fe1ce6..b6a614bd8ded 100644 --- a/detekt-gradle-plugin/src/functionalTest/kotlin/io/gitlab/arturbosch/detekt/JvmSpec.kt +++ b/detekt-gradle-plugin/src/functionalTest/kotlin/io/gitlab/arturbosch/detekt/JvmSpec.kt @@ -15,7 +15,9 @@ class JvmSpec { .buildAndFail() assertThat(result.output).contains("failed with 2 weighted issues.") - assertThat(result.output).contains("Do not directly exit the process outside the `main` function. Throw an exception(...)") + assertThat(result.output).contains( + "Do not directly exit the process outside the `main` function. Throw an exception(...)" + ) assertThat(result.output).contains("Errors.kt:7:9") assertThat(result.output).contains("Errors.kt:12:16") } diff --git a/detekt-gradle-plugin/src/main/kotlin/io/gitlab/arturbosch/detekt/DetektGenerateCustomConfigTask.kt b/detekt-gradle-plugin/src/main/kotlin/io/gitlab/arturbosch/detekt/DetektGenerateCustomConfigTask.kt new file mode 100644 index 000000000000..bc97f3d0fb94 --- /dev/null +++ b/detekt-gradle-plugin/src/main/kotlin/io/gitlab/arturbosch/detekt/DetektGenerateCustomConfigTask.kt @@ -0,0 +1,87 @@ +package io.gitlab.arturbosch.detekt + +import io.gitlab.arturbosch.detekt.DetektPlugin.Companion.CONFIG_DIR_NAME +import io.gitlab.arturbosch.detekt.DetektPlugin.Companion.SOURCE_DIR_NAME +import io.gitlab.arturbosch.detekt.invoke.CliArgument +import io.gitlab.arturbosch.detekt.invoke.ConfigArgument +import io.gitlab.arturbosch.detekt.invoke.DetektInvoker +import io.gitlab.arturbosch.detekt.invoke.GenerateCustomConfigArgument +import io.gitlab.arturbosch.detekt.invoke.InputArgument +import io.gitlab.arturbosch.detekt.invoke.isDryRunEnabled +import org.gradle.api.DefaultTask +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.Classpath +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.TaskAction +import org.gradle.language.base.plugins.LifecycleBasePlugin +import javax.inject.Inject + +@CacheableTask +open class DetektGenerateCustomConfigTask @Inject constructor( + private val objects: ObjectFactory +) : DefaultTask() { + + init { + description = "Generate configuration file for user-defined rules." + group = LifecycleBasePlugin.VERIFICATION_GROUP + } + + @get:Classpath + val detektClasspath: ConfigurableFileCollection = project.objects.fileCollection() + + @get:Classpath + val pluginClasspath: ConfigurableFileCollection = objects.fileCollection() + + @get:InputFiles + @get:Optional + @get:PathSensitive(PathSensitivity.RELATIVE) + val config: ConfigurableFileCollection = project.objects.fileCollection() + + @get:InputFiles + @get:Optional + @get:PathSensitive(PathSensitivity.RELATIVE) + val source: ConfigurableFileCollection = project.objects.fileCollection() + + private val isDryRun: Boolean = project.isDryRunEnabled() + + private val defaultConfigPath = project.rootDir.toPath().resolve(CONFIG_DIR_NAME) + + private val configurationToUse = if (config.isEmpty) { + objects.fileCollection().from(defaultConfigPath) + } else { + config + } + + private val defaultSourcePath = project.rootDir.toPath().resolve(SOURCE_DIR_NAME) + + private val sourceToUse = if (source.isEmpty) { + objects.fileCollection().from(defaultSourcePath) + } else { + source + } + + @get:Internal + internal val arguments: Provider> = project.provider { + listOf( + GenerateCustomConfigArgument, + InputArgument(sourceToUse), + ConfigArgument(configurationToUse.last()) + ).flatMap(CliArgument::toArgument) + } + + @TaskAction + fun generateCustomConfig() { + DetektInvoker.create(task = this, isDryRun = isDryRun).invokeCli( + arguments = arguments.get(), + classpath = detektClasspath.plus(pluginClasspath), + taskName = name, + ) + } +} diff --git a/detekt-gradle-plugin/src/main/kotlin/io/gitlab/arturbosch/detekt/DetektPlugin.kt b/detekt-gradle-plugin/src/main/kotlin/io/gitlab/arturbosch/detekt/DetektPlugin.kt index 38f02436b124..edc06c272f70 100644 --- a/detekt-gradle-plugin/src/main/kotlin/io/gitlab/arturbosch/detekt/DetektPlugin.kt +++ b/detekt-gradle-plugin/src/main/kotlin/io/gitlab/arturbosch/detekt/DetektPlugin.kt @@ -35,6 +35,7 @@ class DetektPlugin : Plugin { project.registerDetektMultiplatformTasks(extension) } project.registerGenerateConfigTask(extension) + project.registerGenerateCustomConfigTask(extension) } private fun Project.registerDetektJvmTasks(extension: DetektExtension) { @@ -65,6 +66,12 @@ class DetektPlugin : Plugin { } } + private fun Project.registerGenerateCustomConfigTask(extension: DetektExtension) { + tasks.register(GENERATE_CUSTOM_CONFIG, DetektGenerateCustomConfigTask::class.java) { + it.config.setFrom(project.provider { extension.config }) + } + } + private fun configurePluginDependencies(project: Project, extension: DetektExtension) { project.configurations.create(CONFIGURATION_DETEKT_PLUGINS) { configuration -> configuration.isVisible = false @@ -99,6 +106,11 @@ class DetektPlugin : Plugin { it.detektClasspath.setFrom(project.configurations.getAt(CONFIGURATION_DETEKT)) it.pluginClasspath.setFrom(project.configurations.getAt(CONFIGURATION_DETEKT_PLUGINS)) } + + project.tasks.withType(DetektGenerateCustomConfigTask::class.java).configureEach { + it.detektClasspath.setFrom(project.configurations.getAt(CONFIGURATION_DETEKT)) + it.pluginClasspath.setFrom(project.configurations.getAt(CONFIGURATION_DETEKT_PLUGINS)) + } } companion object { @@ -106,9 +118,11 @@ class DetektPlugin : Plugin { const val BASELINE_TASK_NAME = "detektBaseline" const val DETEKT_EXTENSION = "detekt" private const val GENERATE_CONFIG = "detektGenerateConfig" + private const val GENERATE_CUSTOM_CONFIG = "detektGenerateCustomConfig" internal val defaultExcludes = listOf("build/") internal val defaultIncludes = listOf("**/*.kt", "**/*.kts") internal const val CONFIG_DIR_NAME = "config/detekt" + internal const val SOURCE_DIR_NAME = "detekt-rules-complexity" internal const val CONFIG_FILE = "detekt.yml" internal const val DETEKT_ANDROID_DISABLED_PROPERTY = "detekt.android.disabled" diff --git a/detekt-gradle-plugin/src/main/kotlin/io/gitlab/arturbosch/detekt/internal/DetektMultiplatform.kt b/detekt-gradle-plugin/src/main/kotlin/io/gitlab/arturbosch/detekt/internal/DetektMultiplatform.kt index 06319abf0c9b..e9e456b356d2 100644 --- a/detekt-gradle-plugin/src/main/kotlin/io/gitlab/arturbosch/detekt/internal/DetektMultiplatform.kt +++ b/detekt-gradle-plugin/src/main/kotlin/io/gitlab/arturbosch/detekt/internal/DetektMultiplatform.kt @@ -116,7 +116,8 @@ internal class DetektMultiplatform(private val project: Project) { } registerCreateBaselineTask( - DetektPlugin.BASELINE_TASK_NAME + taskSuffix, extension + DetektPlugin.BASELINE_TASK_NAME + taskSuffix, + extension ) { setSource(inputSource) if (runWithTypeResolution) { diff --git a/detekt-gradle-plugin/src/main/kotlin/io/gitlab/arturbosch/detekt/invoke/CliArgument.kt b/detekt-gradle-plugin/src/main/kotlin/io/gitlab/arturbosch/detekt/invoke/CliArgument.kt index f44401694ae3..b68adfe785ae 100644 --- a/detekt-gradle-plugin/src/main/kotlin/io/gitlab/arturbosch/detekt/invoke/CliArgument.kt +++ b/detekt-gradle-plugin/src/main/kotlin/io/gitlab/arturbosch/detekt/invoke/CliArgument.kt @@ -17,6 +17,7 @@ private const val FAIL_FAST_PARAMETER = "--fail-fast" private const val ALL_RULES_PARAMETER = "--all-rules" private const val REPORT_PARAMETER = "--report" private const val GENERATE_CONFIG_PARAMETER = "--generate-config" +private const val GENERATE_CUSTOM_CONFIG_PARAMETER = "--generate-custom-config" private const val CREATE_BASELINE_PARAMETER = "--create-baseline" private const val CLASSPATH_PARAMETER = "--classpath" private const val LANGUAGE_VERSION_PARAMETER = "--language-version" @@ -35,6 +36,10 @@ internal object GenerateConfigArgument : CliArgument() { override fun toArgument() = listOf(GENERATE_CONFIG_PARAMETER) } +internal object GenerateCustomConfigArgument : CliArgument() { + override fun toArgument() = listOf(GENERATE_CUSTOM_CONFIG_PARAMETER) +} + internal data class InputArgument(val fileCollection: FileCollection) : CliArgument() { override fun toArgument() = listOf(INPUT_PARAMETER, fileCollection.joinToString(",") { it.absolutePath }) } diff --git a/detekt-gradle-plugin/src/test/kotlin/io/gitlab/arturbosch/detekt/DetektJvmSpec.kt b/detekt-gradle-plugin/src/test/kotlin/io/gitlab/arturbosch/detekt/DetektJvmSpec.kt index d3a244d698b1..8b99d3c8c064 100644 --- a/detekt-gradle-plugin/src/test/kotlin/io/gitlab/arturbosch/detekt/DetektJvmSpec.kt +++ b/detekt-gradle-plugin/src/test/kotlin/io/gitlab/arturbosch/detekt/DetektJvmSpec.kt @@ -19,7 +19,6 @@ class DetektJvmSpec { apply() repositories { mavenCentral() - mavenLocal() } tasks.withType(Detekt::class.java).configureEach { it.reports { reports -> diff --git a/detekt-gradle-plugin/src/test/kotlin/io/gitlab/arturbosch/detekt/DetektPlainSpec.kt b/detekt-gradle-plugin/src/test/kotlin/io/gitlab/arturbosch/detekt/DetektPlainSpec.kt index 382c80ba8916..e5a9f4963887 100644 --- a/detekt-gradle-plugin/src/test/kotlin/io/gitlab/arturbosch/detekt/DetektPlainSpec.kt +++ b/detekt-gradle-plugin/src/test/kotlin/io/gitlab/arturbosch/detekt/DetektPlainSpec.kt @@ -25,7 +25,6 @@ class DetektPlainSpec { repositories { mavenCentral() - mavenLocal() } configure { @@ -53,7 +52,6 @@ class DetektPlainSpec { repositories { mavenCentral() - mavenLocal() } tasks.withType(Detekt::class.java).configureEach { diff --git a/detekt-gradle-plugin/src/testFixtures/kotlin/io/gitlab/arturbosch/detekt/testkit/DslGradleRunner.kt b/detekt-gradle-plugin/src/testFixtures/kotlin/io/gitlab/arturbosch/detekt/testkit/DslGradleRunner.kt index c28b56a4cae6..1b69a89b2d0c 100644 --- a/detekt-gradle-plugin/src/testFixtures/kotlin/io/gitlab/arturbosch/detekt/testkit/DslGradleRunner.kt +++ b/detekt-gradle-plugin/src/testFixtures/kotlin/io/gitlab/arturbosch/detekt/testkit/DslGradleRunner.kt @@ -9,7 +9,8 @@ import java.nio.file.Files import java.util.UUID @Suppress("TooManyFunctions", "ClassOrdering") -class DslGradleRunner @Suppress("LongParameterList") constructor( +class DslGradleRunner @Suppress("LongParameterList") +constructor( val projectLayout: ProjectLayout, val buildFileName: String, val mainBuildFileContent: String = "", @@ -18,7 +19,7 @@ class DslGradleRunner @Suppress("LongParameterList") constructor( val gradleVersionOrNone: String? = null, val dryRun: Boolean = false, val jvmArgs: String = "-Xmx2g -XX:MaxMetaspaceSize=1g", - val projectScript: Project.() -> Unit = {}, + val projectScript: Project.() -> Unit = {} ) { private val rootDir: File = Files.createTempDirectory("applyPlugin").toFile().apply { deleteOnExit() } diff --git a/detekt-metrics/src/main/kotlin/io/github/detekt/metrics/LinesOfCode.kt b/detekt-metrics/src/main/kotlin/io/github/detekt/metrics/LinesOfCode.kt index d93f7b51e3a0..398a42066947 100644 --- a/detekt-metrics/src/main/kotlin/io/github/detekt/metrics/LinesOfCode.kt +++ b/detekt-metrics/src/main/kotlin/io/github/detekt/metrics/LinesOfCode.kt @@ -2,6 +2,7 @@ package io.github.detekt.metrics +import io.github.detekt.psi.getLineAndColumnInPsiFile import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiComment import org.jetbrains.kotlin.com.intellij.psi.PsiElement @@ -10,7 +11,6 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiCommentImpl import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiCoreCommentImpl import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl -import org.jetbrains.kotlin.diagnostics.DiagnosticUtils import org.jetbrains.kotlin.kdoc.psi.api.KDoc import org.jetbrains.kotlin.kdoc.psi.api.KDocElement import org.jetbrains.kotlin.kdoc.psi.impl.KDocElementImpl @@ -44,16 +44,7 @@ fun KtElement.linesOfCode(inFile: KtFile = this.containingKtFile): Int = .distinct() .count() -fun ASTNode.line(inFile: KtFile): Int = try { - DiagnosticUtils.getLineAndColumnInPsiFile(inFile, this.textRange).line -} catch (@Suppress("SwallowedException", "TooGenericExceptionCaught") e: IndexOutOfBoundsException) { - // When auto-correctable rules performs actual mutation, KtFile.text is updated but - // KtFile.viewProvider.document is not updated. This will cause crash in subsequent rules - // if they are using any function relying on the KtFile.viewProvider.document. - // The exception is silenced to return -1 while we should seek long-term solution for execution - // order of rules (#3445) - -1 -} +fun ASTNode.line(inFile: KtFile): Int = getLineAndColumnInPsiFile(inFile, this.textRange)?.line ?: -1 private val comments: Set> = setOf( PsiWhiteSpace::class.java, diff --git a/detekt-parser/src/main/kotlin/io/github/detekt/parser/KotlinEnvironmentUtils.kt b/detekt-parser/src/main/kotlin/io/github/detekt/parser/KotlinEnvironmentUtils.kt index b96a22961080..cbc3d4c94f7d 100644 --- a/detekt-parser/src/main/kotlin/io/github/detekt/parser/KotlinEnvironmentUtils.kt +++ b/detekt-parser/src/main/kotlin/io/github/detekt/parser/KotlinEnvironmentUtils.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoots import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots +import org.jetbrains.kotlin.cli.jvm.config.configureJdkClasspathRoots import org.jetbrains.kotlin.com.intellij.mock.MockProject import org.jetbrains.kotlin.com.intellij.openapi.Disposable import org.jetbrains.kotlin.com.intellij.openapi.util.Disposer @@ -98,6 +99,7 @@ fun createCompilerConfiguration( addJavaSourceRoots(javaFiles) addKotlinSourceRoots(kotlinFiles) addJvmClasspathRoots(classpathFiles) + configureJdkClasspathRoots() } } diff --git a/detekt-psi-utils/api/detekt-psi-utils.api b/detekt-psi-utils/api/detekt-psi-utils.api index cdb632ca8a20..e73a4ff57bb4 100644 --- a/detekt-psi-utils/api/detekt-psi-utils.api +++ b/detekt-psi-utils/api/detekt-psi-utils.api @@ -33,6 +33,7 @@ public final class io/github/detekt/psi/KtFilesKt { public static final fun basePath (Lorg/jetbrains/kotlin/com/intellij/psi/PsiFile;)Ljava/nio/file/Path; public static final fun fileNameWithoutSuffix (Lorg/jetbrains/kotlin/com/intellij/psi/PsiFile;)Ljava/lang/String; public static final fun getFileName (Lorg/jetbrains/kotlin/com/intellij/psi/PsiFile;)Ljava/lang/String; + public static final fun getLineAndColumnInPsiFile (Lorg/jetbrains/kotlin/com/intellij/psi/PsiFile;Lorg/jetbrains/kotlin/com/intellij/openapi/util/TextRange;)Lorg/jetbrains/kotlin/diagnostics/PsiDiagnosticUtils$LineAndColumn; public static final fun relativePath (Lorg/jetbrains/kotlin/com/intellij/psi/PsiFile;)Ljava/nio/file/Path; public static final fun toFilePath (Lorg/jetbrains/kotlin/com/intellij/psi/PsiFile;)Lio/github/detekt/psi/FilePath; public static final fun toUnifiedString (Ljava/nio/file/Path;)Ljava/lang/String; @@ -106,6 +107,7 @@ public final class io/gitlab/arturbosch/detekt/rules/KtModifierListKt { public static final fun isOverride (Lorg/jetbrains/kotlin/psi/KtModifierListOwner;)Z public static final fun isProtected (Lorg/jetbrains/kotlin/psi/KtModifierListOwner;)Z public static final fun isPublicNotOverridden (Lorg/jetbrains/kotlin/psi/KtModifierListOwner;)Z + public static final fun isPublicNotOverridden (Lorg/jetbrains/kotlin/psi/KtModifierListOwner;Z)Z } public final class io/gitlab/arturbosch/detekt/rules/KtValueArgumentKt { @@ -134,6 +136,7 @@ public final class io/gitlab/arturbosch/detekt/rules/ThrowExtensionsKt { public final class io/gitlab/arturbosch/detekt/rules/TraversingKt { public static final fun isPublicInherited (Lorg/jetbrains/kotlin/psi/KtNamedDeclaration;)Z + public static final fun isPublicInherited (Lorg/jetbrains/kotlin/psi/KtNamedDeclaration;Z)Z } public final class io/gitlab/arturbosch/detekt/rules/TypeUtilsKt { diff --git a/detekt-psi-utils/build.gradle.kts b/detekt-psi-utils/build.gradle.kts index b3581319b20e..124b2ab9ed4d 100644 --- a/detekt-psi-utils/build.gradle.kts +++ b/detekt-psi-utils/build.gradle.kts @@ -10,6 +10,10 @@ dependencies { testImplementation(projects.detektTest) } +tasks.apiDump { + notCompatibleWithConfigurationCache("https://github.com/Kotlin/binary-compatibility-validator/issues/95") +} + apiValidation { ignoredPackages.add("io.github.detekt.psi.internal") } diff --git a/detekt-psi-utils/src/main/kotlin/io/github/detekt/psi/KtFiles.kt b/detekt-psi-utils/src/main/kotlin/io/github/detekt/psi/KtFiles.kt index 8b4b2be69c17..70f06268733e 100644 --- a/detekt-psi-utils/src/main/kotlin/io/github/detekt/psi/KtFiles.kt +++ b/detekt-psi-utils/src/main/kotlin/io/github/detekt/psi/KtFiles.kt @@ -1,6 +1,9 @@ package io.github.detekt.psi +import org.jetbrains.kotlin.com.intellij.openapi.util.TextRange import org.jetbrains.kotlin.com.intellij.psi.PsiFile +import org.jetbrains.kotlin.diagnostics.DiagnosticUtils +import org.jetbrains.kotlin.diagnostics.PsiDiagnosticUtils import java.io.File import java.nio.file.Path import java.nio.file.Paths @@ -68,6 +71,14 @@ fun PsiFile.toFilePath(): FilePath { } } +// #3317 If any rule mutates the PsiElement, searching the original PsiElement may throw an exception. +fun getLineAndColumnInPsiFile(file: PsiFile, range: TextRange): PsiDiagnosticUtils.LineAndColumn? { + return runCatching { + @Suppress("ForbiddenMethodCall") + DiagnosticUtils.getLineAndColumnInPsiFile(file, range) + }.getOrNull() +} + /** * Returns a system-independent string with UNIX system file separator. */ diff --git a/detekt-psi-utils/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/KtModifierList.kt b/detekt-psi-utils/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/KtModifierList.kt index ea30d74838c8..97204905312d 100644 --- a/detekt-psi-utils/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/KtModifierList.kt +++ b/detekt-psi-utils/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/KtModifierList.kt @@ -7,7 +7,14 @@ import org.jetbrains.kotlin.psi.KtModifierListOwner import org.jetbrains.kotlin.psi.psiUtil.isPublic fun KtModifierListOwner.isPublicNotOverridden() = - isPublic && !isOverride() + isPublicNotOverridden(false) + +fun KtModifierListOwner.isPublicNotOverridden(considerProtectedAsPublic: Boolean) = + if (considerProtectedAsPublic) { + isPublic || isProtected() + } else { + isPublic + } && !isOverride() fun KtModifierListOwner.isAbstract() = hasModifier(KtTokens.ABSTRACT_KEYWORD) diff --git a/detekt-psi-utils/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/Traversing.kt b/detekt-psi-utils/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/Traversing.kt index 5141bdc96975..fec929f3c1e6 100644 --- a/detekt-psi-utils/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/Traversing.kt +++ b/detekt-psi-utils/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/Traversing.kt @@ -20,10 +20,12 @@ inline fun KtElement.parentsOfTyp } } -fun KtNamedDeclaration.isPublicInherited(): Boolean { +fun KtNamedDeclaration.isPublicInherited(): Boolean = isPublicInherited(false) + +fun KtNamedDeclaration.isPublicInherited(considerProtectedAsPublic: Boolean): Boolean { var classOrObject = containingClassOrObject while (classOrObject != null) { - if (!classOrObject.isPublic) { + if (!classOrObject.isPublic && !(considerProtectedAsPublic && classOrObject.isProtected())) { return false } classOrObject = classOrObject.containingClassOrObject diff --git a/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/ComplexMethodSpec.kt b/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/ComplexMethodSpec.kt index e66fc783ab73..c5027ee7eba8 100644 --- a/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/ComplexMethodSpec.kt +++ b/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/ComplexMethodSpec.kt @@ -129,7 +129,7 @@ class ComplexMethodSpec { ) val subject = ComplexMethod(config) - assertThat(subject.lint(path)).hasSourceLocations(SourceLocation(43, 5)) + assertThat(subject.lint(path)).hasStartSourceLocations(SourceLocation(43, 5)) } @Test @@ -137,7 +137,7 @@ class ComplexMethodSpec { val config = TestConfig(mapOf("threshold" to "4")) val subject = ComplexMethod(config) - assertThat(subject.lint(path)).hasSourceLocations( + assertThat(subject.lint(path)).hasStartSourceLocations( SourceLocation(6, 5), SourceLocation(15, 5), SourceLocation(25, 5), @@ -235,7 +235,7 @@ class ComplexMethodSpec { private fun assertExpectedComplexityValue(code: String, config: TestConfig, expectedValue: Int) { val findings = ComplexMethod(config).lint(code) - assertThat(findings).hasSourceLocations(SourceLocation(1, 5)) + assertThat(findings).hasStartSourceLocations(SourceLocation(1, 5)) assertThat(findings.first()) .isThresholded() diff --git a/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/LargeClassSpec.kt b/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/LargeClassSpec.kt index e8cc3e4b6f05..641b90d38ced 100644 --- a/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/LargeClassSpec.kt +++ b/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/LargeClassSpec.kt @@ -16,7 +16,7 @@ class LargeClassSpec { fun `should detect only the nested large class which exceeds threshold 70`() { val findings = subject(threshold = 70).lint(resourceAsPath("NestedClasses.kt")) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocations(SourceLocation(12, 15)) + assertThat(findings).hasStartSourceLocations(SourceLocation(12, 15)) } @Test diff --git a/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt b/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt index df1817d52b59..583ab5dd918f 100644 --- a/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt +++ b/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt @@ -116,7 +116,7 @@ class NestedScopeFunctionsSpec(private val env: KotlinCoreEnvironment) { } private fun expectSourceLocation(location: Pair) { - assertThat(actual).hasSourceLocation(location.first, location.second) + assertThat(actual).hasStartSourceLocation(location.first, location.second) } private fun expectFunctionInMsg(scopeFunction: String) { diff --git a/detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicClass.kt b/detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicClass.kt index 70dddc21f57c..c9f971d32852 100644 --- a/detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicClass.kt +++ b/detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicClass.kt @@ -44,6 +44,9 @@ class UndocumentedPublicClass(config: Config = Config.empty) : Rule(config) { @Configuration("if inner interfaces should be searched") private val searchInInnerInterface: Boolean by config(true) + @Configuration("if protected classes should be searched") + private val searchInProtectedClass: Boolean by config(false) + override fun visitClass(klass: KtClass) { if (requiresDocumentation(klass)) { reportIfUndocumented(klass) @@ -81,7 +84,7 @@ class UndocumentedPublicClass(config: Config = Config.empty) : Rule(config) { } private fun isPublicAndPublicInherited(element: KtClassOrObject) = - element.isPublicInherited() && element.isPublicNotOverridden() + element.isPublicInherited(searchInProtectedClass) && element.isPublicNotOverridden(searchInProtectedClass) private fun KtObjectDeclaration.isCompanionWithoutName() = isCompanion() && nameAsSafeName.asString() == "Companion" diff --git a/detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicFunction.kt b/detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicFunction.kt index df1691573dfc..c229e25ce076 100644 --- a/detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicFunction.kt +++ b/detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicFunction.kt @@ -7,6 +7,9 @@ import io.gitlab.arturbosch.detekt.api.Entity import io.gitlab.arturbosch.detekt.api.Issue import io.gitlab.arturbosch.detekt.api.Rule import io.gitlab.arturbosch.detekt.api.Severity +import io.gitlab.arturbosch.detekt.api.config +import io.gitlab.arturbosch.detekt.api.internal.Configuration +import io.gitlab.arturbosch.detekt.rules.isProtected import io.gitlab.arturbosch.detekt.rules.isPublicNotOverridden import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtNamedFunction @@ -27,6 +30,9 @@ class UndocumentedPublicFunction(config: Config = Config.empty) : Rule(config) { Debt.TWENTY_MINS ) + @Configuration("if protected functions should be searched") + private val searchProtectedFunction: Boolean by config(false) + override fun visitNamedFunction(function: KtNamedFunction) { if (function.funKeyword == null && function.isLocal) return @@ -42,5 +48,9 @@ class UndocumentedPublicFunction(config: Config = Config.empty) : Rule(config) { } private fun KtNamedFunction.shouldBeDocumented() = - parents.filterIsInstance().all { it.isPublic } && isPublicNotOverridden() + if (searchProtectedFunction) { + parents.filterIsInstance().all { it.isPublic || it.isProtected() } + } else { + parents.filterIsInstance().all { it.isPublic } + } && isPublicNotOverridden(searchProtectedFunction) } diff --git a/detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicProperty.kt b/detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicProperty.kt index 361d8c8c6381..62edb37efd03 100644 --- a/detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicProperty.kt +++ b/detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicProperty.kt @@ -7,6 +7,8 @@ import io.gitlab.arturbosch.detekt.api.Entity import io.gitlab.arturbosch.detekt.api.Issue import io.gitlab.arturbosch.detekt.api.Rule import io.gitlab.arturbosch.detekt.api.Severity +import io.gitlab.arturbosch.detekt.api.config +import io.gitlab.arturbosch.detekt.api.internal.Configuration import io.gitlab.arturbosch.detekt.rules.isPublicInherited import io.gitlab.arturbosch.detekt.rules.isPublicNotOverridden import org.jetbrains.kotlin.psi.KtNamedDeclaration @@ -31,6 +33,9 @@ class UndocumentedPublicProperty(config: Config = Config.empty) : Rule(config) { Debt.TWENTY_MINS ) + @Configuration("if protected functions should be searched") + private val searchProtectedProperty: Boolean by config(false) + override fun visitPrimaryConstructor(constructor: KtPrimaryConstructor) { if (constructor.isPublicInherited()) { val comment = constructor.containingClassOrObject?.docComment?.text @@ -43,7 +48,7 @@ class UndocumentedPublicProperty(config: Config = Config.empty) : Rule(config) { } override fun visitProperty(property: KtProperty) { - if (property.isPublicInherited() && !property.isLocal && property.shouldBeDocumented()) { + if (property.isPublicInherited(searchProtectedProperty) && !property.isLocal && property.shouldBeDocumented()) { report(property) } super.visitProperty(property) @@ -58,7 +63,7 @@ class UndocumentedPublicProperty(config: Config = Config.empty) : Rule(config) { } private fun KtProperty.shouldBeDocumented() = - docComment == null && isTopLevelOrInPublicClass() && isPublicNotOverridden() + docComment == null && isTopLevelOrInPublicClass() && isPublicNotOverridden(searchProtectedProperty) private fun KtProperty.isTopLevelOrInPublicClass() = isTopLevel || containingClassOrObject?.isPublic == true diff --git a/detekt-rules-documentation/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicClassSpec.kt b/detekt-rules-documentation/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicClassSpec.kt index 894ab5ad5cc2..9cd06e0c8e85 100644 --- a/detekt-rules-documentation/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicClassSpec.kt +++ b/detekt-rules-documentation/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicClassSpec.kt @@ -9,6 +9,7 @@ private const val SEARCH_IN_NESTED_CLASS = "searchInNestedClass" private const val SEARCH_IN_INNER_CLASS = "searchInInnerClass" private const val SEARCH_IN_INNER_OBJECT = "searchInInnerObject" private const val SEARCH_IN_INNER_INTERFACE = "searchInInnerInterface" +private const val SEARCH_IN_PROTECTED_CLASS = "searchInProtectedClass" class UndocumentedPublicClassSpec { val subject = UndocumentedPublicClass() @@ -233,4 +234,23 @@ class UndocumentedPublicClassSpec { """ assertThat(subject.compileAndLint(code)).isEmpty() } + + @Test + fun `does not report protected class by default`() { + val code = """ + protected class Test { + } + """ + assertThat(subject.compileAndLint(code)).isEmpty() + } + + @Test + fun `reports protected class if configured`() { + val code = """ + protected class Test { + } + """ + val subject = UndocumentedPublicClass(TestConfig(mapOf(SEARCH_IN_PROTECTED_CLASS to "true"))) + assertThat(subject.compileAndLint(code)).hasSize(1) + } } diff --git a/detekt-rules-documentation/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicFunctionSpec.kt b/detekt-rules-documentation/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicFunctionSpec.kt index 86d255fec6dd..39f4167ff10e 100644 --- a/detekt-rules-documentation/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicFunctionSpec.kt +++ b/detekt-rules-documentation/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicFunctionSpec.kt @@ -1,10 +1,13 @@ package io.gitlab.arturbosch.detekt.rules.documentation +import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.compileAndLint import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test +private const val SEARCH_PROTECTED_FUN = "searchProtectedFunction" + class UndocumentedPublicFunctionSpec { val subject = UndocumentedPublicFunction() @@ -141,6 +144,27 @@ class UndocumentedPublicFunctionSpec { assertThat(subject.compileAndLint(code)).isEmpty() } + @Test + fun `does not report protected functions by default`() { + val code = """ + open class Test { + protected fun noComment1() {} + } + """ + assertThat(subject.compileAndLint(code)).isEmpty() + } + + @Test + fun `reports protected functions if configured`() { + val code = """ + open class Test { + protected fun noComment1() {} + } + """ + val subject = UndocumentedPublicFunction(TestConfig(mapOf(SEARCH_PROTECTED_FUN to "true"))) + assertThat(subject.compileAndLint(code)).hasSize(1) + } + @Nested inner class `nested class` { @Test diff --git a/detekt-rules-documentation/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicPropertySpec.kt b/detekt-rules-documentation/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicPropertySpec.kt index 053ea6c6f906..1e5fbe9bbbe9 100644 --- a/detekt-rules-documentation/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicPropertySpec.kt +++ b/detekt-rules-documentation/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/UndocumentedPublicPropertySpec.kt @@ -1,10 +1,13 @@ package io.gitlab.arturbosch.detekt.rules.documentation +import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.compileAndLint import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test +private const val SEARCH_PROTECTED_PROPERTY = "searchProtectedProperty" + class UndocumentedPublicPropertySpec { val subject = UndocumentedPublicProperty() @@ -213,6 +216,27 @@ class UndocumentedPublicPropertySpec { assertThat(subject.compileAndLint(code)).isEmpty() } + @Test + fun `does not report undocumented protected properties by default`() { + val code = """ + open class Test { + protected val a = 1 + } + """ + assertThat(subject.compileAndLint(code)).isEmpty() + } + + @Test + fun `reports undocumented protected properties if configured`() { + val code = """ + open class Test { + protected val a = 1 + } + """ + val subject = UndocumentedPublicProperty(TestConfig(mapOf(SEARCH_PROTECTED_PROPERTY to "true"))) + assertThat(subject.compileAndLint(code)).hasSize(1) + } + @Nested inner class `public properties in nested classes` { diff --git a/detekt-rules-empty/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/empty/EmptyFunctionBlockSpec.kt b/detekt-rules-empty/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/empty/EmptyFunctionBlockSpec.kt index 1c1ad463f84a..5254750568f5 100644 --- a/detekt-rules-empty/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/empty/EmptyFunctionBlockSpec.kt +++ b/detekt-rules-empty/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/empty/EmptyFunctionBlockSpec.kt @@ -21,7 +21,7 @@ class EmptyFunctionBlockSpec { protected fun stuff() {} } """ - assertThat(subject.compileAndLint(code)).hasSourceLocation(2, 27) + assertThat(subject.compileAndLint(code)).hasStartSourceLocation(2, 27) } @Test @@ -51,7 +51,7 @@ class EmptyFunctionBlockSpec { fun b() {} } """ - assertThat(subject.compileAndLint(code)).hasSourceLocation(2, 13) + assertThat(subject.compileAndLint(code)).hasStartSourceLocation(2, 13) } @Nested @@ -89,7 +89,7 @@ class EmptyFunctionBlockSpec { @Test fun `should not flag overridden functions`() { val config = TestConfig(mapOf(IGNORE_OVERRIDDEN_FUNCTIONS to "true")) - assertThat(EmptyFunctionBlock(config).compileAndLint(code)).hasSourceLocation(1, 13) + assertThat(EmptyFunctionBlock(config).compileAndLint(code)).hasStartSourceLocation(1, 13) } } @@ -115,7 +115,7 @@ class EmptyFunctionBlockSpec { @Test fun `should not flag overridden functions with commented body`() { - assertThat(subject.compileAndLint(code)).hasSourceLocation(12, 31) + assertThat(subject.compileAndLint(code)).hasStartSourceLocation(12, 31) } @Test diff --git a/detekt-rules-errorprone/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/ExitOutsideMain.kt b/detekt-rules-errorprone/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/ExitOutsideMain.kt index 451dd84b5196..d40c3c347a55 100644 --- a/detekt-rules-errorprone/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/ExitOutsideMain.kt +++ b/detekt-rules-errorprone/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/ExitOutsideMain.kt @@ -58,7 +58,7 @@ class ExitOutsideMain(config: Config = Config.empty) : Rule(config) { override fun visitCallExpression(expression: KtCallExpression) { super.visitCallExpression(expression) - if (context == BindingContext.EMPTY) return + if (bindingContext == BindingContext.EMPTY) return if (expression.getStrictParentOfType()?.isMainFunction() == true) return val fqName = expression.getResolvedCall(bindingContext)?.resultingDescriptor?.fqNameOrNull() ?: return diff --git a/detekt-rules-errorprone/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/UnusedUnaryOperator.kt b/detekt-rules-errorprone/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/UnusedUnaryOperator.kt index 04c95bae66e1..da3b6e64487f 100644 --- a/detekt-rules-errorprone/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/UnusedUnaryOperator.kt +++ b/detekt-rules-errorprone/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/UnusedUnaryOperator.kt @@ -17,8 +17,8 @@ import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtPrefixExpression -import org.jetbrains.kotlin.psi.psiUtil.getTopmostParentOfType import org.jetbrains.kotlin.psi.psiUtil.leaves +import org.jetbrains.kotlin.psi.psiUtil.parents import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall @@ -62,7 +62,7 @@ class UnusedUnaryOperator(config: Config = Config.empty) : Rule(config) { .none { it is PsiWhiteSpace && it.textContains('\n') } ) return - val parentOrSelf = (expression.getTopmostParentOfType() ?: expression) as KtExpression + val parentOrSelf = expression.parentBinaryExpressionOrThis() if (parentOrSelf.isUsedAsExpression(bindingContext)) return val operatorDescriptor = expression.operationReference.getResolvedCall(bindingContext) @@ -72,4 +72,8 @@ class UnusedUnaryOperator(config: Config = Config.empty) : Rule(config) { val message = "This '${parentOrSelf.text}' is not used" report(CodeSmell(issue, Entity.from(expression), message)) } + + private fun KtExpression.parentBinaryExpressionOrThis(): KtExpression { + return parents.takeWhile { it is KtBinaryExpression }.lastOrNull() as? KtBinaryExpression ?: this + } } diff --git a/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/CastToNullableTypeSpec.kt b/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/CastToNullableTypeSpec.kt index 0e8cfde05ae3..9be0ba15c301 100644 --- a/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/CastToNullableTypeSpec.kt +++ b/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/CastToNullableTypeSpec.kt @@ -16,7 +16,7 @@ class CastToNullableTypeSpec { """ val findings = subject.compileAndLint(code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(2, 22) + assertThat(findings).hasStartSourceLocation(2, 22) assertThat(findings[0]).hasMessage("Use the safe cast ('as? String') instead of 'as String?'.") } diff --git a/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/DoubleMutabilityForCollectionSpec.kt b/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/DoubleMutabilityForCollectionSpec.kt index 40dace89da9f..d2c201441c7e 100644 --- a/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/DoubleMutabilityForCollectionSpec.kt +++ b/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/DoubleMutabilityForCollectionSpec.kt @@ -30,7 +30,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(2, 5) + assertThat(result).hasStartSourceLocation(2, 5) } @Test @@ -42,7 +42,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(2, 5) + assertThat(result).hasStartSourceLocation(2, 5) } @Test @@ -54,7 +54,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(2, 5) + assertThat(result).hasStartSourceLocation(2, 5) } @Test @@ -66,7 +66,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(2, 5) + assertThat(result).hasStartSourceLocation(2, 5) } @Test @@ -78,7 +78,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(2, 5) + assertThat(result).hasStartSourceLocation(2, 5) } @Test @@ -90,7 +90,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(2, 5) + assertThat(result).hasStartSourceLocation(2, 5) } @Test @@ -102,7 +102,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(2, 5) + assertThat(result).hasStartSourceLocation(2, 5) } @Test @@ -114,7 +114,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(2, 5) + assertThat(result).hasStartSourceLocation(2, 5) } @Test @@ -135,7 +135,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = rule.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(3, 5) + assertThat(result).hasStartSourceLocation(3, 5) } @Test @@ -157,7 +157,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = rule.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(4, 5) + assertThat(result).hasStartSourceLocation(4, 5) } @Test @@ -180,7 +180,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = rule.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(5, 5) + assertThat(result).hasStartSourceLocation(5, 5) } } @@ -357,7 +357,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(1, 1) + assertThat(result).hasStartSourceLocation(1, 1) } @Test @@ -367,7 +367,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(1, 1) + assertThat(result).hasStartSourceLocation(1, 1) } @Test @@ -377,7 +377,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(1, 1) + assertThat(result).hasStartSourceLocation(1, 1) } @Test @@ -387,7 +387,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(1, 1) + assertThat(result).hasStartSourceLocation(1, 1) } @Test @@ -397,7 +397,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(1, 1) + assertThat(result).hasStartSourceLocation(1, 1) } @Test @@ -407,7 +407,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(1, 1) + assertThat(result).hasStartSourceLocation(1, 1) } @Test @@ -417,7 +417,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(1, 1) + assertThat(result).hasStartSourceLocation(1, 1) } @Test @@ -427,7 +427,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(1, 1) + assertThat(result).hasStartSourceLocation(1, 1) } @Test @@ -446,7 +446,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = rule.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(2, 1) + assertThat(result).hasStartSourceLocation(2, 1) } @Test @@ -466,7 +466,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = rule.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(3, 1) + assertThat(result).hasStartSourceLocation(3, 1) } @Test @@ -487,7 +487,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = rule.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(4, 1) + assertThat(result).hasStartSourceLocation(4, 1) } } @@ -642,7 +642,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(2, 5) + assertThat(result).hasStartSourceLocation(2, 5) } @Test @@ -654,7 +654,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(2, 5) + assertThat(result).hasStartSourceLocation(2, 5) } @Test @@ -666,7 +666,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(2, 5) + assertThat(result).hasStartSourceLocation(2, 5) } @Test @@ -678,7 +678,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(2, 5) + assertThat(result).hasStartSourceLocation(2, 5) } @Test @@ -690,7 +690,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(2, 5) + assertThat(result).hasStartSourceLocation(2, 5) } @Test @@ -702,7 +702,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(2, 5) + assertThat(result).hasStartSourceLocation(2, 5) } @Test @@ -714,7 +714,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(2, 5) + assertThat(result).hasStartSourceLocation(2, 5) } @Test @@ -726,7 +726,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = subject.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(2, 5) + assertThat(result).hasStartSourceLocation(2, 5) } @Test @@ -747,7 +747,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = rule.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(3, 5) + assertThat(result).hasStartSourceLocation(3, 5) } @Test @@ -769,7 +769,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = rule.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(4, 5) + assertThat(result).hasStartSourceLocation(4, 5) } @Test @@ -792,7 +792,7 @@ class DoubleMutabilityForCollectionSpec(private val env: KotlinCoreEnvironment) """ val result = rule.compileAndLintWithContext(env, code) assertThat(result).hasSize(1) - assertThat(result).hasSourceLocation(5, 5) + assertThat(result).hasStartSourceLocation(5, 5) } } diff --git a/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/ElseCaseInsteadOfExhaustiveWhenSpec.kt b/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/ElseCaseInsteadOfExhaustiveWhenSpec.kt index e28c2b8abb6a..f503e33ae5cd 100644 --- a/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/ElseCaseInsteadOfExhaustiveWhenSpec.kt +++ b/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/ElseCaseInsteadOfExhaustiveWhenSpec.kt @@ -86,7 +86,7 @@ class ElseCaseInsteadOfExhaustiveWhenSpec(private val env: KotlinCoreEnvironment } } """ - assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() + assertThat(subject.lintWithContext(env, code)).isEmpty() } } @@ -148,7 +148,7 @@ class ElseCaseInsteadOfExhaustiveWhenSpec(private val env: KotlinCoreEnvironment } } """ - assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() + assertThat(subject.lintWithContext(env, code)).isEmpty() } } diff --git a/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/IgnoredReturnValueSpec.kt b/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/IgnoredReturnValueSpec.kt index 41693016a9e3..f78eaeff2f03 100644 --- a/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/IgnoredReturnValueSpec.kt +++ b/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/IgnoredReturnValueSpec.kt @@ -206,7 +206,7 @@ class IgnoredReturnValueSpec(private val env: KotlinCoreEnvironment) { val findings = subject.lintWithContext(env, code, annotationClass) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(7, 5) + assertThat(findings).hasStartSourceLocation(7, 5) assertThat(findings[0]).hasMessage("The call listOfChecked is returning a value that is ignored.") } @@ -227,7 +227,7 @@ class IgnoredReturnValueSpec(private val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(9, 5) + assertThat(findings).hasStartSourceLocation(9, 5) assertThat(findings[0]).hasMessage("The call listOfChecked is returning a value that is ignored.") } @@ -294,7 +294,7 @@ class IgnoredReturnValueSpec(private val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(12, 10) + assertThat(findings).hasStartSourceLocation(12, 10) assertThat(findings[0]).hasMessage("The call listOfChecked is returning a value that is ignored.") } @@ -314,7 +314,7 @@ class IgnoredReturnValueSpec(private val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(9, 5) + assertThat(findings).hasStartSourceLocation(9, 5) assertThat(findings[0]).hasMessage("The call listOfChecked is returning a value that is ignored.") } @@ -335,7 +335,7 @@ class IgnoredReturnValueSpec(private val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(9, 20) + assertThat(findings).hasStartSourceLocation(9, 20) assertThat(findings[0]).hasMessage("The call listOfChecked is returning a value that is ignored.") } @@ -356,7 +356,7 @@ class IgnoredReturnValueSpec(private val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(9, 14) + assertThat(findings).hasStartSourceLocation(9, 14) assertThat(findings[0]).hasMessage("The call listOfChecked is returning a value that is ignored.") } @@ -376,7 +376,7 @@ class IgnoredReturnValueSpec(private val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(8, 11) + assertThat(findings).hasStartSourceLocation(8, 11) assertThat(findings[0]).hasMessage("The call isTheAnswer is returning a value that is ignored.") } @@ -730,7 +730,7 @@ class IgnoredReturnValueSpec(private val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(8, 5) + assertThat(findings).hasStartSourceLocation(8, 5) assertThat(findings[0]).hasMessage("The call listOfChecked is returning a value that is ignored.") } @@ -791,7 +791,7 @@ class IgnoredReturnValueSpec(private val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(9, 5) + assertThat(findings).hasStartSourceLocation(9, 5) assertThat(findings[0]).hasMessage("The call listOfChecked is returning a value that is ignored.") } @@ -807,7 +807,7 @@ class IgnoredReturnValueSpec(private val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(4, 5) + assertThat(findings).hasStartSourceLocation(4, 5) assertThat(findings[0]).hasMessage("The call listOfChecked is returning a value that is ignored.") } diff --git a/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/MissingWhenCaseSpec.kt b/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/MissingWhenCaseSpec.kt index 42bda1090d96..1f7ab05aee43 100644 --- a/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/MissingWhenCaseSpec.kt +++ b/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/MissingWhenCaseSpec.kt @@ -3,6 +3,7 @@ package io.gitlab.arturbosch.detekt.rules.bugs import io.gitlab.arturbosch.detekt.rules.KotlinCoreEnvironmentTest import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.compileAndLintWithContext +import io.gitlab.arturbosch.detekt.test.lintWithContext import org.assertj.core.api.Assertions.assertThat import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.junit.jupiter.api.Nested @@ -33,10 +34,12 @@ class MissingWhenCaseSpec(private val env: KotlinCoreEnvironment) { } } """ - val actual = subject.compileAndLintWithContext(env, code) + val actual = subject.lintWithContext(env, code) assertThat(actual).hasSize(1) assertThat(actual.first().issue.id).isEqualTo("MissingWhenCase") - assertThat(actual.first().message).isEqualTo("When expression is missing cases: RED. Either add missing cases or a default `else` case.") + assertThat(actual.first().message).isEqualTo( + "When expression is missing cases: RED. Either add missing cases or a default `else` case." + ) } @Test @@ -55,10 +58,12 @@ class MissingWhenCaseSpec(private val env: KotlinCoreEnvironment) { } } """ - val actual = subject.compileAndLintWithContext(env, code) + val actual = subject.lintWithContext(env, code) assertThat(actual).hasSize(1) assertThat(actual.first().issue.id).isEqualTo("MissingWhenCase") - assertThat(actual.first().message).isEqualTo("When expression is missing cases: RED, null. Either add missing cases or a default `else` case.") + assertThat(actual.first().message).isEqualTo( + "When expression is missing cases: RED, null. Either add missing cases or a default `else` case." + ) } @Test @@ -78,10 +83,12 @@ class MissingWhenCaseSpec(private val env: KotlinCoreEnvironment) { } } """ - val actual = subject.compileAndLintWithContext(env, code) + val actual = subject.lintWithContext(env, code) assertThat(actual).hasSize(1) assertThat(actual.first().issue.id).isEqualTo("MissingWhenCase") - assertThat(actual.first().message).isEqualTo("When expression is missing cases: null. Either add missing cases or a default `else` case.") + assertThat(actual.first().message).isEqualTo( + "When expression is missing cases: null. Either add missing cases or a default `else` case." + ) } @Test @@ -151,10 +158,12 @@ class MissingWhenCaseSpec(private val env: KotlinCoreEnvironment) { } } """ - val actual = subject.compileAndLintWithContext(env, code) + val actual = subject.lintWithContext(env, code) assertThat(actual).hasSize(1) assertThat(actual.first().issue.id).isEqualTo("MissingWhenCase") - assertThat(actual.first().message).isEqualTo("When expression is missing cases: VariantC. Either add missing cases or a default `else` case.") + assertThat(actual.first().message).isEqualTo( + "When expression is missing cases: VariantC. Either add missing cases or a default `else` case." + ) } @Test @@ -174,10 +183,12 @@ class MissingWhenCaseSpec(private val env: KotlinCoreEnvironment) { } } """ - val actual = subject.compileAndLintWithContext(env, code) + val actual = subject.lintWithContext(env, code) assertThat(actual).hasSize(1) assertThat(actual.first().issue.id).isEqualTo("MissingWhenCase") - assertThat(actual.first().message).isEqualTo("When expression is missing cases: null. Either add missing cases or a default `else` case.") + assertThat(actual.first().message).isEqualTo( + "When expression is missing cases: null. Either add missing cases or a default `else` case." + ) } @Test @@ -196,10 +207,12 @@ class MissingWhenCaseSpec(private val env: KotlinCoreEnvironment) { } } """ - val actual = subject.compileAndLintWithContext(env, code) + val actual = subject.lintWithContext(env, code) assertThat(actual).hasSize(1) assertThat(actual.first().issue.id).isEqualTo("MissingWhenCase") - assertThat(actual.first().message).isEqualTo("When expression is missing cases: VariantC, null. Either add missing cases or a default `else` case.") + assertThat(actual.first().message).isEqualTo( + "When expression is missing cases: VariantC, null. Either add missing cases or a default `else` case." + ) } @Test diff --git a/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/NullableToStringCallSpec.kt b/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/NullableToStringCallSpec.kt index 2cd3bfe64cfa..4542f6ce73d3 100644 --- a/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/NullableToStringCallSpec.kt +++ b/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/NullableToStringCallSpec.kt @@ -153,7 +153,6 @@ class NullableToStringCallSpec(private val env: KotlinCoreEnvironment) { data class Bar(val a: Any) fun test6(bar: Bar?) { if (bar == null) return - val x = bar?.a.toString() val y = "${'$'}{bar?.a}" } """ diff --git a/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/RedundantElseInWhenSpec.kt b/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/RedundantElseInWhenSpec.kt index a507cd5f31b2..decd8dcf3daf 100644 --- a/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/RedundantElseInWhenSpec.kt +++ b/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/RedundantElseInWhenSpec.kt @@ -2,6 +2,7 @@ package io.gitlab.arturbosch.detekt.rules.bugs import io.gitlab.arturbosch.detekt.rules.KotlinCoreEnvironmentTest import io.gitlab.arturbosch.detekt.test.compileAndLintWithContext +import io.gitlab.arturbosch.detekt.test.lintWithContext import org.assertj.core.api.Assertions.assertThat import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.junit.jupiter.api.Nested @@ -113,7 +114,7 @@ class RedundantElseInWhenSpec(private val env: KotlinCoreEnvironment) { } } """ - assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() + assertThat(subject.lintWithContext(env, code)).isEmpty() } } @@ -203,7 +204,7 @@ class RedundantElseInWhenSpec(private val env: KotlinCoreEnvironment) { } } """ - assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() + assertThat(subject.lintWithContext(env, code)).isEmpty() } } diff --git a/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/UnnecessarySafeCallSpec.kt b/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/UnnecessarySafeCallSpec.kt index 5b3037eecf88..688f61e356ee 100644 --- a/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/UnnecessarySafeCallSpec.kt +++ b/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/UnnecessarySafeCallSpec.kt @@ -50,8 +50,8 @@ class UnnecessarySafeCallSpec(private val env: KotlinCoreEnvironment) { } """ val findings = subject.compileAndLintWithContext(env, code) - assertThat(findings).hasSize(2) - assertThat(findings).hasTextLocations(48 to 59, 48 to 70) + assertThat(findings).hasSize(1) + assertThat(findings).hasTextLocations(48 to 59) } } diff --git a/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/UnreachableCatchBlockSpec.kt b/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/UnreachableCatchBlockSpec.kt index 98e6a584f326..a9ebdbb393df 100644 --- a/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/UnreachableCatchBlockSpec.kt +++ b/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/UnreachableCatchBlockSpec.kt @@ -23,7 +23,7 @@ class UnreachableCatchBlockSpec(private val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(4, 7) + assertThat(findings).hasStartSourceLocation(4, 7) } @Test @@ -38,7 +38,7 @@ class UnreachableCatchBlockSpec(private val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(4, 7) + assertThat(findings).hasStartSourceLocation(4, 7) } @Test @@ -54,7 +54,7 @@ class UnreachableCatchBlockSpec(private val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(2) - assertThat(findings).hasSourceLocations( + assertThat(findings).hasStartSourceLocations( SourceLocation(4, 7), SourceLocation(5, 7) ) diff --git a/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/UnusedUnaryOperatorSpec.kt b/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/UnusedUnaryOperatorSpec.kt index 7683fbe216b2..893b23bc3ee5 100644 --- a/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/UnusedUnaryOperatorSpec.kt +++ b/detekt-rules-errorprone/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/UnusedUnaryOperatorSpec.kt @@ -20,7 +20,7 @@ class UnusedUnaryOperatorSpec(private val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(3, 9) + assertThat(findings).hasStartSourceLocation(3, 9) assertThat(findings[0]).hasMessage("This '+ 3' is not used") } @@ -34,7 +34,7 @@ class UnusedUnaryOperatorSpec(private val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(3, 9) + assertThat(findings).hasStartSourceLocation(3, 9) assertThat(findings[0]).hasMessage("This '- 3' is not used") } @@ -122,4 +122,20 @@ class UnusedUnaryOperatorSpec(private val env: KotlinCoreEnvironment) { val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).isEmpty() } + + @Test + fun `var assignment by if expression`() { + val code = """ + fun test(b: Boolean) { + var x = 0 + x = if (b) { + -1 + } else { + 1 + } + } + """ + val findings = subject.compileAndLintWithContext(env, code) + assertThat(findings).isEmpty() + } } diff --git a/detekt-rules-exceptions/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/exceptions/ThrowingNewInstanceOfSameExceptionSpec.kt b/detekt-rules-exceptions/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/exceptions/ThrowingNewInstanceOfSameExceptionSpec.kt index 05c87835d9c9..2df376892ec6 100644 --- a/detekt-rules-exceptions/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/exceptions/ThrowingNewInstanceOfSameExceptionSpec.kt +++ b/detekt-rules-exceptions/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/exceptions/ThrowingNewInstanceOfSameExceptionSpec.kt @@ -46,7 +46,9 @@ class ThrowingNewInstanceOfSameExceptionSpec { } @Nested - @DisplayName("a catch block which throws a new instance of the same exception type without wrapping the caught exception") + @DisplayName( + "a catch block which throws a new instance of the same exception type without wrapping the caught exception" + ) inner class CatchBlockThrowingSameExceptionWithoutWrapping { val code = """ fun x() { diff --git a/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/BooleanPropertyNaming.kt b/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/BooleanPropertyNaming.kt index 29ae0bbb0a03..2bb5c283402d 100644 --- a/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/BooleanPropertyNaming.kt +++ b/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/BooleanPropertyNaming.kt @@ -41,7 +41,8 @@ class BooleanPropertyNaming(config: Config = Config.empty) : Rule(config) { private val ignoreOverridden: Boolean by config(true) override val issue = Issue( - javaClass.simpleName, Severity.CodeSmell, + javaClass.simpleName, + Severity.CodeSmell, "Boolean property name should follow the naming convention set in the projects configuration.", Debt.FIVE_MINS ) diff --git a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/FunctionNamingSpec.kt b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/FunctionNamingSpec.kt index e9c0e445a765..59dc5ca06353 100644 --- a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/FunctionNamingSpec.kt +++ b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/FunctionNamingSpec.kt @@ -48,7 +48,7 @@ class FunctionNamingSpec { } interface I { fun shouldNotBeFlagged() } """ - assertThat(FunctionNaming().compileAndLint(code)).hasSourceLocation(3, 13) + assertThat(FunctionNaming().compileAndLint(code)).hasStartSourceLocation(3, 13) } @Test @@ -84,7 +84,7 @@ class FunctionNamingSpec { } interface I { @Suppress("FunctionNaming") fun SHOULD_BE_FLAGGED() } """ - assertThat(FunctionNaming().compileAndLint(code)).hasSourceLocation(3, 13) + assertThat(FunctionNaming().compileAndLint(code)).hasStartSourceLocation(3, 13) } @Test @@ -96,7 +96,7 @@ class FunctionNamingSpec { interface I { fun SHOULD_BE_FLAGGED() } """ val config = TestConfig(mapOf(FunctionNaming.IGNORE_OVERRIDDEN to "false")) - assertThat(FunctionNaming(config).compileAndLint(code)).hasSourceLocations( + assertThat(FunctionNaming(config).compileAndLint(code)).hasStartSourceLocations( SourceLocation(2, 18), SourceLocation(4, 19) ) @@ -107,6 +107,6 @@ class FunctionNamingSpec { val code = """ fun `7his is a function name _`() = Unit """ - assertThat(FunctionNaming().compileAndLint(code)).hasSourceLocations(SourceLocation(1, 5)) + assertThat(FunctionNaming().compileAndLint(code)).hasStartSourceLocations(SourceLocation(1, 5)) } } diff --git a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/MatchingDeclarationNameSpec.kt b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/MatchingDeclarationNameSpec.kt index b5c296559872..89cb46cfa9b3 100644 --- a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/MatchingDeclarationNameSpec.kt +++ b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/MatchingDeclarationNameSpec.kt @@ -132,7 +132,7 @@ class MatchingDeclarationNameSpec { fun `should not pass for object declaration`() { val ktFile = compileContentForTest("object O", filename = "Objects.kt") val findings = MatchingDeclarationName().lint(ktFile) - assertThat(findings).hasSourceLocation(1, 8) + assertThat(findings).hasStartSourceLocation(1, 8) } @Test @@ -142,14 +142,14 @@ class MatchingDeclarationNameSpec { filename = "Objects.kt" ) val findings = MatchingDeclarationName().lint(ktFile) - assertThat(findings).hasSourceLocation(1, 45) + assertThat(findings).hasStartSourceLocation(1, 45) } @Test fun `should not pass for class declaration`() { val ktFile = compileContentForTest("class C", filename = "Classes.kt") val findings = MatchingDeclarationName().lint(ktFile) - assertThat(findings).hasSourceLocation(1, 7) + assertThat(findings).hasStartSourceLocation(1, 7) } @Test @@ -163,14 +163,14 @@ class MatchingDeclarationNameSpec { filename = "ClassUtils.kt" ) val findings = MatchingDeclarationName().lint(ktFile) - assertThat(findings).hasSourceLocation(1, 7) + assertThat(findings).hasStartSourceLocation(1, 7) } @Test fun `should not pass for interface declaration`() { val ktFile = compileContentForTest("interface I", filename = "Not_I.kt") val findings = MatchingDeclarationName().lint(ktFile) - assertThat(findings).hasSourceLocation(1, 11) + assertThat(findings).hasStartSourceLocation(1, 11) } @Test @@ -184,7 +184,7 @@ class MatchingDeclarationNameSpec { filename = "E.kt" ) val findings = MatchingDeclarationName().lint(ktFile) - assertThat(findings).hasSourceLocation(1, 12) + assertThat(findings).hasStartSourceLocation(1, 12) } @Test @@ -211,7 +211,7 @@ class MatchingDeclarationNameSpec { val findings = MatchingDeclarationName( TestConfig("mustBeFirst" to "false") ).lint(ktFile) - assertThat(findings).hasSourceLocation(3, 7) + assertThat(findings).hasStartSourceLocation(3, 7) } } } diff --git a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/NoNameShadowingSpec.kt b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/NoNameShadowingSpec.kt index e6f707169b9b..ed16f59845b2 100644 --- a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/NoNameShadowingSpec.kt +++ b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/NoNameShadowingSpec.kt @@ -19,7 +19,7 @@ class NoNameShadowingSpec(val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(2, 9) + assertThat(findings).hasStartSourceLocation(2, 9) assertThat(findings[0]).hasMessage("Name shadowed: i") } diff --git a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/ObjectPropertyNamingSpec.kt b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/ObjectPropertyNamingSpec.kt index 3ab372728239..2c8dc13eb3b2 100644 --- a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/ObjectPropertyNamingSpec.kt +++ b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/ObjectPropertyNamingSpec.kt @@ -64,7 +64,7 @@ class ObjectPropertyNamingSpec { ${PublicConst.positive} } """ - assertThat(subject.compileAndLint(code)).hasSourceLocation(2, 15) + assertThat(subject.compileAndLint(code)).hasStartSourceLocation(2, 15) } } @@ -130,7 +130,7 @@ class ObjectPropertyNamingSpec { } } """ - assertThat(subject.compileAndLint(code)).hasSourceLocation(3, 19) + assertThat(subject.compileAndLint(code)).hasStartSourceLocation(3, 19) } } diff --git a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/VariableNamingSpec.kt b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/VariableNamingSpec.kt index 0c9e783e6315..78c33c6982d2 100644 --- a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/VariableNamingSpec.kt +++ b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/VariableNamingSpec.kt @@ -18,7 +18,7 @@ class VariableNamingSpec { } """ assertThat(VariableNaming().compileAndLint(code)) - .hasSourceLocations( + .hasStartSourceLocations( SourceLocation(2, 17), SourceLocation(3, 9), SourceLocation(4, 9) @@ -68,7 +68,7 @@ class VariableNamingSpec { """ val config = TestConfig(mapOf(IGNORE_OVERRIDDEN to "false")) assertThat(VariableNaming(config).compileAndLint(code)) - .hasSourceLocations( + .hasStartSourceLocations( SourceLocation(2, 18), SourceLocation(5, 18) ) diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ExplicitCollectionElementAccessMethod.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ExplicitCollectionElementAccessMethod.kt index 61601003522e..58e7eaeb37a4 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ExplicitCollectionElementAccessMethod.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ExplicitCollectionElementAccessMethod.kt @@ -19,7 +19,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlin.types.ErrorType +import org.jetbrains.kotlin.types.error.ErrorType import org.jetbrains.kotlin.types.typeUtil.supertypes /** diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenImport.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenImport.kt index 069c1e65fa43..ed6a1b474c84 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenImport.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenImport.kt @@ -10,6 +10,7 @@ import io.gitlab.arturbosch.detekt.api.Severity import io.gitlab.arturbosch.detekt.api.config import io.gitlab.arturbosch.detekt.api.internal.Configuration import io.gitlab.arturbosch.detekt.api.simplePatternToRegex +import io.gitlab.arturbosch.detekt.api.valuesWithReason import org.jetbrains.kotlin.psi.KtImportDirective /** @@ -17,7 +18,6 @@ import org.jetbrains.kotlin.psi.KtImportDirective * or deprecated APIs. Detekt will then report all imports that are forbidden. * * - * package foo * import kotlin.jvm.JvmField * import kotlin.SinceKotlin * @@ -33,8 +33,8 @@ class ForbiddenImport(config: Config = Config.empty) : Rule(config) { ) @Configuration("imports which should not be used") - private val imports: List by config(emptyList()) { - it.distinct().map(String::simplePatternToRegex) + private val imports: List by config(valuesWithReason()) { list -> + list.map { Forbidden(it.value.simplePatternToRegex(), it.reason) } } @Configuration("reports imports which match the specified regular expression. For example `net.*R`.") @@ -44,18 +44,29 @@ class ForbiddenImport(config: Config = Config.empty) : Rule(config) { super.visitImportDirective(importDirective) val import = importDirective.importedFqName?.asString().orEmpty() - if (imports.any { it.matches(import) } || containsForbiddenPattern(import)) { - report( - CodeSmell( - issue, - Entity.from(importDirective), - "The import " + - "$import has been forbidden in the Detekt config." - ) - ) + + val forbidden = imports.find { it.import.matches(import) } + val reason = if (forbidden != null) { + if (forbidden.reason != null) { + "The import `$import` has been forbidden: ${forbidden.reason}" + } else { + defaultReason(import) + } + } else { + if (containsForbiddenPattern(import)) defaultReason(import) else null + } + + if (reason != null) { + report(CodeSmell(issue, Entity.from(importDirective), reason)) } } + private fun defaultReason(forbiddenImport: String): String { + return "The import `$forbiddenImport` has been forbidden in the detekt config." + } + private fun containsForbiddenPattern(import: String): Boolean = forbiddenPatterns.pattern.isNotEmpty() && forbiddenPatterns.containsMatchIn(import) } + +private data class Forbidden(val import: Regex, val reason: String?) diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCall.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCall.kt index ca4226d314a1..a49cfb05eb21 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCall.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCall.kt @@ -1,6 +1,7 @@ package io.gitlab.arturbosch.detekt.rules.style import io.github.detekt.tooling.api.FunctionMatcher +import io.github.detekt.tooling.api.FunctionMatcher.Companion.fromFunctionSignature import io.gitlab.arturbosch.detekt.api.CodeSmell import io.gitlab.arturbosch.detekt.api.Config import io.gitlab.arturbosch.detekt.api.Debt @@ -11,12 +12,21 @@ import io.gitlab.arturbosch.detekt.api.Severity import io.gitlab.arturbosch.detekt.api.config import io.gitlab.arturbosch.detekt.api.internal.Configuration import io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution +import io.gitlab.arturbosch.detekt.api.valuesWithReason +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtCallExpression +import org.jetbrains.kotlin.psi.KtCallableReferenceExpression +import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtPostfixExpression import org.jetbrains.kotlin.psi.KtPrefixExpression +import org.jetbrains.kotlin.psi.psiUtil.isDotSelector +import org.jetbrains.kotlin.psi2ir.unwrappedGetMethod +import org.jetbrains.kotlin.psi2ir.unwrappedSetMethod import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.util.getCalleeExpressionIfAny import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall /** @@ -28,6 +38,7 @@ import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall * import java.lang.System * fun main() { * System.gc() + * System::gc * } * * @@ -52,12 +63,14 @@ class ForbiddenMethodCall(config: Config = Config.empty) : Rule(config) { "`fun String.hello(a: Int)` you should add the receiver parameter as the first parameter like this: " + "`hello(kotlin.String, kotlin.Int)`" ) - private val methods: List by config( - listOf( - "kotlin.io.print", - "kotlin.io.println", + private val methods: List by config( + valuesWithReason( + "kotlin.io.print" to "print does not allow you to configure the output stream. Use a logger instead.", + "kotlin.io.println" to "println does not allow you to configure the output stream. Use a logger instead.", ) - ) { it.map(FunctionMatcher::fromFunctionSignature) } + ) { list -> + list.map { Forbidden(fromFunctionSignature(it.value), it.reason) } + } override fun visitCallExpression(expression: KtCallExpression) { super.visitCallExpression(expression) @@ -69,6 +82,13 @@ class ForbiddenMethodCall(config: Config = Config.empty) : Rule(config) { check(expression.operationReference) } + override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) { + super.visitDotQualifiedExpression(expression) + if (expression.getCalleeExpressionIfAny()?.isDotSelector() == true) { + check(expression) + } + } + override fun visitPrefixExpression(expression: KtPrefixExpression) { super.visitPrefixExpression(expression) check(expression.operationReference) @@ -79,18 +99,34 @@ class ForbiddenMethodCall(config: Config = Config.empty) : Rule(config) { check(expression.operationReference) } + override fun visitCallableReferenceExpression(expression: KtCallableReferenceExpression) { + super.visitCallableReferenceExpression(expression) + check(expression.callableReference) + } + private fun check(expression: KtExpression) { if (bindingContext == BindingContext.EMPTY) return val descriptors = expression.getResolvedCall(bindingContext)?.resultingDescriptor?.let { - listOf(it) + it.overriddenDescriptors + val foundDescriptors = if (it is PropertyDescriptor) { + listOfNotNull(it.unwrappedGetMethod, it.unwrappedSetMethod) + } else { + listOf(it) + } + foundDescriptors + foundDescriptors.flatMap(CallableDescriptor::getOverriddenDescriptors) } ?: return for (descriptor in descriptors) { - methods.find { it.match(descriptor) }?.let { functionMatcher -> - val message = "The method $functionMatcher has been forbidden in the Detekt config." + methods.find { it.value.match(descriptor) }?.let { forbidden -> + val message = if (forbidden.reason != null) { + "The method `${forbidden.value}` has been forbidden: ${forbidden.reason}" + } else { + "The method `${forbidden.value}` has been forbidden in the detekt config." + } report(CodeSmell(issue, Entity.from(expression), message)) } } } + + private data class Forbidden(val value: FunctionMatcher, val reason: String?) } diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/NewLineAtEndOfFile.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/NewLineAtEndOfFile.kt index a04bbcd17bbd..903b1bc6e3cc 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/NewLineAtEndOfFile.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/NewLineAtEndOfFile.kt @@ -1,5 +1,6 @@ package io.gitlab.arturbosch.detekt.rules.style +import io.github.detekt.psi.getLineAndColumnInPsiFile import io.github.detekt.psi.toFilePath import io.gitlab.arturbosch.detekt.api.CodeSmell import io.gitlab.arturbosch.detekt.api.Config @@ -13,7 +14,6 @@ import io.gitlab.arturbosch.detekt.api.SourceLocation import io.gitlab.arturbosch.detekt.api.TextLocation import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault import org.jetbrains.kotlin.com.intellij.openapi.util.TextRange -import org.jetbrains.kotlin.diagnostics.DiagnosticUtils import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.psiUtil.endOffset @@ -33,11 +33,11 @@ class NewLineAtEndOfFile(config: Config = Config.empty) : Rule(config) { override fun visitKtFile(file: KtFile) { val text = file.text if (text.isNotEmpty() && !text.endsWith('\n')) { - val coords = DiagnosticUtils.getLineAndColumnInPsiFile( + val coords = getLineAndColumnInPsiFile( file, TextRange(file.endOffset, file.endOffset) ) - val sourceLocation = SourceLocation(coords.line, coords.column) + val sourceLocation = SourceLocation(coords?.line ?: 0, coords?.column ?: 0) val textLocation = TextLocation(file.endOffset, file.endOffset) val location = Location(sourceLocation, textLocation, file.containingFile.toFilePath()) report( diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/RedundantExplicitType.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/RedundantExplicitType.kt index bf41d0411310..8b28cd4f1231 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/RedundantExplicitType.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/RedundantExplicitType.kt @@ -7,6 +7,7 @@ import io.gitlab.arturbosch.detekt.api.Entity import io.gitlab.arturbosch.detekt.api.Issue import io.gitlab.arturbosch.detekt.api.Rule import io.gitlab.arturbosch.detekt.api.Severity +import io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.psi.KtCallExpression @@ -43,6 +44,7 @@ import org.jetbrains.kotlin.types.typeUtil.isLong * } * */ +@RequiresTypeResolution class RedundantExplicitType(config: Config) : Rule(config) { override val issue = Issue( diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ReturnCount.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ReturnCount.kt index 1d6d69e53eb2..d0ffdba332c4 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ReturnCount.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ReturnCount.kt @@ -59,8 +59,8 @@ class ReturnCount(config: Config = Config.empty) : Rule(config) { @Configuration("define the maximum number of return statements allowed per function") private val max: Int by config(2) - @Configuration("define a free-form comma separated list of function names to be ignored by this check") - private val excludedFunctions: SplitPattern by config("equals") { SplitPattern(it) } + @Configuration("define a list of function names to be ignored by this check") + private val excludedFunctions: SplitPattern by config(listOf("equals")) { SplitPattern(it.joinToString(",")) } @Configuration("if labeled return statements should be ignored") private val excludeLabeled: Boolean by config(false) diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt index 4591807eb81f..834e95b2f7d7 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt @@ -30,6 +30,7 @@ class StyleGuideProvider : DefaultRuleSetProvider { DestructuringDeclarationWithTooManyEntries(config), ReturnCount(config), ThrowsCount(config), + TrimMultilineRawString(config), NewLineAtEndOfFile(config), WildcardImport(config), FileParsingRule(config), diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/TrimMultilineRawString.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/TrimMultilineRawString.kt new file mode 100644 index 000000000000..d2d215ee919e --- /dev/null +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/TrimMultilineRawString.kt @@ -0,0 +1,74 @@ +package io.gitlab.arturbosch.detekt.rules.style + +import io.gitlab.arturbosch.detekt.api.CodeSmell +import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.Debt +import io.gitlab.arturbosch.detekt.api.Entity +import io.gitlab.arturbosch.detekt.api.Issue +import io.gitlab.arturbosch.detekt.api.Rule +import io.gitlab.arturbosch.detekt.api.Severity +import org.jetbrains.kotlin.psi.KtCallExpression +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtStringTemplateExpression +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForReceiver +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis + +/** + * All the Raw strings that have more than one line should be followed by `trimMargin()` or `trimIndent()`. + * + * + * """ + * Hello World! + * How are you? + * """ + * + * + * + * """ + * | Hello World! + * | How are you? + * """.trimMargin() + * + * """ + * Hello World! + * How are you? + * """.trimIndent() + * + * """Hello World! How are you?""" + * + */ +class TrimMultilineRawString(val config: Config) : Rule(config) { + override val issue = Issue( + javaClass.simpleName, + Severity.Style, + "Multiline raw strings should be followed by `trimMargin()` or `trimIndent()`.", + Debt.FIVE_MINS + ) + + override fun visitStringTemplateExpression(expression: KtStringTemplateExpression) { + super.visitStringTemplateExpression(expression) + + if (expression.text.lines().count() <= 1) return + + val nextCall = expression.getQualifiedExpressionForSelectorOrThis() + .getQualifiedExpressionForReceiver() + ?.selectorExpression + ?.asKtCallExpression() + ?.calleeExpression + ?.text + + if (nextCall !in trimFunctions) { + report( + CodeSmell( + issue, + Entity.from(expression), + "Multiline raw strings should be followed by `trimMargin()` or `trimIndent()`", + ) + ) + } + } +} + +private fun KtExpression.asKtCallExpression(): KtCallExpression? = this as? KtCallExpression + +private val trimFunctions = listOf("trimIndent", "trimMargin") diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryParentheses.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryParentheses.kt index c33c408b681a..8731a1c92686 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryParentheses.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryParentheses.kt @@ -7,7 +7,17 @@ import io.gitlab.arturbosch.detekt.api.Entity import io.gitlab.arturbosch.detekt.api.Issue import io.gitlab.arturbosch.detekt.api.Rule import io.gitlab.arturbosch.detekt.api.Severity +import io.gitlab.arturbosch.detekt.api.config +import io.gitlab.arturbosch.detekt.api.internal.Configuration +import org.jetbrains.kotlin.com.intellij.psi.PsiElement +import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.parsing.KotlinExpressionParsing.Precedence +import org.jetbrains.kotlin.psi.KtBinaryExpression +import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtIsExpression import org.jetbrains.kotlin.psi.KtParenthesizedExpression import org.jetbrains.kotlin.psi.KtPsiUtil @@ -46,17 +56,97 @@ class UnnecessaryParentheses(config: Config = Config.empty) : Rule(config) { Debt.FIVE_MINS ) + @Configuration( + "allow parentheses when not strictly required but precedence may be unclear, such as `(a && b) || c`" + ) + private val allowForUnclearPrecedence: Boolean by config(defaultValue = false) + + @Suppress("ReturnCount") override fun visitParenthesizedExpression(expression: KtParenthesizedExpression) { super.visitParenthesizedExpression(expression) - if (expression.expression == null) return + val inner = expression.expression ?: return + val outer = expression.parent + + if (outer is KtDelegatedSuperTypeEntry) return + + if (!KtPsiUtil.areParenthesesUseless(expression)) return + + if (allowForUnclearPrecedence && inner.isBinaryOperationPrecedenceUnclearWithParent()) return + + val message = "Parentheses in ${expression.text} are unnecessary and can be replaced with: " + + KtPsiUtil.deparenthesize(expression)?.text + report(CodeSmell(issue, Entity.from(expression), message)) + } + + companion object { + /** + * Map from operators to a set of other operators between which precedence can be unclear. + * + * This is built from a mapping of [Precedence] to other, greater, [Precedence](s) which should be considered + * unclear when mixed as child binary expressions. + */ + @Suppress("CommentOverPrivateProperty") + private val childToUnclearPrecedenceParentsMapping: Map> = arrayOf( + Precedence.ELVIS to arrayOf( + Precedence.EQUALITY, // (a ?: b) == c + Precedence.COMPARISON, // (a ?: b) > c + Precedence.IN_OR_IS, // (a ?: b) in c + ), + + Precedence.SIMPLE_NAME to arrayOf( + Precedence.ELVIS, // a ?: (b to c) + Precedence.SIMPLE_NAME, // (a to b) to c + ), + + // (a * b) + c + Precedence.MULTIPLICATIVE to arrayOf(Precedence.ADDITIVE), + + // (a && b) || c + Precedence.CONJUNCTION to arrayOf(Precedence.DISJUNCTION), + ) + .onEach { (child, parents) -> + parents.forEach { check(child <= it) } + } + .flatMap { (child, parents) -> + child.operations.types.map { childOp -> + childOp to parents.flatMapTo(mutableSetOf()) { parentOp -> parentOp.operations.types.toList() } + } + } + .toMap() + + /** + * Retrieves the [IElementType] of the binary operation from this element if it is a non-assignment binary + * expression, or null otherwise. + */ + private fun PsiElement.binaryOp(): IElementType? { + return when (this) { + is KtBinaryExpression -> + operationReference.takeUnless { operationToken in KtTokens.ALL_ASSIGNMENTS } + is KtBinaryExpressionWithTypeRHS -> operationReference + is KtIsExpression -> operationReference + else -> null + }?.getReferencedNameElementType() + } + + /** + * Returns either the parent of this [KtExpression] or its first parent expression which is not a + * [KtParenthesizedExpression]. + */ + private fun KtExpression.firstNonParenParent(): PsiElement? { + return generateSequence(parent) { (it as? KtParenthesizedExpression)?.parent } + .firstOrNull { it !is KtParenthesizedExpression } + } - if (expression.parent is KtDelegatedSuperTypeEntry) return + /** + * Determines whether this is a binary expression whose operation precedence is unclear with the parent binary + * operation per [childToUnclearPrecedenceParentsMapping]. + */ + private fun KtExpression.isBinaryOperationPrecedenceUnclearWithParent(): Boolean { + val innerOp = binaryOp() ?: return false + val outerOp = firstNonParenParent()?.binaryOp() ?: return false - if (KtPsiUtil.areParenthesesUseless(expression)) { - val message = "Parentheses in ${expression.text} are unnecessary and can be replaced with: " + - "${KtPsiUtil.deparenthesize(expression)?.text}" - report(CodeSmell(issue, Entity.from(expression), message)) + return childToUnclearPrecedenceParentsMapping[innerOp]?.contains(outerOp) == true } } } diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedImports.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedImports.kt index e5b1853974e7..6da0c9505299 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedImports.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedImports.kt @@ -54,26 +54,44 @@ class UnusedImports(config: Config) : Rule(config) { private val staticReferences = mutableSetOf() private val namedReferencesInKDoc = mutableSetOf() + private val namedReferencesAsString: Set by lazy { + namedReferences.mapTo(mutableSetOf()) { it.text.trim('`') } + } + private val staticReferencesAsString: Set by lazy { + staticReferences.mapTo(mutableSetOf()) { it.text.trim('`') } + } + private val fqNames: Set by lazy { + namedReferences.mapNotNullTo(mutableSetOf()) { it.fqNameOrNull() } + } + + /** + * All [namedReferences] whose [KtReferenceExpression.fqNameOrNull] cannot be resolved + * mapped to their text. String matches to such references shouldn't be marked as unused + * imports since they could match the unknown value being imported. + */ + @Suppress("CommentOverPrivateProperty") + private val unresolvedNamedReferencesAsString: Set by lazy { + namedReferences.mapNotNullTo(mutableSetOf()) { + if (it.fqNameOrNull() == null) it.text.trim('`') else null + } + } + fun unusedImports(): List { fun KtImportDirective.isFromSamePackage() = importedFqName?.parent() == currentPackage && alias == null @Suppress("ReturnCount") fun KtImportDirective.isNotUsed(): Boolean { - val namedReferencesAsString = namedReferences.map { it.text.trim('`') } - val staticReferencesAsString = staticReferences.map { it.text.trim('`') } if (aliasName in (namedReferencesInKDoc + namedReferencesAsString)) return false val identifier = identifier() if (identifier in namedReferencesInKDoc || identifier in staticReferencesAsString) return false return if (bindingContext == BindingContext.EMPTY) { identifier !in namedReferencesAsString } else { - val fqNames = namedReferences.mapNotNull { - val descriptor = bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, it] - ?: bindingContext[BindingContext.REFERENCE_TARGET, it] - descriptor?.getImportableDescriptor()?.fqNameOrNull() - } - importPath?.fqName?.let { it !in fqNames } == true + val fqNameUsed = importPath?.fqName?.let { it in fqNames } == true + val unresolvedNameUsed = identifier in unresolvedNamedReferencesAsString + + !fqNameUsed && !unresolvedNameUsed } } @@ -135,6 +153,12 @@ class UnusedImports(config: Config) : Rule(config) { namedReferencesInKDoc.add(str.split(".")[0]) } } + + private fun KtReferenceExpression.fqNameOrNull(): FqName? { + val descriptor = bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, this] + ?: bindingContext[BindingContext.REFERENCE_TARGET, this] + return descriptor?.getImportableDescriptor()?.fqNameOrNull() + } } companion object { diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UselessCallOnNotNull.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UselessCallOnNotNull.kt index 0c12e3d61e85..8f6e0eb6a920 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UselessCallOnNotNull.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UselessCallOnNotNull.kt @@ -18,7 +18,7 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall import org.jetbrains.kotlin.resolve.calls.util.getType import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull -import org.jetbrains.kotlin.types.ErrorType +import org.jetbrains.kotlin.types.error.ErrorType import org.jetbrains.kotlin.types.isNullable /* diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenImportSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenImportSpec.kt index 4e89ca8ef308..18370f8a8077 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenImportSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenImportSpec.kt @@ -1,7 +1,9 @@ package io.gitlab.arturbosch.detekt.rules.style +import io.gitlab.arturbosch.detekt.api.ValueWithReason import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.lint +import io.gitlab.arturbosch.detekt.test.toConfig import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test @@ -29,43 +31,61 @@ class ForbiddenImportSpec { @Test fun `should report nothing when imports are blank`() { - val findings = ForbiddenImport(TestConfig(mapOf(IMPORTS to " "))).lint(code) + val findings = ForbiddenImport(TestConfig(mapOf(IMPORTS to listOf(" ")))).lint(code) assertThat(findings).isEmpty() } @Test fun `should report nothing when imports do not match`() { - val findings = ForbiddenImport(TestConfig(mapOf(IMPORTS to "org.*"))).lint(code) + val findings = ForbiddenImport(TestConfig(mapOf(IMPORTS to listOf("org.*")))).lint(code) assertThat(findings).isEmpty() } @Test @DisplayName("should report kotlin.* when imports are kotlin.*") fun reportKotlinWildcardImports() { - val findings = ForbiddenImport(TestConfig(mapOf(IMPORTS to "kotlin.*"))).lint(code) + val findings = ForbiddenImport(TestConfig(mapOf(IMPORTS to listOf("kotlin.*")))).lint(code) + assertThat(findings) + .extracting("message") + .containsExactlyInAnyOrder( + "The import `kotlin.jvm.JvmField` has been forbidden in the detekt config.", + "The import `kotlin.SinceKotlin` has been forbidden in the detekt config.", + ) + } + + @Test + @DisplayName("should report kotlin.* when imports are kotlin.* with reasons") + fun reportKotlinWildcardImports2() { + val config = TestConfig(mapOf(IMPORTS to listOf(ValueWithReason("kotlin.*", "I'm just joking!").toConfig()))) + val findings = ForbiddenImport(config).lint(code) assertThat(findings).hasSize(2) + assertThat(findings[0].message) + .isEqualTo("The import `kotlin.jvm.JvmField` has been forbidden: I'm just joking!") + assertThat(findings[1].message) + .isEqualTo("The import `kotlin.SinceKotlin` has been forbidden: I'm just joking!") } @Test @DisplayName("should report kotlin.SinceKotlin when specified via fully qualified name") fun reportKotlinSinceKotlinWhenFqdnSpecified() { - val findings = - ForbiddenImport(TestConfig(mapOf(IMPORTS to "kotlin.SinceKotlin"))).lint(code) - assertThat(findings).hasSize(1) + val findings = ForbiddenImport(TestConfig(mapOf(IMPORTS to listOf("kotlin.SinceKotlin")))).lint(code) + assertThat(findings) + .hasSize(1) } @Test @DisplayName("should report kotlin.SinceKotlin and kotlin.jvm.JvmField when specified via fully qualified names") fun reportMultipleConfiguredImportsCommaSeparated() { val findings = - ForbiddenImport(TestConfig(mapOf(IMPORTS to "kotlin.SinceKotlin,kotlin.jvm.JvmField"))).lint( - code - ) + ForbiddenImport(TestConfig(mapOf(IMPORTS to listOf("kotlin.SinceKotlin", "kotlin.jvm.JvmField")))) + .lint(code) assertThat(findings).hasSize(2) } @Test - @DisplayName("should report kotlin.SinceKotlin and kotlin.jvm.JvmField when specified via fully qualified names list") + @DisplayName( + "should report kotlin.SinceKotlin and kotlin.jvm.JvmField when specified via fully qualified names list" + ) fun reportMultipleConfiguredImportsInList() { val findings = ForbiddenImport( @@ -81,14 +101,14 @@ class ForbiddenImportSpec { @Test @DisplayName("should report kotlin.SinceKotlin when specified via kotlin.Since*") fun reportsKotlinSinceKotlinWhenSpecifiedWithWildcard() { - val findings = ForbiddenImport(TestConfig(mapOf(IMPORTS to "kotlin.Since*"))).lint(code) + val findings = ForbiddenImport(TestConfig(mapOf(IMPORTS to listOf("kotlin.Since*")))).lint(code) assertThat(findings).hasSize(1) } @Test @DisplayName("should report all of com.example.R.string, net.example.R.dimen, and net.example.R.dimension") fun preAndPostWildcard() { - val findings = ForbiddenImport(TestConfig(mapOf(IMPORTS to "*.R.*"))).lint(code) + val findings = ForbiddenImport(TestConfig(mapOf(IMPORTS to listOf("*.R.*")))).lint(code) assertThat(findings).hasSize(3) } @@ -96,7 +116,7 @@ class ForbiddenImportSpec { @DisplayName("should report net.example.R.dimen but not net.example.R.dimension") fun doNotReportSubstringOfFqdn() { val findings = - ForbiddenImport(TestConfig(mapOf(IMPORTS to "net.example.R.dimen"))).lint(code) + ForbiddenImport(TestConfig(mapOf(IMPORTS to listOf("net.example.R.dimen")))).lint(code) assertThat(findings).hasSize(1) } @@ -112,5 +132,9 @@ class ForbiddenImportSpec { val findings = ForbiddenImport(TestConfig(mapOf(FORBIDDEN_PATTERNS to "net.*R|com.*expiremental"))).lint(code) assertThat(findings).hasSize(2) + assertThat(findings[0].message) + .isEqualTo("The import `net.example.R.dimen` has been forbidden in the detekt config.") + assertThat(findings[1].message) + .isEqualTo("The import `net.example.R.dimension` has been forbidden in the detekt config.") } } diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCallSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCallSpec.kt index 1c665b7e39d2..1102c0eb9fbb 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCallSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCallSpec.kt @@ -23,11 +23,18 @@ class ForbiddenMethodCallSpec(val env: KotlinCoreEnvironment) { } """ val findings = ForbiddenMethodCall(TestConfig()).compileAndLintWithContext(env, code) - assertThat(findings).hasSize(2) - assertThat(findings).hasSourceLocations( - SourceLocation(2, 5), - SourceLocation(3, 5) - ) + + assertThat(findings) + .hasSize(2) + .hasStartSourceLocations( + SourceLocation(2, 5), + SourceLocation(3, 5), + ) + .extracting("message") + .containsExactly( + "The method `kotlin.io.print` has been forbidden: print does not allow you to configure the output stream. Use a logger instead.", + "The method `kotlin.io.println` has been forbidden: println does not allow you to configure the output stream. Use a logger instead.", + ) } @Test @@ -38,11 +45,9 @@ class ForbiddenMethodCallSpec(val env: KotlinCoreEnvironment) { System.out.println("hello") } """ - val findings = - ForbiddenMethodCall(TestConfig(mapOf(METHODS to " "))).compileAndLintWithContext( - env, - code - ) + val findings = ForbiddenMethodCall( + TestConfig(mapOf(METHODS to listOf(" "))) + ).compileAndLintWithContext(env, code) assertThat(findings).isEmpty() } @@ -112,22 +117,6 @@ class ForbiddenMethodCallSpec(val env: KotlinCoreEnvironment) { assertThat(findings).hasTextLocations(48 to 64, 76 to 80) } - @Test - fun `should report multiple different methods config with sting`() { - val code = """ - import java.lang.System - fun main() { - System.out.println("hello") - System.gc() - } - """ - val findings = ForbiddenMethodCall( - TestConfig(mapOf(METHODS to "java.io.PrintStream.println, java.lang.System.gc")) - ).compileAndLintWithContext(env, code) - assertThat(findings).hasSize(2) - assertThat(findings).hasTextLocations(48 to 64, 76 to 80) - } - @Test fun `should report equals operator`() { val code = """ @@ -200,8 +189,9 @@ class ForbiddenMethodCallSpec(val env: KotlinCoreEnvironment) { val findings = ForbiddenMethodCall( TestConfig(mapOf(METHODS to listOf("java.time.LocalDate.now()"))) ).compileAndLintWithContext(env, code) - assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(5, 26) + assertThat(findings).hasStartSourceLocation(5, 26) + assertThat(findings[0]) + .hasMessage("The method `java.time.LocalDate.now()` has been forbidden in the detekt config.") } @Test @@ -219,7 +209,7 @@ class ForbiddenMethodCallSpec(val env: KotlinCoreEnvironment) { TestConfig(mapOf(METHODS to listOf("java.time.LocalDate.now(java.time.Clock)"))) ).compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(6, 27) + assertThat(findings).hasStartSourceLocation(6, 27) } @Test @@ -234,7 +224,7 @@ class ForbiddenMethodCallSpec(val env: KotlinCoreEnvironment) { TestConfig(mapOf(METHODS to listOf("java.time.LocalDate.of(kotlin.Int, kotlin.Int, kotlin.Int)"))) ).compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(3, 26) + assertThat(findings).hasStartSourceLocation(3, 26) } @Test @@ -249,7 +239,7 @@ class ForbiddenMethodCallSpec(val env: KotlinCoreEnvironment) { TestConfig(mapOf(METHODS to listOf("java.time.LocalDate.of(kotlin.Int,kotlin.Int,kotlin.Int)"))) ).compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(3, 26) + assertThat(findings).hasStartSourceLocation(3, 26) } @Test @@ -267,7 +257,7 @@ class ForbiddenMethodCallSpec(val env: KotlinCoreEnvironment) { TestConfig(mapOf(METHODS to listOf("io.gitlab.arturbosch.detekt.rules.style.`some, test`()"))) ).compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(6, 13) + assertThat(findings).hasStartSourceLocation(6, 13) } @Test @@ -290,7 +280,7 @@ class ForbiddenMethodCallSpec(val env: KotlinCoreEnvironment) { ) ).compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(6, 13) + assertThat(findings).hasStartSourceLocation(6, 13) } @Test @@ -429,7 +419,7 @@ class ForbiddenMethodCallSpec(val env: KotlinCoreEnvironment) { ).compileAndLintWithContext(env, code) assertThat(findings) .hasSize(1) - .hasSourceLocation(5, 16) + .hasStartSourceLocation(5, 16) } @Test @@ -439,7 +429,7 @@ class ForbiddenMethodCallSpec(val env: KotlinCoreEnvironment) { ).compileAndLintWithContext(env, code) assertThat(findings) .hasSize(1) - .hasSourceLocation(6, 9) + .hasStartSourceLocation(6, 9) } } @@ -457,7 +447,7 @@ class ForbiddenMethodCallSpec(val env: KotlinCoreEnvironment) { } """ val findings = - ForbiddenMethodCall(TestConfig(mapOf(METHODS to "java.util.Calendar.getFirstDayOfWeek"))).compileAndLintWithContext( + ForbiddenMethodCall(TestConfig(mapOf(METHODS to listOf("java.util.Calendar.getFirstDayOfWeek")))).compileAndLintWithContext( env, code ) @@ -465,7 +455,7 @@ class ForbiddenMethodCallSpec(val env: KotlinCoreEnvironment) { } @Test - fun `should not report property call`() { + fun `should report property getters call`() { val code = """ import java.util.Calendar @@ -475,11 +465,47 @@ class ForbiddenMethodCallSpec(val env: KotlinCoreEnvironment) { } """ val findings = - ForbiddenMethodCall(TestConfig(mapOf(METHODS to "java.util.Calendar.firstDayOfWeek"))).compileAndLintWithContext( + ForbiddenMethodCall(TestConfig(mapOf(METHODS to listOf("java.util.Calendar.getFirstDayOfWeek")))).compileAndLintWithContext( env, code ) - assertThat(findings).isEmpty() + assertThat(findings).hasSize(1) } } + + @Test + fun `should report property setters call`() { + val code = """ + import java.util.Calendar + + fun main() { + val calendar = Calendar.getInstance() + calendar.firstDayOfWeek = 1 + } + """ + val findings = + ForbiddenMethodCall(TestConfig(mapOf(METHODS to listOf("java.util.Calendar.setFirstDayOfWeek")))).compileAndLintWithContext( + env, + code + ) + assertThat(findings).hasSize(1) + } + + @Test + fun `should report reference call`() { + val code = """ + import java.util.Calendar + + fun main() { + val calendar = Calendar.getInstance() + calendar.let(calendar::compareTo) + } + """ + val findings = + ForbiddenMethodCall(TestConfig(mapOf(METHODS to listOf("java.util.Calendar.compareTo")))).compileAndLintWithContext( + env, + code + ) + assertThat(findings).hasSize(1) + } } diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenSuppressSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenSuppressSpec.kt index 668b7c941473..3fd6714e5c46 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenSuppressSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenSuppressSpec.kt @@ -24,7 +24,7 @@ internal class ForbiddenSuppressSpec { """ val findings = subject.compileAndLint(code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(3, 1) + assertThat(findings).hasStartSourceLocation(3, 1) assertThat(findings.first()).hasMessage( "Cannot @Suppress rule \"ARule\" due to the current configuration." ) @@ -38,7 +38,7 @@ internal class ForbiddenSuppressSpec { """ val findings = subject.compileAndLint(code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(1, 1) + assertThat(findings).hasStartSourceLocation(1, 1) assertThat(findings.first()).hasMessage( "Cannot @Suppress rule \"ARule\" due to the current configuration." ) @@ -54,7 +54,7 @@ internal class ForbiddenSuppressSpec { """ val findings = subject.compileAndLint(code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(3, 1) + assertThat(findings).hasStartSourceLocation(3, 1) assertThat(findings.first()).hasMessage( "Cannot @Suppress rule \"ARule\" due to the current configuration." ) @@ -72,7 +72,7 @@ internal class ForbiddenSuppressSpec { """ val findings = subject.compileAndLint(code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(4, 5) + assertThat(findings).hasStartSourceLocation(4, 5) assertThat(findings.first()).hasMessage( "Cannot @Suppress rule \"ARule\" due to the current configuration." ) @@ -133,7 +133,7 @@ internal class ForbiddenSuppressSpec { """ val findings = subject.compileAndLint(code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(1, 1) + assertThat(findings).hasStartSourceLocation(1, 1) assertThat(findings.first()).hasMessage( "Cannot @Suppress rules \"ARule\", \"BRule\" " + "due to the current configuration." @@ -150,7 +150,7 @@ internal class ForbiddenSuppressSpec { """ val findings = subject.compileAndLint(code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(3, 1) + assertThat(findings).hasStartSourceLocation(3, 1) assertThat(findings.first()).hasMessage( "Cannot @Suppress rule \"BRule\" due to the current configuration." ) diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/FunctionOnlyReturningConstantSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/FunctionOnlyReturningConstantSpec.kt index 9d132d003501..b674cf7dd2fd 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/FunctionOnlyReturningConstantSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/FunctionOnlyReturningConstantSpec.kt @@ -71,7 +71,9 @@ class FunctionOnlyReturningConstantSpec { } @Test - @DisplayName("does not report excluded annotated function which returns a constant when given \"kotlin.SinceKotlin\"") + @DisplayName( + "does not report excluded annotated function which returns a constant when given \"kotlin.SinceKotlin\"" + ) fun ignoreAnnotatedFunctionWhichReturnsConstantWhenGivenKotlinSinceKotlin() { val config = TestConfig(mapOf(EXCLUDE_ANNOTATED_FUNCTION to "kotlin.SinceKotlin")) val rule = FunctionOnlyReturningConstant(config) @@ -79,7 +81,9 @@ class FunctionOnlyReturningConstantSpec { } @Test - @DisplayName("does not report excluded annotated function which returns a constant when given listOf(\"kotlin.SinceKotlin\")") + @DisplayName( + "does not report excluded annotated function which returns a constant when given listOf(\"kotlin.SinceKotlin\")" + ) fun ignoreAnnotatedFunctionWhichReturnsConstantWhenGivenListOfKotlinSinceKotlin() { val config = TestConfig(mapOf(EXCLUDE_ANNOTATED_FUNCTION to listOf("kotlin.SinceKotlin"))) val rule = FunctionOnlyReturningConstant(config) diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/MagicNumberSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/MagicNumberSpec.kt index 3075bed107d0..ea9d7c09b4b2 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/MagicNumberSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/MagicNumberSpec.kt @@ -39,7 +39,7 @@ class MagicNumberSpec { @Test fun `should be reported when ignoredNumbers is empty`() { val findings = MagicNumber(TestConfig(mapOf(IGNORE_NUMBERS to emptyList()))).lint(code) - assertThat(findings).hasSourceLocation(1, 15) + assertThat(findings).hasStartSourceLocation(1, 15) } } @@ -73,7 +73,7 @@ class MagicNumberSpec { @Test fun `should be reported when ignoredNumbers is empty`() { val findings = MagicNumber(TestConfig(mapOf(IGNORE_NUMBERS to emptyList()))).lint(code) - assertThat(findings).hasSourceLocation(1, 13) + assertThat(findings).hasStartSourceLocation(1, 13) } } @@ -107,7 +107,7 @@ class MagicNumberSpec { @Test fun `should be reported when ignoredNumbers is empty`() { val findings = MagicNumber(TestConfig(mapOf(IGNORE_NUMBERS to emptyList()))).lint(code) - assertThat(findings).hasSourceLocation(1, 14) + assertThat(findings).hasStartSourceLocation(1, 14) } } @@ -124,7 +124,7 @@ class MagicNumberSpec { @Test fun `should be reported when ignoredNumbers is empty`() { val findings = MagicNumber(TestConfig(mapOf(IGNORE_NUMBERS to emptyList()))).lint(code) - assertThat(findings).hasSourceLocation(1, 15) + assertThat(findings).hasStartSourceLocation(1, 15) } } @@ -135,7 +135,7 @@ class MagicNumberSpec { @Test fun `should be reported by default`() { val findings = MagicNumber().lint(code) - assertThat(findings).hasSourceLocation(1, 15) + assertThat(findings).hasStartSourceLocation(1, 15) } @Test @@ -154,14 +154,14 @@ class MagicNumberSpec { fun `should not be ignored when ignoredNumbers contains 2 but not -2`() { val findings = MagicNumber(TestConfig(mapOf(IGNORE_NUMBERS to listOf("1", "2", "3", "-1", "0")))) .lint(code) - assertThat(findings).hasSourceLocation(1, 15) + assertThat(findings).hasStartSourceLocation(1, 15) } @Test fun `should not be ignored when ignoredNumbers contains 2 but not -2 config with string`() { val findings = MagicNumber(TestConfig(mapOf(IGNORE_NUMBERS to "1,2,3,-1,0"))) .lint(code) - assertThat(findings).hasSourceLocation(1, 15) + assertThat(findings).hasStartSourceLocation(1, 15) } } @@ -195,7 +195,7 @@ class MagicNumberSpec { @Test fun `should be reported when ignoredNumbers is empty`() { val findings = MagicNumber(TestConfig(mapOf(IGNORE_NUMBERS to emptyList()))).lint(code) - assertThat(findings).hasSourceLocation(1, 16) + assertThat(findings).hasStartSourceLocation(1, 16) } } @@ -229,7 +229,7 @@ class MagicNumberSpec { @Test fun `should be reported when ignoredNumbers is empty`() { val findings = MagicNumber(TestConfig(mapOf(IGNORE_NUMBERS to emptyList()))).lint(code) - assertThat(findings).hasSourceLocation(1, 13) + assertThat(findings).hasStartSourceLocation(1, 13) } } @@ -291,7 +291,7 @@ class MagicNumberSpec { @Test fun `should be reported by default`() { val findings = MagicNumber().lint(code) - assertThat(findings).hasSourceLocation(1, 13) + assertThat(findings).hasStartSourceLocation(1, 13) } @Test @@ -321,7 +321,7 @@ class MagicNumberSpec { fun `should be reported`() { val findings = MagicNumber().lint(code) assertThat(findings) - .hasSourceLocations( + .hasStartSourceLocations( SourceLocation(1, 17), SourceLocation(1, 21), SourceLocation(1, 24), @@ -345,7 +345,7 @@ class MagicNumberSpec { @Test fun `should be reported`() { val findings = MagicNumber().lint(code) - assertThat(findings).hasSourceLocations( + assertThat(findings).hasStartSourceLocations( SourceLocation(3, 9), SourceLocation(3, 21), SourceLocation(4, 9), @@ -405,7 +405,7 @@ class MagicNumberSpec { @Test fun `should be reported by default`() { val findings = MagicNumber().lint(code) - assertThat(findings).hasSourceLocation(1, 12) + assertThat(findings).hasStartSourceLocation(1, 12) } @Test @@ -486,7 +486,7 @@ class MagicNumberSpec { val findings = MagicNumber(config).lint(code) assertThat(findings) - .hasSourceLocations( + .hasStartSourceLocations( SourceLocation(1, 17), SourceLocation(3, 24), SourceLocation(4, 33), @@ -601,7 +601,7 @@ class MagicNumberSpec { ) val findings = MagicNumber(config).lint(code) - assertThat(findings).hasSourceLocation(4, 35) + assertThat(findings).hasStartSourceLocation(4, 35) } @Test @@ -616,7 +616,7 @@ class MagicNumberSpec { val findings = MagicNumber(config).lint(code) assertThat(findings) - .hasSourceLocations( + .hasStartSourceLocations( SourceLocation(4, 35), SourceLocation(5, 43) ) diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/MaxLineLengthSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/MaxLineLengthSpec.kt index fb2abb65d99f..b9a814dca7f1 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/MaxLineLengthSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/MaxLineLengthSpec.kt @@ -258,7 +258,7 @@ class MaxLineLengthSpec { rule.visit(fileContent) assertThat(rule.findings).hasSize(1) - assertThat(rule.findings).hasSourceLocations(SourceLocation(6, 5)) + assertThat(rule.findings).hasStartSourceLocations(SourceLocation(6, 5)) } } } diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/MayBeConstSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/MayBeConstSpec.kt index f3786d258b57..97dd7c3da6fc 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/MayBeConstSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/MayBeConstSpec.kt @@ -87,7 +87,7 @@ class MayBeConstSpec { val x = 1 """ subject.compileAndLint(code) - assertThat(subject.findings).hasSize(1).hasSourceLocations( + assertThat(subject.findings).hasSize(1).hasStartSourceLocations( SourceLocation(1, 5) ) } @@ -98,7 +98,7 @@ class MayBeConstSpec { @JvmField val x = 1 """ subject.compileAndLint(code) - assertThat(subject.findings).hasSize(1).hasSourceLocations( + assertThat(subject.findings).hasSize(1).hasStartSourceLocations( SourceLocation(1, 15) ) } @@ -111,7 +111,7 @@ class MayBeConstSpec { } """ subject.compileAndLint(code) - assertThat(subject.findings).hasSize(1).hasSourceLocations( + assertThat(subject.findings).hasSize(1).hasStartSourceLocations( SourceLocation(2, 19) ) } @@ -126,7 +126,7 @@ class MayBeConstSpec { } """ subject.compileAndLint(code) - assertThat(subject.findings).hasSize(1).hasSourceLocations( + assertThat(subject.findings).hasSize(1).hasStartSourceLocations( SourceLocation(3, 13) ) } @@ -143,7 +143,7 @@ class MayBeConstSpec { } """ subject.compileAndLint(code) - assertThat(subject.findings).hasSize(1).hasSourceLocations( + assertThat(subject.findings).hasSize(1).hasStartSourceLocations( SourceLocation(3, 9) ) } @@ -159,7 +159,7 @@ class MayBeConstSpec { } """ subject.compileAndLint(code) - assertThat(subject.findings).hasSize(1).hasSourceLocations( + assertThat(subject.findings).hasSize(1).hasStartSourceLocations( SourceLocation(4, 13) ) } @@ -173,7 +173,7 @@ class MayBeConstSpec { } """ subject.compileAndLint(code) - assertThat(subject.findings).hasSize(1).hasSourceLocations( + assertThat(subject.findings).hasSize(1).hasStartSourceLocations( SourceLocation(3, 9) ) } @@ -187,7 +187,7 @@ class MayBeConstSpec { } """ subject.compileAndLint(code) - assertThat(subject.findings).hasSize(1).hasSourceLocations( + assertThat(subject.findings).hasSize(1).hasStartSourceLocations( SourceLocation(3, 9) ) } @@ -203,7 +203,7 @@ class MayBeConstSpec { } """ subject.compileAndLint(code) - assertThat(subject.findings).hasSize(1).hasSourceLocations( + assertThat(subject.findings).hasSize(1).hasStartSourceLocations( SourceLocation(5, 9) ) } @@ -217,7 +217,7 @@ class MayBeConstSpec { } """ subject.compileAndLint(code) - assertThat(subject.findings).hasSize(1).hasSourceLocations( + assertThat(subject.findings).hasSize(1).hasStartSourceLocations( SourceLocation(3, 17) ) } @@ -363,7 +363,7 @@ class MayBeConstSpec { } """ subject.compileAndLint(code) - assertThat(subject.findings).hasSize(3).hasSourceLocations( + assertThat(subject.findings).hasSize(3).hasStartSourceLocations( SourceLocation(4, 13), SourceLocation(7, 17), SourceLocation(11, 13) diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/NewLineAtEndOfFileSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/NewLineAtEndOfFileSpec.kt index 85d79712cd38..b1cf36d45ca1 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/NewLineAtEndOfFileSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/NewLineAtEndOfFileSpec.kt @@ -18,7 +18,7 @@ class NewLineAtEndOfFileSpec { fun `should flag a kt file not containing new line at the end`() { val code = "class Test" assertThat(subject.compileAndLint(code)).hasSize(1) - .hasSourceLocation(1, 11) + .hasStartSourceLocation(1, 11) } @Test diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ObjectLiteralToLambdaSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ObjectLiteralToLambdaSpec.kt index 7452591d2961..ca0b3e89673e 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ObjectLiteralToLambdaSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ObjectLiteralToLambdaSpec.kt @@ -33,7 +33,7 @@ class ObjectLiteralToLambdaSpec { subject.compileAndLintWithContext(env, code) .assert() .hasSize(1) - .hasSourceLocations(SourceLocation(4, 9)) + .hasStartSourceLocations(SourceLocation(4, 9)) } @Test @@ -52,7 +52,7 @@ class ObjectLiteralToLambdaSpec { subject.compileAndLintWithContext(env, code) .assert() .hasSize(1) - .hasSourceLocations(SourceLocation(5, 5)) + .hasStartSourceLocations(SourceLocation(5, 5)) } @Test @@ -73,7 +73,7 @@ class ObjectLiteralToLambdaSpec { subject.compileAndLintWithContext(env, code) .assert() .hasSize(1) - .hasSourceLocations(SourceLocation(6, 9)) + .hasStartSourceLocations(SourceLocation(6, 9)) } @Test @@ -91,7 +91,7 @@ class ObjectLiteralToLambdaSpec { subject.compileAndLintWithContext(env, code) .assert() .hasSize(1) - .hasSourceLocations(SourceLocation(4, 9)) + .hasStartSourceLocations(SourceLocation(4, 9)) } @Test @@ -109,7 +109,7 @@ class ObjectLiteralToLambdaSpec { subject.compileAndLintWithContext(env, code) .assert() .hasSize(1) - .hasSourceLocations(SourceLocation(5, 9)) + .hasStartSourceLocations(SourceLocation(5, 9)) } @Test @@ -130,7 +130,7 @@ class ObjectLiteralToLambdaSpec { subject.compileAndLintWithContext(env, code) .assert() .hasSize(1) - .hasSourceLocations(SourceLocation(7, 5)) + .hasStartSourceLocations(SourceLocation(7, 5)) } @Test @@ -146,7 +146,7 @@ class ObjectLiteralToLambdaSpec { subject.compileAndLintWithContext(env, code) .assert() .hasSize(1) - .hasSourceLocations(SourceLocation(4, 9)) + .hasStartSourceLocations(SourceLocation(4, 9)) } } @@ -337,7 +337,7 @@ class ObjectLiteralToLambdaSpec { subject.compileAndLintWithContext(env, code) .assert() .hasSize(1) - .hasSourceLocations(SourceLocation(1, 9)) + .hasStartSourceLocations(SourceLocation(1, 9)) } @Test @@ -353,7 +353,7 @@ class ObjectLiteralToLambdaSpec { subject.compileAndLintWithContext(env, code) .assert() .hasSize(1) - .hasSourceLocations(SourceLocation(2, 9)) + .hasStartSourceLocations(SourceLocation(2, 9)) } @Test @@ -466,7 +466,7 @@ class ObjectLiteralToLambdaSpec { subject.compileAndLintWithContext(env, code) .assert() .hasSize(1) - .hasSourceLocations(SourceLocation(6, 5)) + .hasStartSourceLocations(SourceLocation(6, 5)) } @Test @@ -489,7 +489,7 @@ class ObjectLiteralToLambdaSpec { subject.compileAndLintWithContext(env, code) .assert() .hasSize(1) - .hasSourceLocations(SourceLocation(7, 9)) + .hasStartSourceLocations(SourceLocation(7, 9)) } @Test @@ -534,7 +534,7 @@ class ObjectLiteralToLambdaSpec { subject.compileAndLintWithContext(env, code) .assert() .hasSize(1) - .hasSourceLocations(SourceLocation(5, 19)) + .hasStartSourceLocations(SourceLocation(5, 19)) } } } diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/OptionalWhenBracesSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/OptionalWhenBracesSpec.kt index 05991eed9587..72171b786809 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/OptionalWhenBracesSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/OptionalWhenBracesSpec.kt @@ -66,7 +66,7 @@ class OptionalWhenBracesSpec { """ assertThat(subject.compileAndLint(code)) .hasSize(2) - .hasSourceLocations(SourceLocation(7, 17), SourceLocation(10, 17)) + .hasStartSourceLocations(SourceLocation(7, 17), SourceLocation(10, 17)) } @Test @@ -87,7 +87,7 @@ class OptionalWhenBracesSpec { """ assertThat(subject.compileAndLint(code)) .hasSize(1) - .hasSourceLocations(SourceLocation(3, 9)) + .hasStartSourceLocations(SourceLocation(3, 9)) } @Nested diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ProtectedMemberInFinalClassSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ProtectedMemberInFinalClassSpec.kt index 1bc59f6c5829..b1e473ff2604 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ProtectedMemberInFinalClassSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ProtectedMemberInFinalClassSpec.kt @@ -22,7 +22,7 @@ class ProtectedMemberInFinalClassSpec { """ val findings = subject.compileAndLint(code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(2, 5) + assertThat(findings).hasStartSourceLocation(2, 5) } @Test @@ -37,7 +37,7 @@ class ProtectedMemberInFinalClassSpec { """ val findings = subject.compileAndLint(code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(3, 5) + assertThat(findings).hasStartSourceLocation(3, 5) } @Test @@ -49,7 +49,7 @@ class ProtectedMemberInFinalClassSpec { """ val findings = subject.compileAndLint(code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(2, 5) + assertThat(findings).hasStartSourceLocation(2, 5) } @Test @@ -63,7 +63,7 @@ class ProtectedMemberInFinalClassSpec { """ val findings = subject.compileAndLint(code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(3, 9) + assertThat(findings).hasStartSourceLocation(3, 9) } @Test @@ -77,7 +77,7 @@ class ProtectedMemberInFinalClassSpec { """ val findings = subject.compileAndLint(code) assertThat(findings).hasSize(2) - assertThat(findings).hasSourceLocations( + assertThat(findings).hasStartSourceLocations( SourceLocation(2, 5), SourceLocation(3, 9) ) @@ -96,7 +96,7 @@ class ProtectedMemberInFinalClassSpec { """ val findings = subject.compileAndLint(code) assertThat(findings).hasSize(3) - assertThat(findings).hasSourceLocations( + assertThat(findings).hasStartSourceLocations( SourceLocation(2, 5), SourceLocation(2, 5), SourceLocation(4, 13) @@ -116,7 +116,7 @@ class ProtectedMemberInFinalClassSpec { """ val findings = subject.compileAndLint(code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(4, 13) + assertThat(findings).hasStartSourceLocation(4, 13) } @Test @@ -130,7 +130,7 @@ class ProtectedMemberInFinalClassSpec { """ val findings = subject.compileAndLint(code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(3, 9) + assertThat(findings).hasStartSourceLocation(3, 9) } @Test @@ -140,7 +140,7 @@ class ProtectedMemberInFinalClassSpec { """ val findings = subject.compileAndLint(code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(1, 42) + assertThat(findings).hasStartSourceLocation(1, 42) } } diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/RedundantHigherOrderMapUsageSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/RedundantHigherOrderMapUsageSpec.kt index d73d3da62697..6d562f0f2e1a 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/RedundantHigherOrderMapUsageSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/RedundantHigherOrderMapUsageSpec.kt @@ -24,7 +24,7 @@ class RedundantHigherOrderMapUsageSpec(val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(4, 10) + assertThat(findings).hasStartSourceLocation(4, 10) assertThat(findings[0]).hasMessage("This 'map' call can be removed.") } @@ -44,7 +44,7 @@ class RedundantHigherOrderMapUsageSpec(val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(5, 10) + assertThat(findings).hasStartSourceLocation(5, 10) assertThat(findings[0]).hasMessage("This 'map' call can be replaced with 'onEach' or 'forEach'.") } diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ReturnCountSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ReturnCountSpec.kt index 9abd3b37a9b5..1ce28adf704a 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ReturnCountSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ReturnCountSpec.kt @@ -347,7 +347,7 @@ class ReturnCountSpec { TestConfig( mapOf( MAX to "2", - EXCLUDED_FUNCTIONS to "test1,test2" + EXCLUDED_FUNCTIONS to listOf("test1", "test2") ) ) ).compileAndLint(code) diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/TrimMultilineRawStringSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/TrimMultilineRawStringSpec.kt new file mode 100644 index 000000000000..d09235756ac1 --- /dev/null +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/TrimMultilineRawStringSpec.kt @@ -0,0 +1,87 @@ +@file:Suppress("StringTemplate") + +package io.gitlab.arturbosch.detekt.rules.style + +import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.test.compileAndLint +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class TrimMultilineRawStringSpec { + val subject = TrimMultilineRawString(Config.empty) + + @Test + fun `raises multiline raw strings without trim`() { + val code = """ + val a = ${TQ} + Hello world! + ${TQ} + """ + subject.compileAndLint(code) + assertThat(subject.findings).hasSize(1) + } + + @Test + fun `raises multiline raw strings with lenght`() { + val code = """ + val a = ${TQ} + Hello world! + ${TQ}.length + """ + subject.compileAndLint(code) + assertThat(subject.findings).hasSize(1) + } + + @Test + fun `doesn't raise multiline raw strings without trimIndent`() { + val code = """ + val a = ${TQ} + Hello world! + ${TQ}.trimIndent() + """ + subject.compileAndLint(code) + assertThat(subject.findings).isEmpty() + } + + @Test + fun `doesn't raise multiline raw strings without trimMargin`() { + val code = """ + val a = ${TQ} + |Hello world! + ${TQ}.trimMargin() + """ + subject.compileAndLint(code) + assertThat(subject.findings).isEmpty() + } + + @Test + fun `doesn't raise multiline raw strings without trimMargin with parameter`() { + val code = """ + val a = ${TQ} + >Hello world! + ${TQ}.trimMargin(">") + """ + subject.compileAndLint(code) + assertThat(subject.findings).isEmpty() + } + + @Test + fun `don't raise one line raw strings`() { + val code = """ + val a = ${TQ}Hello world!${TQ} + """ + subject.compileAndLint(code) + assertThat(subject.findings).isEmpty() + } + + @Test + fun `doesn't raise if it is not a raw string`() { + val code = """ + val a = "Hello world!" + """ + subject.compileAndLint(code) + assertThat(subject.findings).isEmpty() + } +} + +private const val TQ = "\"\"\"" diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryAbstractClassSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryAbstractClassSpec.kt index d9b65a797bda..b6b2066444c4 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryAbstractClassSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryAbstractClassSpec.kt @@ -31,7 +31,7 @@ class UnnecessaryAbstractClassSpec(val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertFindingMessage(findings, message) - assertThat(findings).hasSourceLocation(1, 16) + assertThat(findings).hasStartSourceLocation(1, 16) } @Nested @@ -41,7 +41,7 @@ class UnnecessaryAbstractClassSpec(val env: KotlinCoreEnvironment) { val code = "abstract class A" val findings = subject.compileAndLintWithContext(env, code) assertFindingMessage(findings, message) - assertThat(findings).hasSourceLocation(1, 16) + assertThat(findings).hasStartSourceLocation(1, 16) } @Test @@ -204,7 +204,7 @@ class UnnecessaryAbstractClassSpec(val env: KotlinCoreEnvironment) { val code = "abstract class A(val i: Int)" val findings = subject.compileAndLintWithContext(env, code) assertFindingMessage(findings, message) - assertThat(findings).hasSourceLocation(1, 16) + assertThat(findings).hasStartSourceLocation(1, 16) } @Test @@ -219,7 +219,7 @@ class UnnecessaryAbstractClassSpec(val env: KotlinCoreEnvironment) { val code = "abstract class A(i: Int)" val findings = subject.compileAndLintWithContext(env, code) assertFindingMessage(findings, message) - assertThat(findings).hasSourceLocation(1, 16) + assertThat(findings).hasStartSourceLocation(1, 16) } @Test diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryParenthesesSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryParenthesesSpec.kt index e896d9c24621..809e94e0bc0d 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryParenthesesSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryParenthesesSpec.kt @@ -1,33 +1,39 @@ package io.gitlab.arturbosch.detekt.rules.style -import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.lint import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource class UnnecessaryParenthesesSpec { - val subject = UnnecessaryParentheses(Config.empty) - - @Test - fun `with unnecessary parentheses on val assignment`() { + @ParameterizedTest + @MethodSource("cases") + fun `with unnecessary parentheses on val assignment`(testCase: RuleTestCase) { val code = "val local = (5)" - assertThat(subject.lint(code)).hasSize(1) + + assertThat(testCase.rule.lint(code)).hasSize(1) } - @Test - fun `with unnecessary parentheses on val assignment operation`() { + @ParameterizedTest + @MethodSource("cases") + fun `with unnecessary parentheses on val assignment operation`(testCase: RuleTestCase) { val code = "val local = (5 + 3)" - assertThat(subject.lint(code)).hasSize(1) + + assertThat(testCase.rule.lint(code)).hasSize(1) } - @Test - fun `with unnecessary parentheses on function call`() { + @ParameterizedTest + @MethodSource("cases") + fun `with unnecessary parentheses on function call`(testCase: RuleTestCase) { val code = "val local = 3.plus((5))" - assertThat(subject.lint(code)).hasSize(1) + + assertThat(testCase.rule.lint(code)).hasSize(1) } - @Test - fun `unnecessary parentheses in other parentheses`() { + @ParameterizedTest + @MethodSource("cases") + fun `unnecessary parentheses in other parentheses`(testCase: RuleTestCase) { val code = """ fun x(a: String, b: String) { if ((a equals b)) { @@ -35,11 +41,13 @@ class UnnecessaryParenthesesSpec { } } """ - assertThat(subject.lint(code)).hasSize(1) + + assertThat(testCase.rule.lint(code)).hasSize(1) } - @Test - fun `does not report unnecessary parentheses around lambdas`() { + @ParameterizedTest + @MethodSource("cases") + fun `does not report unnecessary parentheses around lambdas`(testCase: RuleTestCase) { val code = """ fun function (a: (input: String) -> Unit) { a.invoke("TEST") @@ -49,11 +57,13 @@ class UnnecessaryParenthesesSpec { function({ input -> println(input) }) } """ - assertThat(subject.lint(code)).isEmpty() + + assertThat(testCase.rule.lint(code)).isEmpty() } - @Test - fun `doesn't report function calls containing lambdas and other parameters`() { + @ParameterizedTest + @MethodSource("cases") + fun `doesn't report function calls containing lambdas and other parameters`(testCase: RuleTestCase) { val code = """ fun function (integer: Int, a: (input: String) -> Unit) { a.invoke("TEST") @@ -63,21 +73,25 @@ class UnnecessaryParenthesesSpec { function(1, { input -> println(input) }) } """ - assertThat(subject.lint(code)).isEmpty() + + assertThat(testCase.rule.lint(code)).isEmpty() } - @Test - fun `does not report unnecessary parentheses when assigning a lambda to a val`() { + @ParameterizedTest + @MethodSource("cases") + fun `does not report unnecessary parentheses when assigning a lambda to a val`(testCase: RuleTestCase) { val code = """ fun f() { instance.copy(value = { false }) } """ - assertThat(subject.lint(code)).isEmpty() + + assertThat(testCase.rule.lint(code)).isEmpty() } - @Test - fun `does not report well behaved parentheses`() { + @ParameterizedTest + @MethodSource("cases") + fun `does not report well behaved parentheses`(testCase: RuleTestCase) { val code = """ fun x(a: String, b: String) { if (a equals b) { @@ -85,11 +99,13 @@ class UnnecessaryParenthesesSpec { } } """ - assertThat(subject.lint(code)).isEmpty() + + assertThat(testCase.rule.lint(code)).isEmpty() } - @Test - fun `does not report well behaved parentheses in super constructors`() { + @ParameterizedTest + @MethodSource("cases") + fun `does not report well behaved parentheses in super constructors`(testCase: RuleTestCase) { val code = """ class TestSpek : SubjectSpek({ describe("a simple test") { @@ -98,11 +114,13 @@ class UnnecessaryParenthesesSpec { } }) """ - assertThat(subject.lint(code)).isEmpty() + + assertThat(testCase.rule.lint(code)).isEmpty() } - @Test - fun `does not report well behaved parentheses in constructors`() { + @ParameterizedTest + @MethodSource("cases") + fun `does not report well behaved parentheses in constructors`(testCase: RuleTestCase) { val code = """ class TestSpek({ describe("a simple test") { @@ -111,11 +129,13 @@ class UnnecessaryParenthesesSpec { } }) """ - assertThat(subject.lint(code)).isEmpty() + + assertThat(testCase.rule.lint(code)).isEmpty() } - @Test - fun `should not report lambdas within super constructor calls`() { + @ParameterizedTest + @MethodSource("cases") + fun `should not report lambdas within super constructor calls`(testCase: RuleTestCase) { val code = """ class Clazz( private val func: (X, Y) -> Z @@ -123,11 +143,13 @@ class UnnecessaryParenthesesSpec { constructor() : this({ first, second -> true }) } """ - assertThat(subject.lint(code)).isEmpty() + + assertThat(testCase.rule.lint(code)).isEmpty() } - @Test - fun `should not report call to function with two lambda parameters with one as block body`() { + @ParameterizedTest + @MethodSource("cases") + fun `should not report call to function with two lambda parameters with one as block body`(testCase: RuleTestCase) { val code = """ class Clazz { fun test(first: (Int) -> Unit, second: (Int) -> Unit) { @@ -140,11 +162,13 @@ class UnnecessaryParenthesesSpec { } } """ - assertThat(subject.lint(code)).isEmpty() + + assertThat(testCase.rule.lint(code)).isEmpty() } - @Test - fun `should not report call to function with two lambda parameters`() { + @ParameterizedTest + @MethodSource("cases") + fun `should not report call to function with two lambda parameters`(testCase: RuleTestCase) { val code = """ class Clazz { fun test(first: (Int) -> Unit, second: (Int) -> Unit) { @@ -157,11 +181,15 @@ class UnnecessaryParenthesesSpec { } } """ - assertThat(subject.lint(code)).isEmpty() + + assertThat(testCase.rule.lint(code)).isEmpty() } - @Test - fun `should not report call to function with multiple lambdas as parameters but also other parameters`() { + @ParameterizedTest + @MethodSource("cases") + fun `should not report call to function with multiple lambdas as parameters but also other parameters`( + testCase: RuleTestCase, + ) { val code = """ class Clazz { fun test(text: String, first: () -> Unit, second: () -> Unit) { @@ -174,14 +202,133 @@ class UnnecessaryParenthesesSpec { } } """ - assertThat(subject.lint(code)).isEmpty() + + assertThat(testCase.rule.lint(code)).isEmpty() } - @Test - fun `should not report interface delegation with parenthesis - #3851`() { + @ParameterizedTest + @MethodSource("cases") + fun `should not report interface delegation with parenthesis - #3851`(testCase: RuleTestCase) { val code = """ class Clazz: Comparable by ("hello".filter { it != 'l' }) """ - assertThat(subject.lint(code)).isEmpty() + + assertThat(testCase.rule.lint(code)).isEmpty() + } + + @ParameterizedTest + @MethodSource("cases") + fun `numeric expressions when precedence is unclear`(testCase: RuleTestCase) { + val code = """ + val a1 = (1 * 2) + 3 + val a2 = (1 / 2) + 3 + val a3 = (1 % 2) + 3 + + val b1 = 3 + (1 * 2) + val b2 = 3 + (1 / 2) + val b3 = 3 + (1 % 2) + + val c = (4 + 5) * 3 // parens required + """ + + assertThat(testCase.rule.lint(code)).hasSize(if (testCase.allowForUnclearPrecedence) 0 else 6) + } + + @ParameterizedTest + @MethodSource("cases") + fun `numeric expressions when precedence is clear`(testCase: RuleTestCase) { + val code = """ + val a1 = (1 + 2) + val a2 = (1 * 2) + val a3 = (1 + 2 * 3) + val b1 = (1 + 2) + 3 + val b2 = (1 * 2) * 3 + """ + + assertThat(testCase.rule.lint(code)).hasSize(5) + } + + @ParameterizedTest + @MethodSource("cases") + fun `boolean expressions when precedence is unclear`(testCase: RuleTestCase) { + val code = """ + val a1 = (true && false) || false + val a2 = (true && false) || (false && true) // 2 warnings when disallowed + val b = false || (true && false) + val c = (true || false) && false // parens required + """ + + assertThat(testCase.rule.lint(code)).hasSize(if (testCase.allowForUnclearPrecedence) 0 else 4) + } + + @ParameterizedTest + @MethodSource("cases") + fun `boolean expressions when precedence is clear`(testCase: RuleTestCase) { + val code = """ + val a1 = (true && false) + val a2 = (true || false) + val a3 = (true && false || false) + val b1 = (true && false) && false + val b2 = (true || false) || false + """ + + assertThat(testCase.rule.lint(code)).hasSize(5) + } + + @ParameterizedTest + @MethodSource("cases") + fun `infix operators when precedence is unclear`(testCase: RuleTestCase) { + val code = """ + val d = (true and false) or false + val e = false or (true and false) // parens required + """ + + assertThat(testCase.rule.lint(code)).hasSize(if (testCase.allowForUnclearPrecedence) 0 else 1) + } + + @ParameterizedTest + @MethodSource("cases") + fun `elvis operators when precedence is unclear`(testCase: RuleTestCase) { + val code = """ + val a1 = null ?: (1 to 2) // parens required + val a2 = (null ?: 1) to 2 + + val b1 = null ?: (1 == 2) // parens required + val b2 = (null ?: 1) == 2 + + val c1 = null ?: (1 > 2) // parens required + val c2 = (null ?: 1) > 2 + + val d1 = null ?: (1 in 2) // parens required + val d2 = (null ?: 1) in 2 + """ + + assertThat(testCase.rule.lint(code)).hasSize(if (testCase.allowForUnclearPrecedence) 0 else 4) + } + + @ParameterizedTest + @MethodSource("cases") + fun `multiple wrapping parentheses`(testCase: RuleTestCase) { + val code = """ + val a = ((false || (((true && false))))) + """ + + assertThat(testCase.rule.lint(code)).hasSize(if (testCase.allowForUnclearPrecedence) 4 else 5) + } + + companion object { + class RuleTestCase(val allowForUnclearPrecedence: Boolean) { + val rule = UnnecessaryParentheses( + TestConfig(mapOf("allowForUnclearPrecedence" to allowForUnclearPrecedence)) + ) + } + + @JvmStatic + fun cases(): List { + return listOf( + RuleTestCase(allowForUnclearPrecedence = false), + RuleTestCase(allowForUnclearPrecedence = true), + ) + } } } diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedImportsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedImportsSpec.kt index d9425c640d53..ca6fbf5ca670 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedImportsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedImportsSpec.kt @@ -634,4 +634,36 @@ class UnusedImportsSpec(val env: KotlinCoreEnvironment) { assertThat(subject.lintWithContext(env, mainFile, additionalFile)).isEmpty() } + + @Test + fun `does not report imports which detekt cannot resolve but have string matches`() { + val mainFile = """ + import x.y.z.foo + import x.y.z.Bar + + fun test() { + foo() + foo("", 123) + foo + + Bar().baz() + } + """ + + assertThat(subject.lintWithContext(env, mainFile)).isEmpty() + } + + @Test + fun `reports imports which detekt cannot resolve and do not have string matches`() { + val mainFile = """ + import x.y.z.foo + import x.y.z.Bar + + fun test() { + 2 + 3 + } + """ + + assertThat(subject.lintWithContext(env, mainFile)).hasSize(2) + } } diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedPrivateClassSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedPrivateClassSpec.kt index 84cc5bacf1db..ff7a305e5d15 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedPrivateClassSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedPrivateClassSpec.kt @@ -23,7 +23,7 @@ class UnusedPrivateClassSpec { val findings = subject.compileAndLint(code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(1, 1) + assertThat(findings).hasStartSourceLocation(1, 1) } @Nested @@ -39,7 +39,7 @@ class UnusedPrivateClassSpec { val findings = subject.compileAndLint(code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(1, 1) + assertThat(findings).hasStartSourceLocation(1, 1) } @Test @@ -52,7 +52,7 @@ class UnusedPrivateClassSpec { val findings = subject.compileAndLint(code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(2, 1) + assertThat(findings).hasStartSourceLocation(2, 1) } @Test @@ -435,7 +435,7 @@ class UnusedPrivateClassSpec { """ val findings = UnusedPrivateClass().lint(code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(10, 5) + assertThat(findings).hasStartSourceLocation(10, 5) } } } diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedPrivateMemberSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedPrivateMemberSpec.kt index e8b7078e96a7..f7e39e0cb5d2 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedPrivateMemberSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedPrivateMemberSpec.kt @@ -1320,7 +1320,7 @@ class UnusedPrivateMemberSpec(val env: KotlinCoreEnvironment) { } """ val findings = subject.compileAndLintWithContext(env, code) - assertThat(findings).hasSize(1).hasSourceLocations( + assertThat(findings).hasSize(1).hasStartSourceLocations( SourceLocation(3, 30) ) } @@ -1606,7 +1606,7 @@ class UnusedPrivateMemberSpec(val env: KotlinCoreEnvironment) { private fun foo() = 1 } """ - assertThat(subject.lint(code)).hasSize(1).hasSourceLocation(5, 17) + assertThat(subject.lint(code)).hasSize(1).hasStartSourceLocation(5, 17) } @Test @@ -1619,7 +1619,7 @@ class UnusedPrivateMemberSpec(val env: KotlinCoreEnvironment) { private val foo = 1 } """ - assertThat(subject.lint(code)).hasSize(1).hasSourceLocation(5, 17) + assertThat(subject.lint(code)).hasSize(1).hasStartSourceLocation(5, 17) } @Test @@ -1634,7 +1634,7 @@ class UnusedPrivateMemberSpec(val env: KotlinCoreEnvironment) { ) = 1 } """ - assertThat(subject.lint(code)).hasSize(1).hasSourceLocation(6, 9) + assertThat(subject.lint(code)).hasSize(1).hasStartSourceLocation(6, 9) } } } diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseCheckOrErrorSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseCheckOrErrorSpec.kt index 28d7ff8e7277..3e67126601fd 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseCheckOrErrorSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseCheckOrErrorSpec.kt @@ -22,7 +22,7 @@ class UseCheckOrErrorSpec(val env: KotlinCoreEnvironment) { if (a < 0) throw IllegalStateException() } """ - assertThat(subject.lint(code)).hasSourceLocation(3, 16) + assertThat(subject.lint(code)).hasStartSourceLocation(3, 16) } @Test @@ -33,7 +33,7 @@ class UseCheckOrErrorSpec(val env: KotlinCoreEnvironment) { if (a < 0) throw IllegalStateException("More details") } """ - assertThat(subject.lint(code)).hasSourceLocation(3, 16) + assertThat(subject.lint(code)).hasStartSourceLocation(3, 16) } @Test @@ -45,7 +45,7 @@ class UseCheckOrErrorSpec(val env: KotlinCoreEnvironment) { else -> throw IllegalStateException() } """ - assertThat(subject.lint(code)).hasSourceLocation(4, 17) + assertThat(subject.lint(code)).hasStartSourceLocation(4, 17) } @Test @@ -56,7 +56,7 @@ class UseCheckOrErrorSpec(val env: KotlinCoreEnvironment) { if (a < 0) throw java.lang.IllegalStateException() } """ - assertThat(subject.lint(code)).hasSourceLocation(3, 16) + assertThat(subject.lint(code)).hasStartSourceLocation(3, 16) } @Test @@ -67,7 +67,7 @@ class UseCheckOrErrorSpec(val env: KotlinCoreEnvironment) { if (a < 0) throw kotlin.IllegalStateException() } """ - assertThat(subject.lint(code)).hasSourceLocation(3, 16) + assertThat(subject.lint(code)).hasStartSourceLocation(3, 16) } @Test @@ -106,13 +106,13 @@ class UseCheckOrErrorSpec(val env: KotlinCoreEnvironment) { @Test fun `reports an issue if the exception thrown as the only action in a function`() { val code = """fun doThrow() = throw IllegalStateException("message")""" - assertThat(subject.lint(code)).hasSourceLocation(1, 17) + assertThat(subject.lint(code)).hasStartSourceLocation(1, 17) } @Test fun `reports an issue if the exception thrown as the only action in a function block`() { val code = """fun doThrow() { throw IllegalStateException("message") }""" - assertThat(subject.lint(code)).hasSourceLocation(1, 17) + assertThat(subject.lint(code)).hasStartSourceLocation(1, 17) } @Test diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseIfEmptyOrIfBlankSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseIfEmptyOrIfBlankSpec.kt index 539199c3f69c..6955ff5e4795 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseIfEmptyOrIfBlankSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseIfEmptyOrIfBlankSpec.kt @@ -26,7 +26,7 @@ class UseIfEmptyOrIfBlankSpec(val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(4, 29) + assertThat(findings).hasStartSourceLocation(4, 29) assertThat(findings[0]).hasMessage("This 'isBlank' call can be replaced with 'ifBlank'") } @@ -45,7 +45,7 @@ class UseIfEmptyOrIfBlankSpec(val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(4, 29) + assertThat(findings).hasStartSourceLocation(4, 29) assertThat(findings[0]).hasMessage("This 'isNotBlank' call can be replaced with 'ifBlank'") } @@ -61,7 +61,7 @@ class UseIfEmptyOrIfBlankSpec(val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(4, 29) + assertThat(findings).hasStartSourceLocation(4, 29) assertThat(findings[0]).hasMessage("This 'isEmpty' call can be replaced with 'ifEmpty'") } @@ -80,7 +80,7 @@ class UseIfEmptyOrIfBlankSpec(val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(4, 29) + assertThat(findings).hasStartSourceLocation(4, 29) assertThat(findings[0]).hasMessage("This 'isNotEmpty' call can be replaced with 'ifEmpty'") } diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseIsNullOrEmptySpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseIsNullOrEmptySpec.kt index a0e436052325..b7a6f6844caf 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseIsNullOrEmptySpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseIsNullOrEmptySpec.kt @@ -24,7 +24,7 @@ class UseIsNullOrEmptySpec(val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(2, 9) + assertThat(findings).hasStartSourceLocation(2, 9) assertThat(findings[0]).hasMessage( "This 'x == null || x.isEmpty()' can be replaced with 'isNullOrEmpty()' call" ) diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseOrEmptySpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseOrEmptySpec.kt index ea9b2900b854..09d1466dcd26 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseOrEmptySpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseOrEmptySpec.kt @@ -22,7 +22,7 @@ class UseOrEmptySpec(val env: KotlinCoreEnvironment) { """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) - assertThat(findings).hasSourceLocation(2, 13) + assertThat(findings).hasStartSourceLocation(2, 13) assertThat(findings[0]).hasMessage("This '?: emptyList()' can be replaced with 'orEmpty()' call") } diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseRequireSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseRequireSpec.kt index 84d11edcc85d..a726d9e96c9f 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseRequireSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseRequireSpec.kt @@ -22,7 +22,7 @@ class UseRequireSpec(val env: KotlinCoreEnvironment) { doSomething() } """ - assertThat(subject.lint(code)).hasSourceLocation(2, 16) + assertThat(subject.lint(code)).hasStartSourceLocation(2, 16) } @Test @@ -33,7 +33,7 @@ class UseRequireSpec(val env: KotlinCoreEnvironment) { doSomething() } """ - assertThat(subject.lint(code)).hasSourceLocation(2, 16) + assertThat(subject.lint(code)).hasStartSourceLocation(2, 16) } @Test @@ -44,7 +44,7 @@ class UseRequireSpec(val env: KotlinCoreEnvironment) { doSomething() } """ - assertThat(subject.lint(code)).hasSourceLocation(2, 16) + assertThat(subject.lint(code)).hasStartSourceLocation(2, 16) } @Test @@ -55,7 +55,7 @@ class UseRequireSpec(val env: KotlinCoreEnvironment) { doSomething() } """ - assertThat(subject.lint(code)).hasSourceLocation(2, 16) + assertThat(subject.lint(code)).hasStartSourceLocation(2, 16) } @Test diff --git a/detekt-test-utils/build.gradle.kts b/detekt-test-utils/build.gradle.kts index de2a88d0ff52..edbf660c62cd 100644 --- a/detekt-test-utils/build.gradle.kts +++ b/detekt-test-utils/build.gradle.kts @@ -13,3 +13,7 @@ dependencies { testImplementation(libs.assertj) runtimeOnly(libs.kotlin.scriptingCompilerEmbeddable) } + +tasks.apiDump { + notCompatibleWithConfigurationCache("https://github.com/Kotlin/binary-compatibility-validator/issues/95") +} diff --git a/detekt-test-utils/src/main/kotlin/io/github/detekt/test/utils/KtTestCompiler.kt b/detekt-test-utils/src/main/kotlin/io/github/detekt/test/utils/KtTestCompiler.kt index 7cc9c03c3480..37a379112252 100644 --- a/detekt-test-utils/src/main/kotlin/io/github/detekt/test/utils/KtTestCompiler.kt +++ b/detekt-test-utils/src/main/kotlin/io/github/detekt/test/utils/KtTestCompiler.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoots import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots +import org.jetbrains.kotlin.cli.jvm.config.configureJdkClasspathRoots import org.jetbrains.kotlin.com.intellij.openapi.project.Project import org.jetbrains.kotlin.com.intellij.openapi.util.Disposer import org.jetbrains.kotlin.com.intellij.openapi.util.text.StringUtilRt @@ -58,6 +59,7 @@ internal object KtTestCompiler : KtCompiler() { addJvmClasspathRoot(kotlinxCoroutinesCorePath()) addJvmClasspathRoots(additionalRootPaths) addJavaSourceRoots(additionalJavaSourceRootPaths) + configureJdkClasspathRoots() } val parentDisposable = Disposer.newDisposable() diff --git a/detekt-test/api/detekt-test.api b/detekt-test/api/detekt-test.api index 3eaa3c7710dc..587575cc6961 100644 --- a/detekt-test/api/detekt-test.api +++ b/detekt-test/api/detekt-test.api @@ -6,8 +6,12 @@ public final class io/gitlab/arturbosch/detekt/test/FindingAssert : org/assertj/ public final class io/gitlab/arturbosch/detekt/test/FindingsAssert : org/assertj/core/api/AbstractListAssert { public fun (Ljava/util/List;)V + public final fun hasEndSourceLocation (II)Lio/gitlab/arturbosch/detekt/test/FindingsAssert; + public final fun hasEndSourceLocations ([Lio/gitlab/arturbosch/detekt/api/SourceLocation;)Lio/gitlab/arturbosch/detekt/test/FindingsAssert; public final fun hasSourceLocation (II)Lio/gitlab/arturbosch/detekt/test/FindingsAssert; public final fun hasSourceLocations ([Lio/gitlab/arturbosch/detekt/api/SourceLocation;)Lio/gitlab/arturbosch/detekt/test/FindingsAssert; + public final fun hasStartSourceLocation (II)Lio/gitlab/arturbosch/detekt/test/FindingsAssert; + public final fun hasStartSourceLocations ([Lio/gitlab/arturbosch/detekt/api/SourceLocation;)Lio/gitlab/arturbosch/detekt/test/FindingsAssert; public final fun hasTextLocations ([Ljava/lang/String;)Lio/gitlab/arturbosch/detekt/test/FindingsAssert; public final fun hasTextLocations ([Lkotlin/Pair;)Lio/gitlab/arturbosch/detekt/test/FindingsAssert; public synthetic fun newAbstractIterableAssert (Ljava/lang/Iterable;)Lorg/assertj/core/api/AbstractIterableAssert; @@ -55,6 +59,10 @@ public final class io/gitlab/arturbosch/detekt/test/TestConfig$Companion { public final fun invoke ([Lkotlin/Pair;)Lio/gitlab/arturbosch/detekt/test/TestConfig; } +public final class io/gitlab/arturbosch/detekt/test/TestConfigKt { + public static final fun toConfig (Lio/gitlab/arturbosch/detekt/api/ValueWithReason;)Ljava/util/Map; +} + public final class io/gitlab/arturbosch/detekt/test/ThresholdedCodeSmellAssert : org/assertj/core/api/AbstractAssert { public fun (Lio/gitlab/arturbosch/detekt/api/ThresholdedCodeSmell;)V public final fun hasThreshold (I)V diff --git a/detekt-test/build.gradle.kts b/detekt-test/build.gradle.kts index 842b58e60a78..1740fae4ac07 100644 --- a/detekt-test/build.gradle.kts +++ b/detekt-test/build.gradle.kts @@ -10,3 +10,7 @@ dependencies { compileOnly(libs.assertj) implementation(projects.detektCore) } + +tasks.apiDump { + notCompatibleWithConfigurationCache("https://github.com/Kotlin/binary-compatibility-validator/issues/95") +} diff --git a/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/FindingsAssertions.kt b/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/FindingsAssertions.kt index e773244b2064..3297cf7c5d7a 100644 --- a/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/FindingsAssertions.kt +++ b/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/FindingsAssertions.kt @@ -27,7 +27,10 @@ class FindingsAssert(actual: List) : override fun toAssert(value: Finding?, description: String?): FindingAssert = FindingAssert(value).`as`(description) - fun hasSourceLocations(vararg expected: SourceLocation) = apply { + @Deprecated("Use hasStartSourceLocations instead", ReplaceWith("hasStartSourceLocations(*expected)")) + fun hasSourceLocations(vararg expected: SourceLocation) = hasStartSourceLocations(*expected) + + fun hasStartSourceLocations(vararg expected: SourceLocation) = apply { val actualSources = actual.asSequence() .map { it.location.source } .sortedWith(compareBy({ it.line }, { it.column })) @@ -37,13 +40,35 @@ class FindingsAssert(actual: List) : if (!Objects.deepEquals(actualSources.toList(), expectedSources.toList())) { failWithMessage( - "Expected source locations to be ${expectedSources.toList()} but was ${actualSources.toList()}" + "Expected start source locations to be ${expectedSources.toList()} but was ${actualSources.toList()}" + ) + } + } + + fun hasEndSourceLocations(vararg expected: SourceLocation) = apply { + val actualSources = actual.asSequence() + .map { it.location.endSource } + .sortedWith(compareBy({ it.line }, { it.column })) + + val expectedSources = expected.asSequence() + .sortedWith(compareBy({ it.line }, { it.column })) + + if (!Objects.deepEquals(actualSources.toList(), expectedSources.toList())) { + failWithMessage( + "Expected end source locations to be ${expectedSources.toList()} but was ${actualSources.toList()}" ) } } - fun hasSourceLocation(line: Int, column: Int) = apply { - hasSourceLocations(SourceLocation(line, column)) + @Deprecated("Use hasStartSourceLocation instead", ReplaceWith("hasStartSourceLocation(line, column)")) + fun hasSourceLocation(line: Int, column: Int) = hasStartSourceLocation(line, column) + + fun hasStartSourceLocation(line: Int, column: Int) = apply { + hasStartSourceLocations(SourceLocation(line, column)) + } + + fun hasEndSourceLocation(line: Int, column: Int) = apply { + hasEndSourceLocations(SourceLocation(line, column)) } fun hasTextLocations(vararg expected: Pair) = apply { diff --git a/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/TestConfig.kt b/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/TestConfig.kt index a84ad05e2c4c..7659bec664d5 100644 --- a/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/TestConfig.kt +++ b/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/TestConfig.kt @@ -1,6 +1,7 @@ package io.gitlab.arturbosch.detekt.test import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.ValueWithReason import io.gitlab.arturbosch.detekt.core.config.tryParseBasedOnDefault import io.gitlab.arturbosch.detekt.core.config.valueOrDefaultInternal @@ -53,3 +54,7 @@ open class TestConfig( operator fun invoke(vararg pairs: Pair) = TestConfig(mapOf(*pairs)) } } + +fun ValueWithReason.toConfig(): Map { + return mapOf("value" to value, "reason" to reason) +} diff --git a/detekt-tooling/api/detekt-tooling.api b/detekt-tooling/api/detekt-tooling.api index fbabfcc95325..f568333971f0 100644 --- a/detekt-tooling/api/detekt-tooling.api +++ b/detekt-tooling/api/detekt-tooling.api @@ -7,6 +7,18 @@ public final class io/github/detekt/tooling/api/AnalysisResultKt { public static final fun exitCode (Lio/github/detekt/tooling/api/AnalysisResult;)I } +public abstract interface class io/github/detekt/tooling/api/CustomConfigurationProvider { + public static final field Companion Lio/github/detekt/tooling/api/CustomConfigurationProvider$Companion; + public abstract fun copy (Ljava/nio/file/Path;)V + public abstract fun get ()Lio/gitlab/arturbosch/detekt/api/Config; + public abstract fun init (Lio/github/detekt/tooling/api/spec/ExtensionsSpec;)V +} + +public final class io/github/detekt/tooling/api/CustomConfigurationProvider$Companion { + public final fun load (Lio/github/detekt/tooling/api/spec/ExtensionsSpec;Ljava/lang/ClassLoader;)Lio/github/detekt/tooling/api/CustomConfigurationProvider; + public static synthetic fun load$default (Lio/github/detekt/tooling/api/CustomConfigurationProvider$Companion;Lio/github/detekt/tooling/api/spec/ExtensionsSpec;Ljava/lang/ClassLoader;ILjava/lang/Object;)Lio/github/detekt/tooling/api/CustomConfigurationProvider; +} + public abstract interface class io/github/detekt/tooling/api/DefaultConfigurationProvider { public static final field Companion Lio/github/detekt/tooling/api/DefaultConfigurationProvider$Companion; public abstract fun copy (Ljava/nio/file/Path;)V diff --git a/detekt-tooling/build.gradle.kts b/detekt-tooling/build.gradle.kts index f203bda4320c..36e23ca15a3b 100644 --- a/detekt-tooling/build.gradle.kts +++ b/detekt-tooling/build.gradle.kts @@ -10,6 +10,10 @@ dependencies { testImplementation(libs.assertj) } +tasks.apiDump { + notCompatibleWithConfigurationCache("https://github.com/Kotlin/binary-compatibility-validator/issues/95") +} + apiValidation { ignoredPackages.add("io.github.detekt.tooling.internal") } diff --git a/detekt-tooling/src/main/kotlin/io/github/detekt/tooling/api/CustomConfigurationProvider.kt b/detekt-tooling/src/main/kotlin/io/github/detekt/tooling/api/CustomConfigurationProvider.kt new file mode 100644 index 000000000000..8b7c4628b60c --- /dev/null +++ b/detekt-tooling/src/main/kotlin/io/github/detekt/tooling/api/CustomConfigurationProvider.kt @@ -0,0 +1,18 @@ +package io.github.detekt.tooling.api + +import java.util.ServiceLoader + +interface CustomConfigurationProvider { + + fun generate(args: Array) + + companion object { + + fun load( + classLoader: ClassLoader = CustomConfigurationProvider::class.java.classLoader, + ): CustomConfigurationProvider { + return ServiceLoader.load(CustomConfigurationProvider::class.java, classLoader) + .first() + } + } +} diff --git a/gradle.properties b/gradle.properties index 1b69faa5a08b..41510ff430d0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,8 @@ kotlin.code.style=official +kotlin.incremental.useClasspathSnapshot=true systemProp.dependency.analysis.test.analysis=false org.gradle.parallel=true org.gradle.caching=true org.gradle.jvmargs=-Xmx1g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -org.gradle.unsafe.configuration-cache=false +org.gradle.unsafe.configuration-cache=true systemProp.org.gradle.kotlin.dsl.precompiled.accessors.strict=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a850b56bf50c..2179fa740681 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,8 +1,8 @@ [versions] -dokka = "1.7.0" +dokka = "1.7.10" jacoco = "0.8.8" -kotlin = "1.6.21" -ktlint = "0.45.2" +kotlin = "1.7.10" +ktlint = "0.46.1" junit = "5.8.2" contester = "0.2.0" @@ -18,8 +18,8 @@ kotlin-scriptUtil = { module = "org.jetbrains.kotlin:kotlin-script-util", versio kotlin-scriptingCompilerEmbeddable = { module = "org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable", version.ref = "kotlin" } kotlin-stdlibJdk8 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" } -kotlinx-html = "org.jetbrains.kotlinx:kotlinx-html-jvm:0.7.5" -kotlinx-coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.3" +kotlinx-html = "org.jetbrains.kotlinx:kotlinx-html-jvm:0.8.0" +kotlinx-coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4" android-gradle = "com.android.tools.build:gradle:7.2.1" @@ -33,7 +33,7 @@ spek-dsl = { module = "org.spekframework.spek2:spek-dsl-jvm", version = "2.0.18" junit-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit" } sarif4k = "io.github.detekt.sarif4k:sarif4k:0.0.1" -assertj = "org.assertj:assertj-core:3.20.2" +assertj = "org.assertj:assertj-core:3.23.1" reflections = "org.reflections:reflections:0.10.2" mockk = "io.mockk:mockk:1.12.4" snakeyaml = "org.yaml:snakeyaml:1.30" @@ -42,7 +42,7 @@ contester-breakpoint = { module = "io.github.davidburstrom.contester:contester-b contester-driver = { module = "io.github.davidburstrom.contester:contester-driver", version.ref = "contester" } [plugins] -binaryCompatibilityValidator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version = "0.10.1" } +binaryCompatibilityValidator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version = "0.11.0" } dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } gradleVersions = { id = "com.github.ben-manes.versions", version = "0.42.0" } pluginPublishing = { id = "com.gradle.plugin-publish", version = "1.0.0" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 92f06b50fd65..2ec77e51a9c9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/scripts/github-milestone-report.main.kts b/scripts/github-milestone-report.main.kts index d0042bbe9dd0..f766a7a2811b 100755 --- a/scripts/github-milestone-report.main.kts +++ b/scripts/github-milestone-report.main.kts @@ -7,7 +7,8 @@ * You need kotlin 1.3.70+ installed on your machine */ -@file:Suppress("detekt.CommentSpacing") // for the exec line +// for the exec line +@file:Suppress("detekt.CommentSpacing") @file:DependsOn("org.kohsuke:github-api:1.135") @file:DependsOn("com.github.ajalt:clikt:2.8.0") diff --git a/settings.gradle.kts b/settings.gradle.kts index c465ee17564f..6bd4a0a283a2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -42,7 +42,7 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") // build scan plugin can only be applied in settings file plugins { // check https://gradle.com/enterprise/releases with new versions. GE plugin version should not lag behind Gradle version - id("com.gradle.enterprise") version "3.10.2" + id("com.gradle.enterprise") version "3.10.3" id("com.gradle.common-custom-user-data-gradle-plugin") version "1.7.2" } diff --git a/website/docs/gettingstarted/gradle.mdx b/website/docs/gettingstarted/gradle.mdx index 70be03c1abd2..59925c8c2300 100644 --- a/website/docs/gettingstarted/gradle.mdx +++ b/website/docs/gettingstarted/gradle.mdx @@ -8,7 +8,7 @@ summary: sidebar_position: 2 --- -Detekt requires **Gradle 6.1** or higher. We, however, recommend using the version of Gradle that is [listed in this table](/docs/introduction/compatibility). +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 diff --git a/website/docs/introduction/changelog 1.x.x.md b/website/docs/introduction/changelog 1.x.x.md index 10404e801fc8..7bfa6c93b82a 100644 --- a/website/docs/introduction/changelog 1.x.x.md +++ b/website/docs/introduction/changelog 1.x.x.md @@ -5,7 +5,17 @@ keywords: [changelog, release-notes, migration] sidebar_position: 1 --- -#### 1.21.0-RC2 - 2022-06-29 +#### 1.21.0 - 2022-07-16 + +We're delighted to announce the next upcoming stable release of Detekt: `1.21.0` 🎉 +This release is coming with 6 new rules, new API and functionalities and several stability improvements. + +We want to thank you very much [our Sponsors](https://github.com/sponsors/detekt) for the support in those last months. The work behind Detekt is all happening on a voluntary basis, and we're more than grateful for all the support we get from the Open Source Ecosystem. + +We're also excited to announce that we're now having an [Open Source Gradle Enterprise](https://ge.detekt.dev) instance. When building the Detekt projects, you'll benefit from the Gradle Remote Cache that this instance is providing! + +Finally, we want to take the opportunity to thank our contributors for testing, bug reporting and helping +us release this new version of Detekt. You're more than welcome to join our community on the [#detekt](https://kotlinlang.slack.com/archives/C88E12QH4) channel on KotlinLang's Slack (you can [get an invite here](https://surveys.jetbrains.com/s3/kotlin-slack-sign-up)). ##### Notable Changes @@ -18,13 +28,35 @@ sidebar_position: 1 - `MaxChainedCallsOnSameLine` - [#4985](https://github.com/detekt/detekt/pull/4985) - `CascadingCallWrapping` - [#4979](https://github.com/detekt/detekt/pull/4979) - We added support for Markdown reports - [#4858](https://github.com/detekt/detekt/pull/4858) -- We now allow users and rule authors to specify a **reason** for every value in the config file - [#4611](https://github.com/detekt/detekt/pull/4611) -- We now report as warnings the in the config file that can be converted to be an array - [#4793](https://github.com/detekt/detekt/pull/4793) +- We now allow users and rule authors to specify a **reason** for every value in the config file - [#4611](https://github.com/detekt/detekt/pull/4611) Please note that this feature requires a rule to be extended to support it. If you're a rule author you can start using it right away in your rule. We're looking into using this feature in some first party rule starting from Detekt `1.22.0`. +- We now report as warnings the Strings in the config file that can be converted to be an array - [#4793](https://github.com/detekt/detekt/pull/4793) - We added a dependency on **ConTester** to help us verify concurrency scenarios for Detekt - [#4672](https://github.com/detekt/detekt/pull/4672) - For contributors: we restructured our build setup to be use **Gradle composite build** - [#4751](https://github.com/detekt/detekt/pull/4751) +##### Migration + +We fixed a bug related to function with KDocs and how their location in the source code was calculated (see [#4961](https://github.com/detekt/detekt/pull/4961) and [#4887](https://github.com/detekt/detekt/issues/4887)). + +Because of this, some users might have to **recreate their baseline** as the location of such functions are not matched anymore against the baseline. You can do so by deleting your old baseline and invoking the `detektBaseline` task (or the corresponding task, based on your configuration). + ##### Changelog +- ReturnCount: Make configuration parameter more explicit - [#5062](https://github.com/detekt/detekt/pull/5062) +- Remove redundant null check - [#5061](https://github.com/detekt/detekt/pull/5061) +- Drop redundant Gradle workaround - [#5057](https://github.com/detekt/detekt/pull/5057) +- Update ktlint links from website to readme - [#5056](https://github.com/detekt/detekt/pull/5056) +- Improve extensions.doc format with admonitions - [#5055](https://github.com/detekt/detekt/pull/5055) +- Update docusaurus monorepo to v2.0.0-beta.22 - [#5050](https://github.com/detekt/detekt/pull/5050) +- Enable strict Kotlin DSL precompiled script plugins accessors generation - [#5048](https://github.com/detekt/detekt/pull/5048) +- MaxChainedCallsOnSameLine: don't count package references as chained calls - [#5036](https://github.com/detekt/detekt/pull/5036) +- Xml Report Merger now merges duplicate smells across input report files - [#5033](https://github.com/detekt/detekt/pull/5033) +- Add ending line and column to Location.kt - [#5032](https://github.com/detekt/detekt/pull/5032) +- Fix type resolution link in Contributing.md - [#5027](https://github.com/detekt/detekt/pull/5027) +- #5014 Fix MaxChainedCallsOnSameLine false positives - [#5020](https://github.com/detekt/detekt/pull/5020) +- Add endColumn/endLine to SARIF region - [#5011](https://github.com/detekt/detekt/pull/5011) +- Removed UnnecessaryAbstractClass if it inherits from a abstract class - [#5009](https://github.com/detekt/detekt/pull/5009) +- Only recommend using index accessors for Java classes that are known collections - [#4994](https://github.com/detekt/detekt/pull/4994) +- UnusedImports: fix false positive for unresolved imports - [#4882](https://github.com/detekt/detekt/pull/4882) - Fix Signatures.kt:buildFunctionSignature - [#4961](https://github.com/detekt/detekt/pull/4961) - Loading a specific resource from a module must use class from module - [#5008](https://github.com/detekt/detekt/pull/5008) - Update github/codeql-action digest to 3f62b75 - [#5007](https://github.com/detekt/detekt/pull/5007) @@ -90,6 +122,8 @@ sidebar_position: 1 ##### Dependency Updates +- 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) - Update dependency org.jetbrains.dokka to v1.7.0 - [#4974](https://github.com/detekt/detekt/pull/4974) - Update plugin binaryCompatibilityValidator to v0.10.1 - [#4954](https://github.com/detekt/detekt/pull/4954) @@ -107,6 +141,9 @@ sidebar_position: 1 ##### Housekeeping & Refactorings +- Fix `ComplexMethod` debt and refactor code - [#5029](https://github.com/detekt/detekt/pull/5029) +- Fix ReturnCount debt and refactor code - [#5026](https://github.com/detekt/detekt/pull/5026) +- Add test for ForbiddenMethodCall with getters - [#5018](https://github.com/detekt/detekt/pull/5018) - Measure flakyness on Windows CI - [#4742](https://github.com/detekt/detekt/pull/4742) - Declare nested test classes as non-static - [#4894](https://github.com/detekt/detekt/pull/4894) - Remove deprecated usages in gradle-plugin test - [#4889](https://github.com/detekt/detekt/pull/4889) diff --git a/website/docs/introduction/compatibility.md b/website/docs/introduction/compatibility.md index 41e868bc318d..84203eb01c74 100644 --- a/website/docs/introduction/compatibility.md +++ b/website/docs/introduction/compatibility.md @@ -28,6 +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.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` | diff --git a/website/package.json b/website/package.json index 1c7899d6e846..77e03a33280c 100644 --- a/website/package.json +++ b/website/package.json @@ -16,9 +16,9 @@ "generate-and-build": "../gradlew -p .. :detekt-generator:generateDocumentation && docusaurus build" }, "dependencies": { - "@docusaurus/core": "2.0.0-beta.22", - "@docusaurus/plugin-client-redirects": "2.0.0-beta.22", - "@docusaurus/preset-classic": "2.0.0-beta.22", + "@docusaurus/core": "2.0.0-rc.1", + "@docusaurus/plugin-client-redirects": "2.0.0-rc.1", + "@docusaurus/preset-classic": "2.0.0-rc.1", "@mdx-js/react": "1.6.22", "clsx": "1.2.1", "prism-react-renderer": "1.3.5", diff --git a/website/src/remark/detektVersionReplace.js b/website/src/remark/detektVersionReplace.js index 5c6e20984ee1..8c4920e30888 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 `:detekt-generator:generateDocumentation` task. -const detektVersion = "1.21.0-RC2"; +const detektVersion = "1.21.0"; const plugin = (options) => { const transformer = async (ast) => { diff --git a/website/yarn.lock b/website/yarn.lock index ebbf20c0163c..4eeaf10b4f66 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -2128,10 +2128,10 @@ "@docsearch/css" "3.1.1" algoliasearch "^4.0.0" -"@docusaurus/core@2.0.0-beta.22": - version "2.0.0-beta.22" - resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-2.0.0-beta.22.tgz#72430b817571e2d7354012523bcaefc99fe4e004" - integrity sha512-8KU56anfNo6V6crZG+K/zPKvyAAosZcWfkeNYWu14BzigRbBirJf7ZLRkkLa1NgDdJt3EEBgg+Iv8olPMC1uog== +"@docusaurus/core@2.0.0-rc.1": + version "2.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-2.0.0-rc.1.tgz#828d93d241171565d8947a9ab404091e04759141" + integrity sha512-b9FX0Z+EddfQ6wAiNh+Wx4fysKfcvEcWJrZ5USROn3C+EVU5P4luaa8mwWK//O+hTwD9ur7/A44IZ/tWCTAoLQ== dependencies: "@babel/core" "^7.18.6" "@babel/generator" "^7.18.7" @@ -2142,14 +2142,14 @@ "@babel/preset-typescript" "^7.18.6" "@babel/runtime" "^7.18.6" "@babel/runtime-corejs3" "^7.18.6" - "@babel/traverse" "^7.18.6" - "@docusaurus/cssnano-preset" "2.0.0-beta.22" - "@docusaurus/logger" "2.0.0-beta.22" - "@docusaurus/mdx-loader" "2.0.0-beta.22" + "@babel/traverse" "^7.18.8" + "@docusaurus/cssnano-preset" "2.0.0-rc.1" + "@docusaurus/logger" "2.0.0-rc.1" + "@docusaurus/mdx-loader" "2.0.0-rc.1" "@docusaurus/react-loadable" "5.5.2" - "@docusaurus/utils" "2.0.0-beta.22" - "@docusaurus/utils-common" "2.0.0-beta.22" - "@docusaurus/utils-validation" "2.0.0-beta.22" + "@docusaurus/utils" "2.0.0-rc.1" + "@docusaurus/utils-common" "2.0.0-rc.1" + "@docusaurus/utils-validation" "2.0.0-rc.1" "@slorber/static-site-generator-webpack-plugin" "^4.0.7" "@svgr/webpack" "^6.2.1" autoprefixer "^10.4.7" @@ -2205,33 +2205,33 @@ webpack-merge "^5.8.0" webpackbar "^5.0.2" -"@docusaurus/cssnano-preset@2.0.0-beta.22": - version "2.0.0-beta.22" - resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-2.0.0-beta.22.tgz#dc3491a452fb66569261801c67e00a1bb8b7dce1" - integrity sha512-ewImLASzPD2dRQLhNdBA5AyckkPDqZPMMrQiuDpe4BgfbjROJWLjVzjMbQRdrB2UQPwm9HyE6/+gP55KNISKvQ== +"@docusaurus/cssnano-preset@2.0.0-rc.1": + version "2.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-2.0.0-rc.1.tgz#76bbd7f6912779a0667f8f2fd8fc1a05618a6148" + integrity sha512-9/KmQvF+eTlMqUqG6UcXbRgxbGed/8bQInXuKEs+95/jI6jO/3xSzuRwuHHHP0naUvSVWjnNI9jngPrQerXE5w== dependencies: cssnano-preset-advanced "^5.3.8" postcss "^8.4.14" postcss-sort-media-queries "^4.2.1" tslib "^2.4.0" -"@docusaurus/logger@2.0.0-beta.22": - version "2.0.0-beta.22" - resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-2.0.0-beta.22.tgz#dd328d8cddd947c2211fd20ae91f5921b6c70f82" - integrity sha512-Gg6So3GYbFi6pyn5YrFS8lNST90f2sNrBTu/mAo2nDU391vIJ3bDkNfHNi4plz9TyCGxxx8BgOExh6x3xGJhMg== +"@docusaurus/logger@2.0.0-rc.1": + version "2.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-2.0.0-rc.1.tgz#db95e9b15bc243695830a5b791c0eff705ef1b54" + integrity sha512-daa3g+SXuO9K60PVMiSUmDEK9Vro+Ed7i7uF8CH6QQJLcNZy/zJc0Xz62eH7ip1x77fmeb6Rg4Us1TqTFc9AbQ== dependencies: chalk "^4.1.2" tslib "^2.4.0" -"@docusaurus/mdx-loader@2.0.0-beta.22": - version "2.0.0-beta.22" - resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-2.0.0-beta.22.tgz#7a015c161a4c40300fe99bde7dc0a6ada094d45f" - integrity sha512-kJT3zsHQTfMFSHlNohw0C4VJjKC2cox6navbMRJM4mZUm+wj0YDE2/WAcwYB8abM1AZkgJvAMZnxynq6vUZxhw== +"@docusaurus/mdx-loader@2.0.0-rc.1": + version "2.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-2.0.0-rc.1.tgz#e78d7d416aacc289f2427c5ccdb9145820acb0cb" + integrity sha512-8Fg0c/ceu39knmr7w0dutm7gq3YxKYCqWVS2cB/cPATzChCCNH/AGLfBT6sz/Z4tjVXE+NyREq2pfOFvkhjVXg== dependencies: - "@babel/parser" "^7.18.6" - "@babel/traverse" "^7.18.6" - "@docusaurus/logger" "2.0.0-beta.22" - "@docusaurus/utils" "2.0.0-beta.22" + "@babel/parser" "^7.18.8" + "@babel/traverse" "^7.18.8" + "@docusaurus/logger" "2.0.0-rc.1" + "@docusaurus/utils" "2.0.0-rc.1" "@mdx-js/mdx" "^1.6.22" escape-html "^1.0.3" file-loader "^6.2.0" @@ -2246,13 +2246,13 @@ url-loader "^4.1.1" webpack "^5.73.0" -"@docusaurus/module-type-aliases@2.0.0-beta.22": - version "2.0.0-beta.22" - resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-2.0.0-beta.22.tgz#bae805e339364eb3d801e010b3098ceb04dd5ce4" - integrity sha512-h0IOYfFgZgV3MjLHefbS1Zf0zmiNOBCtvu9vXwoxbws7fzjqUl1HALS0HQ2SaHsVsQ4AeepYidHtkS2upw8+JQ== +"@docusaurus/module-type-aliases@2.0.0-rc.1": + version "2.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-2.0.0-rc.1.tgz#c7839ac15b7712a8d86353a3253918f63ffbea09" + integrity sha512-la7D8ggFP8I5nOp/Epl6NqTeDWcbofPVMOaVisRxQbx5iuF9Al+AITbaDgm4CXpFLJACsqhsXD5W4BnKX8ZxfA== dependencies: "@docusaurus/react-loadable" "5.5.2" - "@docusaurus/types" "2.0.0-beta.22" + "@docusaurus/types" "2.0.0-rc.1" "@types/history" "^4.7.11" "@types/react" "*" "@types/react-router-config" "*" @@ -2260,33 +2260,33 @@ react-helmet-async "*" react-loadable "npm:@docusaurus/react-loadable@5.5.2" -"@docusaurus/plugin-client-redirects@2.0.0-beta.22": - version "2.0.0-beta.22" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-2.0.0-beta.22.tgz#12875d42a1640bf03b2e046d340f1de6608ec25d" - integrity sha512-v+ZpmJv40KWvtUKXk2y3YfFyMWW2xzIVgkf8jJFyt92MmR/oe+Rve8Xe8eF2Q3MmjrV5yqiInTlYKIT0mteBSg== +"@docusaurus/plugin-client-redirects@2.0.0-rc.1": + version "2.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-2.0.0-rc.1.tgz#ce71d9f73bfbb4b2e159ee3a22f5517b3480c83c" + integrity sha512-uGiK7kzQeJ+gChzIgazKMlHEonOwlmK6NEJvr44aWS6DbauVXOs/aolXZCHx8ZdKPETOpZEzSRYjU5e+QIN+HA== dependencies: - "@docusaurus/core" "2.0.0-beta.22" - "@docusaurus/logger" "2.0.0-beta.22" - "@docusaurus/utils" "2.0.0-beta.22" - "@docusaurus/utils-common" "2.0.0-beta.22" - "@docusaurus/utils-validation" "2.0.0-beta.22" + "@docusaurus/core" "2.0.0-rc.1" + "@docusaurus/logger" "2.0.0-rc.1" + "@docusaurus/utils" "2.0.0-rc.1" + "@docusaurus/utils-common" "2.0.0-rc.1" + "@docusaurus/utils-validation" "2.0.0-rc.1" eta "^1.12.3" fs-extra "^10.1.0" lodash "^4.17.21" tslib "^2.4.0" -"@docusaurus/plugin-content-blog@2.0.0-beta.22": - version "2.0.0-beta.22" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.0.0-beta.22.tgz#c08a685fe19db417318b1e18414e9fefa520a51d" - integrity sha512-igXqg3O7KKwYq+RleeK73dxVOM2ONnerykmy5Uaasfzxzi2z5erAzTTUSINa86Czo4CfwaSDwVAkc43z4Z8Hiw== - dependencies: - "@docusaurus/core" "2.0.0-beta.22" - "@docusaurus/logger" "2.0.0-beta.22" - "@docusaurus/mdx-loader" "2.0.0-beta.22" - "@docusaurus/types" "2.0.0-beta.22" - "@docusaurus/utils" "2.0.0-beta.22" - "@docusaurus/utils-common" "2.0.0-beta.22" - "@docusaurus/utils-validation" "2.0.0-beta.22" +"@docusaurus/plugin-content-blog@2.0.0-rc.1": + version "2.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.0.0-rc.1.tgz#8ae5d5ec2da08c583a057bf2754a5b9278b3eb08" + integrity sha512-BVVrAGZujpjS/0rarY2o24rlylRRh2NZuM65kg0JNkkViF79SeEHsepog7IuHyoqGWPm1N/I7LpEp7k+gowZzQ== + dependencies: + "@docusaurus/core" "2.0.0-rc.1" + "@docusaurus/logger" "2.0.0-rc.1" + "@docusaurus/mdx-loader" "2.0.0-rc.1" + "@docusaurus/types" "2.0.0-rc.1" + "@docusaurus/utils" "2.0.0-rc.1" + "@docusaurus/utils-common" "2.0.0-rc.1" + "@docusaurus/utils-validation" "2.0.0-rc.1" cheerio "^1.0.0-rc.12" feed "^4.2.2" fs-extra "^10.1.0" @@ -2297,18 +2297,18 @@ utility-types "^3.10.0" webpack "^5.73.0" -"@docusaurus/plugin-content-docs@2.0.0-beta.22": - version "2.0.0-beta.22" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.0.0-beta.22.tgz#ef8c60729f74c1604e178863a798890376a0dd63" - integrity sha512-Hfb0+coxJshheAQISamfGrU2T1CLhV6EAPcYx3ejCXsMTjAAtyFsK17t6qGOCGFg3J36gPrzBstBWwEvaVHCqw== - dependencies: - "@docusaurus/core" "2.0.0-beta.22" - "@docusaurus/logger" "2.0.0-beta.22" - "@docusaurus/mdx-loader" "2.0.0-beta.22" - "@docusaurus/module-type-aliases" "2.0.0-beta.22" - "@docusaurus/types" "2.0.0-beta.22" - "@docusaurus/utils" "2.0.0-beta.22" - "@docusaurus/utils-validation" "2.0.0-beta.22" +"@docusaurus/plugin-content-docs@2.0.0-rc.1": + version "2.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.0.0-rc.1.tgz#2dda88166bf21b0eeb3821ef748059b20c8c49f7" + integrity sha512-Yk5Hu6uaw3tRplzJnbDygwRhmZ3PCzEXD4SJpBA6cPC73ylfqOEh6qhiU+BWhMTtDXNhY+athk5Kycfk3DW1aQ== + dependencies: + "@docusaurus/core" "2.0.0-rc.1" + "@docusaurus/logger" "2.0.0-rc.1" + "@docusaurus/mdx-loader" "2.0.0-rc.1" + "@docusaurus/module-type-aliases" "2.0.0-rc.1" + "@docusaurus/types" "2.0.0-rc.1" + "@docusaurus/utils" "2.0.0-rc.1" + "@docusaurus/utils-validation" "2.0.0-rc.1" "@types/react-router-config" "^5.0.6" combine-promises "^1.1.0" fs-extra "^10.1.0" @@ -2319,84 +2319,84 @@ utility-types "^3.10.0" webpack "^5.73.0" -"@docusaurus/plugin-content-pages@2.0.0-beta.22": - version "2.0.0-beta.22" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.0.0-beta.22.tgz#a9ed66a21f66019ada8d7cd5bb0bca06c2fe313c" - integrity sha512-v+oBM0IvRuU2D5HACaaHdxgW+XajFYgimRwV8jp1z6trjRInCO//VjYl+VEaqRHFZ1y7gwbInJxn4as1uGHcjw== +"@docusaurus/plugin-content-pages@2.0.0-rc.1": + version "2.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.0.0-rc.1.tgz#2be82f53d6c77e6aa66787726c30dc60b210e6f8" + integrity sha512-FdO79WC5hfWDQu3/CTFLRQzTNc0e5n+HNzavm2MNkSzGV08BFJ6RAkbPbtra5CWef+6iXZav6D/tzv2jDPvLzA== dependencies: - "@docusaurus/core" "2.0.0-beta.22" - "@docusaurus/mdx-loader" "2.0.0-beta.22" - "@docusaurus/types" "2.0.0-beta.22" - "@docusaurus/utils" "2.0.0-beta.22" - "@docusaurus/utils-validation" "2.0.0-beta.22" + "@docusaurus/core" "2.0.0-rc.1" + "@docusaurus/mdx-loader" "2.0.0-rc.1" + "@docusaurus/types" "2.0.0-rc.1" + "@docusaurus/utils" "2.0.0-rc.1" + "@docusaurus/utils-validation" "2.0.0-rc.1" fs-extra "^10.1.0" tslib "^2.4.0" webpack "^5.73.0" -"@docusaurus/plugin-debug@2.0.0-beta.22": - version "2.0.0-beta.22" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-2.0.0-beta.22.tgz#a0ca40d5e50c216d57a774c6f7831c18ccb92423" - integrity sha512-uB7+eHGpJugDy/Rzxbs293FuOf66ck5Wx/Q1wcRA1AQQVSiqDfvj2ZBTHBNr+onympYdL7IPWqTnjf1tt40nBQ== +"@docusaurus/plugin-debug@2.0.0-rc.1": + version "2.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-2.0.0-rc.1.tgz#73c06ad08d66810941e456d50b07be008f5235cb" + integrity sha512-aOsyYrPMbnsyqHwsVZ+0frrMRtnYqm4eaJpG4sC/6LYAJ07IDRQ9j3GOku2dKr5GsFK1Vx7VlE6ZLwe0MaGstg== dependencies: - "@docusaurus/core" "2.0.0-beta.22" - "@docusaurus/types" "2.0.0-beta.22" - "@docusaurus/utils" "2.0.0-beta.22" + "@docusaurus/core" "2.0.0-rc.1" + "@docusaurus/types" "2.0.0-rc.1" + "@docusaurus/utils" "2.0.0-rc.1" fs-extra "^10.1.0" react-json-view "^1.21.3" tslib "^2.4.0" -"@docusaurus/plugin-google-analytics@2.0.0-beta.22": - version "2.0.0-beta.22" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.0.0-beta.22.tgz#f6bdf168e5cd0ef8b8e48568e604ec554e3e08fe" - integrity sha512-5rT1b3QTcelOzx7ZeyL0mKiYvUR2c78gLmh4wHpqRJXSgZAr7Fz8VSgDzu4xfvp8+MSWWeGhCTHXQok256U4Vg== +"@docusaurus/plugin-google-analytics@2.0.0-rc.1": + version "2.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.0.0-rc.1.tgz#0136cc7534573ca56e023178ec2bda5c1e89ce71" + integrity sha512-f+G8z5OJWfg5QqWDLIdcN2SDoK5J5Gg8HMrqCI6Pfl+rxPb5I1niA+/UkAM+kMCpnekvhSt5AWz2fgkRenkPLA== dependencies: - "@docusaurus/core" "2.0.0-beta.22" - "@docusaurus/types" "2.0.0-beta.22" - "@docusaurus/utils-validation" "2.0.0-beta.22" + "@docusaurus/core" "2.0.0-rc.1" + "@docusaurus/types" "2.0.0-rc.1" + "@docusaurus/utils-validation" "2.0.0-rc.1" tslib "^2.4.0" -"@docusaurus/plugin-google-gtag@2.0.0-beta.22": - version "2.0.0-beta.22" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.0.0-beta.22.tgz#4e3aac750f67c92275832c0d83281a5293184989" - integrity sha512-DkoFfHErs10YMQoXPmFn5MC9fj9URH9LbryjTPqDoIerAZjR7MZA5g/+OueYBcachpygPlWBu6Q3mhNX19VObA== +"@docusaurus/plugin-google-gtag@2.0.0-rc.1": + version "2.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.0.0-rc.1.tgz#61698fdc41a4ace912fb8f6c834efd288edad3c0" + integrity sha512-yE1Et9hhhX9qMRnMJzpNq0854qIYiSEc2dZaXNk537HN7Q0rKkr/YONUHz2iqNYwPX2hGOY4LdpTxlMP88uVhA== dependencies: - "@docusaurus/core" "2.0.0-beta.22" - "@docusaurus/types" "2.0.0-beta.22" - "@docusaurus/utils-validation" "2.0.0-beta.22" + "@docusaurus/core" "2.0.0-rc.1" + "@docusaurus/types" "2.0.0-rc.1" + "@docusaurus/utils-validation" "2.0.0-rc.1" tslib "^2.4.0" -"@docusaurus/plugin-sitemap@2.0.0-beta.22": - version "2.0.0-beta.22" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.0.0-beta.22.tgz#2066a94cf3a7518517f940001cedcc96319ea793" - integrity sha512-XGziHGR5ZeuNxBI3D3obRS2ufZvuWKrlFQpDCq1gWvZb5EgMePGNs1ZiXUIVNyW3jOSILbemvH6DAXuXSo1DlA== - dependencies: - "@docusaurus/core" "2.0.0-beta.22" - "@docusaurus/logger" "2.0.0-beta.22" - "@docusaurus/types" "2.0.0-beta.22" - "@docusaurus/utils" "2.0.0-beta.22" - "@docusaurus/utils-common" "2.0.0-beta.22" - "@docusaurus/utils-validation" "2.0.0-beta.22" +"@docusaurus/plugin-sitemap@2.0.0-rc.1": + version "2.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.0.0-rc.1.tgz#0b638e774b253d90e9f2d11663e961250f557bc4" + integrity sha512-5JmbNpssUF03odFM4ArvIsrO9bv7HnAJ0VtefXhh0WBpaFs8NgI3rTkCTFimvtRQjDR9U2bh23fXz2vjQQz6oA== + dependencies: + "@docusaurus/core" "2.0.0-rc.1" + "@docusaurus/logger" "2.0.0-rc.1" + "@docusaurus/types" "2.0.0-rc.1" + "@docusaurus/utils" "2.0.0-rc.1" + "@docusaurus/utils-common" "2.0.0-rc.1" + "@docusaurus/utils-validation" "2.0.0-rc.1" fs-extra "^10.1.0" sitemap "^7.1.1" tslib "^2.4.0" -"@docusaurus/preset-classic@2.0.0-beta.22": - version "2.0.0-beta.22" - resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-2.0.0-beta.22.tgz#f7c798398d805e8b06e8babb2d10e4b9206802c9" - integrity sha512-an4u7KnFLR6vyBQ7l3HCNL4mXdV5QNRleZv9G+kvVeUejxs0GMF1W2pRLyfU6bEnAD0W6bDH4bYdYgIAX4kGaw== - dependencies: - "@docusaurus/core" "2.0.0-beta.22" - "@docusaurus/plugin-content-blog" "2.0.0-beta.22" - "@docusaurus/plugin-content-docs" "2.0.0-beta.22" - "@docusaurus/plugin-content-pages" "2.0.0-beta.22" - "@docusaurus/plugin-debug" "2.0.0-beta.22" - "@docusaurus/plugin-google-analytics" "2.0.0-beta.22" - "@docusaurus/plugin-google-gtag" "2.0.0-beta.22" - "@docusaurus/plugin-sitemap" "2.0.0-beta.22" - "@docusaurus/theme-classic" "2.0.0-beta.22" - "@docusaurus/theme-common" "2.0.0-beta.22" - "@docusaurus/theme-search-algolia" "2.0.0-beta.22" - "@docusaurus/types" "2.0.0-beta.22" +"@docusaurus/preset-classic@2.0.0-rc.1": + version "2.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-2.0.0-rc.1.tgz#5e5b1cf80b3dd4e2c3f824c78a111f105858d853" + integrity sha512-5jjTVZkhArjyoNHwCI9x4PSG0zPmBJILjZLVrxPcHpm/K0ltkYcp6J3GxYpf5EbMuOh5+yCWM63cSshGcNOo3Q== + dependencies: + "@docusaurus/core" "2.0.0-rc.1" + "@docusaurus/plugin-content-blog" "2.0.0-rc.1" + "@docusaurus/plugin-content-docs" "2.0.0-rc.1" + "@docusaurus/plugin-content-pages" "2.0.0-rc.1" + "@docusaurus/plugin-debug" "2.0.0-rc.1" + "@docusaurus/plugin-google-analytics" "2.0.0-rc.1" + "@docusaurus/plugin-google-gtag" "2.0.0-rc.1" + "@docusaurus/plugin-sitemap" "2.0.0-rc.1" + "@docusaurus/theme-classic" "2.0.0-rc.1" + "@docusaurus/theme-common" "2.0.0-rc.1" + "@docusaurus/theme-search-algolia" "2.0.0-rc.1" + "@docusaurus/types" "2.0.0-rc.1" "@docusaurus/react-loadable@5.5.2", "react-loadable@npm:@docusaurus/react-loadable@5.5.2": version "5.5.2" @@ -2406,27 +2406,27 @@ "@types/react" "*" prop-types "^15.6.2" -"@docusaurus/theme-classic@2.0.0-beta.22": - version "2.0.0-beta.22" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-2.0.0-beta.22.tgz#20e0521b7eedbc0160829f2be08f812832ac766b" - integrity sha512-WkoN1XC4F3v1vCWnyAdIuNF27dMccehnglloCNj0dF6mop6PHMXREQ2f6wKhp5ZjMZ/LKTAKyGjBotxPsOElvA== - dependencies: - "@docusaurus/core" "2.0.0-beta.22" - "@docusaurus/mdx-loader" "2.0.0-beta.22" - "@docusaurus/module-type-aliases" "2.0.0-beta.22" - "@docusaurus/plugin-content-blog" "2.0.0-beta.22" - "@docusaurus/plugin-content-docs" "2.0.0-beta.22" - "@docusaurus/plugin-content-pages" "2.0.0-beta.22" - "@docusaurus/theme-common" "2.0.0-beta.22" - "@docusaurus/theme-translations" "2.0.0-beta.22" - "@docusaurus/types" "2.0.0-beta.22" - "@docusaurus/utils" "2.0.0-beta.22" - "@docusaurus/utils-common" "2.0.0-beta.22" - "@docusaurus/utils-validation" "2.0.0-beta.22" +"@docusaurus/theme-classic@2.0.0-rc.1": + version "2.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-2.0.0-rc.1.tgz#4ab30745e6b03b0f277837debae786a0a83aee6a" + integrity sha512-qNiz7ieeq3AC+V8TbW6S63pWLJph1CbzWDDPTqxDLHgA8VQaNaSmJM8S92pH+yKALRb9u14ogjjYYc75Nj2JmQ== + dependencies: + "@docusaurus/core" "2.0.0-rc.1" + "@docusaurus/mdx-loader" "2.0.0-rc.1" + "@docusaurus/module-type-aliases" "2.0.0-rc.1" + "@docusaurus/plugin-content-blog" "2.0.0-rc.1" + "@docusaurus/plugin-content-docs" "2.0.0-rc.1" + "@docusaurus/plugin-content-pages" "2.0.0-rc.1" + "@docusaurus/theme-common" "2.0.0-rc.1" + "@docusaurus/theme-translations" "2.0.0-rc.1" + "@docusaurus/types" "2.0.0-rc.1" + "@docusaurus/utils" "2.0.0-rc.1" + "@docusaurus/utils-common" "2.0.0-rc.1" + "@docusaurus/utils-validation" "2.0.0-rc.1" "@mdx-js/react" "^1.6.22" - clsx "^1.2.0" + clsx "^1.2.1" copy-text-to-clipboard "^3.0.1" - infima "0.2.0-alpha.41" + infima "0.2.0-alpha.42" lodash "^4.17.21" nprogress "^0.2.0" postcss "^8.4.14" @@ -2437,60 +2437,60 @@ tslib "^2.4.0" utility-types "^3.10.0" -"@docusaurus/theme-common@2.0.0-beta.22": - version "2.0.0-beta.22" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-2.0.0-beta.22.tgz#696ae7fb963bf40ad7ee017ca77f4a31719cfa02" - integrity sha512-BTH23SryhomEetWiJKdl5C9JgnglO17IbbabhZ6wbm0bLNYXmRxV1Bh7LhVmoJECdc1LeQHDOY45mCjVxI5LAg== - dependencies: - "@docusaurus/mdx-loader" "2.0.0-beta.22" - "@docusaurus/module-type-aliases" "2.0.0-beta.22" - "@docusaurus/plugin-content-blog" "2.0.0-beta.22" - "@docusaurus/plugin-content-docs" "2.0.0-beta.22" - "@docusaurus/plugin-content-pages" "2.0.0-beta.22" - "@docusaurus/utils" "2.0.0-beta.22" +"@docusaurus/theme-common@2.0.0-rc.1": + version "2.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-2.0.0-rc.1.tgz#ea5d9732a16b03b488555e50107161bfa2abad98" + integrity sha512-1r9ZLKD9SeoCYVzWzcdR79Dia4ANlrlRjNl6uzETOEybjK6FF7yEa9Yra8EJcOCbi3coyYz5xFh/r1YHFTFHug== + dependencies: + "@docusaurus/mdx-loader" "2.0.0-rc.1" + "@docusaurus/module-type-aliases" "2.0.0-rc.1" + "@docusaurus/plugin-content-blog" "2.0.0-rc.1" + "@docusaurus/plugin-content-docs" "2.0.0-rc.1" + "@docusaurus/plugin-content-pages" "2.0.0-rc.1" + "@docusaurus/utils" "2.0.0-rc.1" "@types/history" "^4.7.11" "@types/react" "*" "@types/react-router-config" "*" - clsx "^1.2.0" + clsx "^1.2.1" parse-numeric-range "^1.3.0" prism-react-renderer "^1.3.5" tslib "^2.4.0" utility-types "^3.10.0" -"@docusaurus/theme-search-algolia@2.0.0-beta.22": - version "2.0.0-beta.22" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.0.0-beta.22.tgz#193641f11df1af2157e652b530897064f707838e" - integrity sha512-z9edT4jQxfZsBOVxDhPpxHR5N/tlgkpogds3/XBapU8b7Qp7mgp5qU3Ndz3BX3CIICDDaI2ayGn8xLL65XFGFw== +"@docusaurus/theme-search-algolia@2.0.0-rc.1": + version "2.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.0.0-rc.1.tgz#e78c0aeaea6a3717ae3a6ecd75a8652bd7c8e974" + integrity sha512-H5yq6V/B4qo6GZrDKMbeSpk3T9e9K2MliDzLonRu0w3QHW9orVGe0c/lZvRbGlDZjnsOo7XGddhXXIDWGwnpaA== dependencies: "@docsearch/react" "^3.1.1" - "@docusaurus/core" "2.0.0-beta.22" - "@docusaurus/logger" "2.0.0-beta.22" - "@docusaurus/plugin-content-docs" "2.0.0-beta.22" - "@docusaurus/theme-common" "2.0.0-beta.22" - "@docusaurus/theme-translations" "2.0.0-beta.22" - "@docusaurus/utils" "2.0.0-beta.22" - "@docusaurus/utils-validation" "2.0.0-beta.22" + "@docusaurus/core" "2.0.0-rc.1" + "@docusaurus/logger" "2.0.0-rc.1" + "@docusaurus/plugin-content-docs" "2.0.0-rc.1" + "@docusaurus/theme-common" "2.0.0-rc.1" + "@docusaurus/theme-translations" "2.0.0-rc.1" + "@docusaurus/utils" "2.0.0-rc.1" + "@docusaurus/utils-validation" "2.0.0-rc.1" algoliasearch "^4.13.1" algoliasearch-helper "^3.10.0" - clsx "^1.2.0" + clsx "^1.2.1" eta "^1.12.3" fs-extra "^10.1.0" lodash "^4.17.21" tslib "^2.4.0" utility-types "^3.10.0" -"@docusaurus/theme-translations@2.0.0-beta.22": - version "2.0.0-beta.22" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-2.0.0-beta.22.tgz#89fa0d0bd8e59bf4a0a5749182153483410946e1" - integrity sha512-duMoS+BEDWk+qCFZay6+L0C2ZYJvUdny9NdH2JLjNfC1ifl4+pM3HHciJgldos7hH/JGfohDY57fl6NKf5pQLQ== +"@docusaurus/theme-translations@2.0.0-rc.1": + version "2.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-2.0.0-rc.1.tgz#bd647f78c741ee7f6c6d2cbbd3e3f282ef2f89ad" + integrity sha512-JLhNdlnbQhxVQzOnLyiCaTzKFa1lpVrM3nCrkGQKscoG2rY6ARGYMgMN2DkoH6hm7TflQ8+PE1S5MzzASeLs4Q== dependencies: fs-extra "^10.1.0" tslib "^2.4.0" -"@docusaurus/types@2.0.0-beta.22": - version "2.0.0-beta.22" - resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-2.0.0-beta.22.tgz#8042ccdf32a915db0e4a2c0e6f98f6d43c6f7fa5" - integrity sha512-F5NQyPKIBXcX+bOK+RMce9K8NTs9Vx6v5pZ4+byLylnNvC4I52USRm+s1l6jMpvlsP4XHz1h2Tm1L3RBCBOwpg== +"@docusaurus/types@2.0.0-rc.1": + version "2.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-2.0.0-rc.1.tgz#032f8afde6b4878e37f984b9949a96b150103c21" + integrity sha512-wX25FOZa/aKnCGA5ljWPaDpMW3TuTbs0BtjQ8WTC557p8zDvuz4r+g2/FPHsgWE0TKwUMf4usQU1m3XpJLPN+g== dependencies: "@types/history" "^4.7.11" "@types/react" "*" @@ -2501,30 +2501,30 @@ webpack "^5.73.0" webpack-merge "^5.8.0" -"@docusaurus/utils-common@2.0.0-beta.22": - version "2.0.0-beta.22" - resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-2.0.0-beta.22.tgz#c3c4365be9d2003917d24393f9ad9678fb6388a1" - integrity sha512-yQM1wPUUqoDCJy0cOFWtUsqxY3utL0E14T4NDtCcdc2Einsl1mamKIaBVpt9SMZugMVXbc/z4IQK8YC81CuXEw== +"@docusaurus/utils-common@2.0.0-rc.1": + version "2.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-2.0.0-rc.1.tgz#3e233a28794325d5d9d3af3f7b1c22b59aa8b847" + integrity sha512-+iZICpeFPZJ9oGJXuG92WTWee6WRnVx5BdzlcfuKf/f5KQX8PvwXR2tDME78FGGhShB8zr+vjuNEXuLvXT7j2A== dependencies: tslib "^2.4.0" -"@docusaurus/utils-validation@2.0.0-beta.22": - version "2.0.0-beta.22" - resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-2.0.0-beta.22.tgz#517320e317dbc35ffe538eb586817929ebdb588c" - integrity sha512-sW2jrYvhvkh8PjjZzWFyqGs7tlls3F2FgOOj79T9rGj8y+b4a6sRjl8+QgXITjypcQWssCg0wqf6xSXD+LSD/Q== +"@docusaurus/utils-validation@2.0.0-rc.1": + version "2.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-2.0.0-rc.1.tgz#dded12f036cda8a54a19e01694b35859fe0cf1d5" + integrity sha512-lj36gm9Ksu4tt/EUeLDWoMbXe3sfBxeIPIUUdqYcBYkF/rpQkh+uL/dncjNGiw6uvBOqXhOfsFVP045HtgShVw== dependencies: - "@docusaurus/logger" "2.0.0-beta.22" - "@docusaurus/utils" "2.0.0-beta.22" + "@docusaurus/logger" "2.0.0-rc.1" + "@docusaurus/utils" "2.0.0-rc.1" joi "^17.6.0" js-yaml "^4.1.0" tslib "^2.4.0" -"@docusaurus/utils@2.0.0-beta.22": - version "2.0.0-beta.22" - resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-2.0.0-beta.22.tgz#4d66df9534419cc5cf2307581bab998541b8b7f2" - integrity sha512-ZwtfJl9n+dMBrdIl1DX9DyO9odMV6+1yqbJkdPrfNSLd17fYZK7HGcwQOem7QIEcJjnroUGrsQoKW8Svg3dQJg== +"@docusaurus/utils@2.0.0-rc.1": + version "2.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-2.0.0-rc.1.tgz#53584b800df9e13864d5ef1a76aa7655a90ec86e" + integrity sha512-ym9I1OwIYbKs1LGaUajaA/vDG8VweJj/6YoZjHp+eDQHhTRIrHXiYoGDqorafRhftKwnA1EnyomuXpNd9bq8Gg== dependencies: - "@docusaurus/logger" "2.0.0-beta.22" + "@docusaurus/logger" "2.0.0-rc.1" "@svgr/webpack" "^6.2.1" file-loader "^6.2.0" fs-extra "^10.1.0" @@ -3810,7 +3810,7 @@ clone-response@^1.0.2: dependencies: mimic-response "^1.0.0" -clsx@1.2.1, clsx@^1.2.0: +clsx@1.2.1, clsx@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== @@ -5408,10 +5408,10 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -infima@0.2.0-alpha.41: - version "0.2.0-alpha.41" - resolved "https://registry.yarnpkg.com/infima/-/infima-0.2.0-alpha.41.tgz#a9b5c7dd2119a151c542e8950a2f9333b204dae4" - integrity sha512-i2RzEkNhaVXMIp54PS3coINbMGzAAbdumBcA0GQGFYAu2p1Y44EKOrI2kYoHt9iac736swdB7z3muU46+DL8AA== +infima@0.2.0-alpha.42: + version "0.2.0-alpha.42" + resolved "https://registry.yarnpkg.com/infima/-/infima-0.2.0-alpha.42.tgz#f6e86a655ad40877c6b4d11b2ede681eb5470aa5" + integrity sha512-ift8OXNbQQwtbIt6z16KnSWP7uJ/SysSMFI4F87MNRTicypfl4Pv3E2OGVv6N3nSZFJvA8imYulCBS64iyHYww== inflight@^1.0.4: version "1.0.6"