From bbb494b8a5907bdb210b8839adf007272afdc349 Mon Sep 17 00:00:00 2001 From: Peter Trifanov Date: Fri, 20 Nov 2020 12:47:24 +0300 Subject: [PATCH 1/4] diktat-examples refactoring and CI (#548) ### What's done: * Updated maven examples, added gradle-groovy examples * Updated functional test * Removed root build.gradle * Update README.md --- .github/workflows/build_and_test.yml | 3 + .github/workflows/diktat_snapshot.yml | 3 + .github/workflows/functional_tests.yml | 31 +- .github/workflows/metrics_for_master.yml | 3 + .github/workflows/release.yml | 8 +- README.md | 4 +- build.gradle.kts | 53 ---- .../test/funcTest/FunctionalTestFile.kt | 5 - examples/gradle-groovy-dsl/build.gradle | 11 + .../diktat-analysis.yml | 12 +- .../src/main/kotlin/Test.kt | 4 +- examples/gradle-kotlin-dsl/build.gradle.kts | 11 + .../gradle-kotlin-dsl/diktat-analysis.yml | 300 ++++++++++++++++++ .../gradle-kotlin-dsl/src/main/kotlin/Test.kt | 13 + examples/gradle/build.gradle.kts | 44 --- examples/maven/diktat-analysis.yml | 12 +- examples/maven/pom.xml | 53 +--- examples/maven/src/main/kotlin/Test.kt | 10 +- pom.xml | 13 - 19 files changed, 420 insertions(+), 173 deletions(-) delete mode 100644 build.gradle.kts delete mode 100644 diktat-rules/src/test/resources/test/funcTest/FunctionalTestFile.kt create mode 100644 examples/gradle-groovy-dsl/build.gradle rename examples/{gradle => gradle-groovy-dsl}/diktat-analysis.yml (93%) rename examples/{gradle => gradle-groovy-dsl}/src/main/kotlin/Test.kt (86%) create mode 100644 examples/gradle-kotlin-dsl/build.gradle.kts create mode 100644 examples/gradle-kotlin-dsl/diktat-analysis.yml create mode 100644 examples/gradle-kotlin-dsl/src/main/kotlin/Test.kt delete mode 100644 examples/gradle/build.gradle.kts diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 822297b063..9ac717ed6f 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -3,6 +3,9 @@ name: Build and test on: pull_request +env: + GRADLE_OPTS: -Dorg.gradle.daemon=false + jobs: test: name: Unit Test diff --git a/.github/workflows/diktat_snapshot.yml b/.github/workflows/diktat_snapshot.yml index 37fa345dfb..7e9789c40a 100644 --- a/.github/workflows/diktat_snapshot.yml +++ b/.github/workflows/diktat_snapshot.yml @@ -6,6 +6,9 @@ on: pull_request: branches: [ master ] +env: + GRADLE_OPTS: -Dorg.gradle.daemon=false + jobs: diktat_snapshot_check: runs-on: ubuntu-latest diff --git a/.github/workflows/functional_tests.yml b/.github/workflows/functional_tests.yml index 84027cee7d..01728aafb0 100644 --- a/.github/workflows/functional_tests.yml +++ b/.github/workflows/functional_tests.yml @@ -7,7 +7,7 @@ on: branches: [ master ] env: - DIKTAT_VERSION: 0.1.2 + DIKTAT_VERSION: 0.1.5 KTLINT_VERSION: 0.39.0 GRADLE_OPTS: -Dorg.gradle.daemon=false # to speed up gradle run @@ -21,7 +21,7 @@ jobs: - name: Setup environment run: | cat pom.xml | grep ".*" | head -1 |awk -F'[><]' '{ENVIRON[DIKTAT_VERSION]=$3 ; print ENVIRON[DIKTAT_VERSION]}' - cat pom.xml | grep ".*" | head -1 |awk -F'[><]' '{ENVIRON[KTLINE_VERSION]=$3 ; print ENVIRON[KTLINE_VERSION]}' + cat pom.xml | grep ".*" | head -1 |awk -F'[><]' '{ENVIRON[KTLINT_VERSION]=$3 ; print ENVIRON[KTLINT_VERSION]}' curl -sSLO https://github.com/pinterest/ktlint/releases/download/$KTLINT_VERSION/ktlint && chmod a+x ktlint curl -sSLO https://github.com/cqfn/diKTat/releases/download/v$DIKTAT_VERSION/diktat-$DIKTAT_VERSION.jar @@ -36,7 +36,7 @@ jobs: - name: Run diKTat from cli continue-on-error: true run: | - ./ktlint -R diktat-$DIKTAT_VERSION.jar "diktat-rules/src/test/resources/test/funcTest/FunctionalTestFile.kt" &> out + ./ktlint -R diktat-$DIKTAT_VERSION.jar "examples/maven/src/main/kotlin/Test.kt" &> out - name: Check output id: cli-check @@ -44,20 +44,37 @@ jobs: cat out grep -E "\[VARIABLE_NAME_INCORRECT_FORMAT\]" out - - name: Run diKTat from gradle + - name: Run diKTat from gradle kotlin DSL continue-on-error: true - run: gradle diktatFunctionalTest &> out + working-directory: ./examples/gradle-kotlin-dsl + run: | + gradle diktatCheck &> out + + - name: Check gradle from kotlin DSL + working-directory: ./examples/gradle-kotlin-dsl + run: | + cat out + grep -E "\[VARIABLE_NAME_INCORRECT_FORMAT\]" out + + - name: Run diKTat from gradle groovy DSL + continue-on-error: true + working-directory: ./examples/gradle-groovy-dsl + run: | + gradle diktatCheck &> out - - name: Check gradle + - name: Check gradle from groovy DSL + working-directory: ./examples/gradle-groovy-dsl run: | cat out grep -E "\[VARIABLE_NAME_INCORRECT_FORMAT\]" out - name: Run diKTat from maven + working-directory: ./examples/maven continue-on-error: true - run: mvn -B diktat:check@functional-test &> out + run: mvn -B diktat:check &> out - name: Check maven + working-directory: ./examples/maven run: | cat out grep -E "\[VARIABLE_NAME_INCORRECT_FORMAT\]" out diff --git a/.github/workflows/metrics_for_master.yml b/.github/workflows/metrics_for_master.yml index b5509b6ee5..21b2ca5ee6 100644 --- a/.github/workflows/metrics_for_master.yml +++ b/.github/workflows/metrics_for_master.yml @@ -5,6 +5,9 @@ on: branches: - 'master' +env: + GRADLE_OPTS: -Dorg.gradle.daemon=false + jobs: master_flow: name: Master branch update diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4e76dc0a35..4fffc01f9a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -67,13 +67,19 @@ jobs: asset_path: ./diktat-ruleset/target/diktat-${{ env.RELEASE_VERSION }}.jar asset_name: diktat-${{ env.RELEASE_VERSION }}.jar asset_content_type: application/zip - - name: Update versions in pom.xml and documentation + - name: Update versions in pom.xml and documentation, update examples id: update-versions run: | mvn -B versions:set -DnextSnapshot=true -DprocessAllModules=true versions:commit mvn versions:set-property -Dproperty=diktat-check.version -DnewVersion=${{ env.RELEASE_VERSION }} sed -i "s/$PREVIOUS_VERSION/$RELEASE_VERSION/g" README.md sed -i "s/$PREVIOUS_VERSION/$RELEASE_VERSION/g" build.gradle.kts + sed -i "s/$PREVIOUS_VERSION/$RELEASE_VERSION/g" .github/workflows/functional_tests.yml + for file in examples/maven/pom.xml examples/gradle-groovy-dsl/build.gradle examples/gradle-kotlin-dsl/build.gradle.kts + do + sed -i "s/$PREVIOUS_VERSION/$RELEASE_VERSION/g" $file + cp diktat-rules/src/main/resources/diktat-analysis.yml $(dirname $file) + done - name: Create pull request uses: peter-evans/create-pull-request@v3 with: diff --git a/README.md b/README.md index 49eeae55d2..3c1c4779cb 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ To run diktat in **only-check** mode use command `$ mvn diktat:check@diktat`. To run diktat in **autocorrect** mode use command `$ mvn diktat:fix@diktat`. ## Run with Gradle using diktat-gradle-plugin -This plugin is available since version 0.1.5. You can see how the plugin is configured in our project for self-checks: [build.gradle.kts](build.gradle.kts). +This plugin is available since version 0.1.5. You can see how the plugin is configured in our examples: [build.gradle.kts](examples/gradle-kotlin-dsl/build.gradle.kts). Add this plugin to your `build.gradle.kts`: ```kotlin plugins { @@ -153,7 +153,7 @@ For example: # all rules are enabled by the default. To disable add 'enabled: false' to the config. enabled: true configuration: - isCopyrightMandatory: true, + isCopyrightMandatory: true copyrightText: Copyright (c) Jeff Lebowski, 2012-2020. All rights reserved. ``` Note, that you can specify and put `diktat-analysis.yml` that contains configuration of diktat in the parent directory of your project on the same level where `build.gradle/pom.xml` is stored. \ diff --git a/build.gradle.kts b/build.gradle.kts deleted file mode 100644 index ac2e6e6c86..0000000000 --- a/build.gradle.kts +++ /dev/null @@ -1,53 +0,0 @@ -val ktlint by configurations.creating - -repositories { - mavenCentral() - jcenter() -} -dependencies { - ktlint("com.pinterest:ktlint:0.39.0") { - // need to exclude standard ruleset to use only diktat rules - exclude("com.pinterest.ktlint", "ktlint-ruleset-standard") - } - - // diktat ruleset - ktlint("org.cqfn.diktat:diktat-rules:0.1.5") -} - -val outputDir = "${project.buildDir}/reports/diktat/" -val inputFiles = project.fileTree(mapOf("dir" to "src", "include" to "**/*.kt")) - -val diktatCheck by tasks.creating(JavaExec::class) { - inputs.files(inputFiles) - outputs.dir(outputDir) - - description = "Check Kotlin code style." - classpath = ktlint - main = "com.pinterest.ktlint.Main" - - // specify proper path to sources that should be checked here - args = listOf("**/src/main/kotlin/**/*.kt") -} - -val diktatFormat by tasks.creating(JavaExec::class) { - inputs.files(inputFiles) - outputs.dir(outputDir) - - description = "Fix Kotlin code style deviations." - classpath = ktlint - main = "com.pinterest.ktlint.Main" - - // specify proper path to sources that should be checked here - args = listOf("-F", "**/src/main/kotlin/**/*.kt") -} - -val diktatFunctionalTest by tasks.creating(JavaExec::class) { - inputs.files(inputFiles) - outputs.dir(outputDir) - - description = "Diktat functional test" - classpath = ktlint - main = "com.pinterest.ktlint.Main" - - args = listOf("diktat-rules/src/test/resources/test/funcTest/FunctionalTestFile.kt") -} \ No newline at end of file diff --git a/diktat-rules/src/test/resources/test/funcTest/FunctionalTestFile.kt b/diktat-rules/src/test/resources/test/funcTest/FunctionalTestFile.kt deleted file mode 100644 index 85168cfe7d..0000000000 --- a/diktat-rules/src/test/resources/test/funcTest/FunctionalTestFile.kt +++ /dev/null @@ -1,5 +0,0 @@ -package test.funcTest - -class FunctionalTestFile { - val AAAA = 5 -} diff --git a/examples/gradle-groovy-dsl/build.gradle b/examples/gradle-groovy-dsl/build.gradle new file mode 100644 index 0000000000..35da1ddf8c --- /dev/null +++ b/examples/gradle-groovy-dsl/build.gradle @@ -0,0 +1,11 @@ +plugins { + id "org.cqfn.diktat.diktat-gradle-plugin" version "0.1.5" +} + +repositories { + mavenCentral() +} + +diktat { + inputs = files("src/**/*.kt") +} diff --git a/examples/gradle/diktat-analysis.yml b/examples/gradle-groovy-dsl/diktat-analysis.yml similarity index 93% rename from examples/gradle/diktat-analysis.yml rename to examples/gradle-groovy-dsl/diktat-analysis.yml index 99cb7048bf..17d166c0b1 100644 --- a/examples/gradle/diktat-analysis.yml +++ b/examples/gradle-groovy-dsl/diktat-analysis.yml @@ -282,9 +282,19 @@ # Use extra functions for it instead. - name: CUSTOM_GETTERS_SETTERS enabled: true +# Checks if null-check was used explicitly (for example: if (a == null)) +# Try to avoid explicit null checks (explicit comparison with `null`) +# Kotlin is declared as [Null-safe](https://kotlinlang.org/docs/reference/null-safety.html) language. +# But Kotlin architects wanted Kotlin to be fully compatible with Java, that's why `null` keyword was also introduced in Kotlin. +# There are several code-structures that can be used in Kotlin to avoid null-checks. For example: `?:`, `.let {}`, `.also {}`, e.t.c +- name: AVOID_NULL_CHECKS + enabled: true # Checks if class instantiation can be wrapped in `apply` for better readability - name: COMPACT_OBJECT_INITIALIZATION enabled: true # Checks explicit supertype qualification - name: USELESS_SUPERTYPE - enabled: true \ No newline at end of file + enabled: true +# Checks if extension function with the same signature don't have related classes +- name: EXTENSION_FUNCTION_SAME_SIGNATURE + enabled: true diff --git a/examples/gradle/src/main/kotlin/Test.kt b/examples/gradle-groovy-dsl/src/main/kotlin/Test.kt similarity index 86% rename from examples/gradle/src/main/kotlin/Test.kt rename to examples/gradle-groovy-dsl/src/main/kotlin/Test.kt index 885e2f69c9..ed2b851143 100644 --- a/examples/gradle/src/main/kotlin/Test.kt +++ b/examples/gradle-groovy-dsl/src/main/kotlin/Test.kt @@ -7,5 +7,7 @@ class incorrectname: Exception { // fun myCommentedFunction() { // } - + +val Incorrect_Val = 5 + } diff --git a/examples/gradle-kotlin-dsl/build.gradle.kts b/examples/gradle-kotlin-dsl/build.gradle.kts new file mode 100644 index 0000000000..bd6af41e63 --- /dev/null +++ b/examples/gradle-kotlin-dsl/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + id("org.cqfn.diktat.diktat-gradle-plugin") version "0.1.5" +} + +repositories { + mavenCentral() +} + +diktat { + inputs = files("src/**/*.kt") +} diff --git a/examples/gradle-kotlin-dsl/diktat-analysis.yml b/examples/gradle-kotlin-dsl/diktat-analysis.yml new file mode 100644 index 0000000000..17d166c0b1 --- /dev/null +++ b/examples/gradle-kotlin-dsl/diktat-analysis.yml @@ -0,0 +1,300 @@ +- name: DIKTAT_COMMON + configuration: + # put your package name here - it will be autofixed and checked + domainName: your.name.here + testDirs: test +- name: CLASS_NAME_INCORRECT + enabled: true +- name: CONSTANT_UPPERCASE + enabled: true +- name: ENUM_VALUE + enabled: true +- name: EXCEPTION_SUFFIX + enabled: true +- name: FILE_NAME_INCORRECT + enabled: true +- name: FILE_NAME_MATCH_CLASS + enabled: true +- name: FUNCTION_BOOLEAN_PREFIX + enabled: true +- name: FUNCTION_NAME_INCORRECT_CASE + enabled: true +- name: GENERIC_NAME + enabled: true +- name: IDENTIFIER_LENGTH + enabled: true +- name: OBJECT_NAME_INCORRECT + enabled: true +- name: PACKAGE_NAME_INCORRECT_CASE + enabled: true # configuration domainName is taken from DIKTAT_COMMON +- name: PACKAGE_NAME_INCORRECT_PREFIX + enabled: false +- name: PACKAGE_NAME_INCORRECT_SYMBOLS + enabled: true +- name: PACKAGE_NAME_INCORRECT_PATH + enabled: false # configuration domainName is taken from DIKTAT_COMMON +- name: PACKAGE_NAME_MISSING + enabled: true +- name: VARIABLE_HAS_PREFIX + enabled: true +- name: VARIABLE_NAME_INCORRECT + enabled: true +- name: VARIABLE_NAME_INCORRECT_FORMAT + enabled: true +- name: MISSING_KDOC_ON_FUNCTION + enabled: true +- name: MISSING_KDOC_TOP_LEVEL + enabled: true +- name: MISSING_KDOC_CLASS_ELEMENTS + enabled: true +- name: KDOC_WITHOUT_PARAM_TAG + enabled: true +- name: KDOC_WITHOUT_RETURN_TAG + enabled: true +- name: KDOC_WITHOUT_THROWS_TAG + enabled: true +- name: KDOC_EMPTY_KDOC + enabled: true +- name: INCORRECT_PACKAGE_SEPARATOR + enabled: true +- name: KDOC_NO_DEPRECATED_TAG + enabled: true +- name: KDOC_NO_EMPTY_TAGS + enabled: true +- name: KDOC_WRONG_SPACES_AFTER_TAG + enabled: true +- name: KDOC_WRONG_TAGS_ORDER + enabled: true +- name: KDOC_NO_NEWLINES_BETWEEN_BASIC_TAGS + enabled: true +- name: KDOC_NEWLINES_BEFORE_BASIC_TAGS + enabled: true +- name: KDOC_NO_NEWLINE_AFTER_SPECIAL_TAGS + enabled: true +- name: KDOC_TRIVIAL_KDOC_ON_FUNCTION + enabled: 'true' +- name: HEADER_WRONG_FORMAT + enabled: true +- name: HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE + enabled: true +- name: HEADER_MISSING_OR_WRONG_COPYRIGHT + enabled: true + configuration: + isCopyrightMandatory: true + copyrightText: 'Copyright (c) Your Company Name Here. 2010-2020' +- name: HEADER_NOT_BEFORE_PACKAGE + enabled: true +- name: HEADER_CONTAINS_DATE_OR_AUTHOR + enabled: true +- name: FILE_IS_TOO_LONG + enabled: true + configuration: + maxSize: '2000' + ignoreFolders: '' +- name: COMMENTED_OUT_CODE + enabled: true +- name: FILE_CONTAINS_ONLY_COMMENTS + enabled: true + # order imports alphabetically +- name: FILE_UNORDERED_IMPORTS + enabled: true + configuration: + # use logical imports grouping with sorting inside of a group + useRecommendedImportsOrder: true +- name: FILE_INCORRECT_BLOCKS_ORDER + enabled: true +- name: FILE_NO_BLANK_LINE_BETWEEN_BLOCKS + enabled: true +# Check: warns if wildcard imports are used except allows. (e.g. import org.cqfn.diktat.*) +- name: FILE_WILDCARD_IMPORTS + enabled: true + configuration: + allowedWildcards: "" # Allowed wildcards for imports (e.g. "import org.cqfn.diktat.*, import org.jetbrains.kotlin.*") + useRecommendedImportsOrder: true +- name: NO_BRACES_IN_CONDITIONALS_AND_LOOPS + enabled: true +- name: WRONG_ORDER_IN_CLASS_LIKE_STRUCTURES + enabled: true +- name: BLANK_LINE_BETWEEN_PROPERTIES + enabled: true +- name: BRACES_BLOCK_STRUCTURE_ERROR + enabled: true + configuration: + openBraceNewline: 'True' + closeBraceNewline: 'True' +- name: WRONG_INDENTATION + enabled: true + configuration: + newlineAtEnd: true + extendedIndentOfParameters: true + alignedParameters: true + extendedIndentAfterOperators: true + indentationSize: 4 +- name: EMPTY_BLOCK_STRUCTURE_ERROR + enabled: true + configuration: + styleEmptyBlockWithNewline: 'True' + allowEmptyBlocks: 'False' +- name: MORE_THAN_ONE_STATEMENT_PER_LINE + enabled: true +- name: LONG_LINE + enabled: true + configuration: + lineLength: '120' +- name: REDUNDANT_SEMICOLON + enabled: true +- name: WRONG_NEWLINES + enabled: true + configuration: + maxParametersInOneLine: 2 +- name: TOO_MANY_CONSECUTIVE_SPACES + enabled: true + configuration: + max_spaces: '1' + saveInitialFormattingForEnums: false +- name: TOO_MANY_BLANK_LINES + enabled: true +- name: WRONG_WHITESPACE + enabled: true +- name: BACKTICKS_PROHIBITED + enabled: true +- name: STRING_CONCATENATION + enabled: true +- name: WHEN_WITHOUT_ELSE + enabled: true +- name: ANNOTATION_NEW_LINE + enabled: true +- name: ENUMS_SEPARATED + enabled: true +- name: LONG_NUMERICAL_VALUES_SEPARATED + enabled: true + configuration: + maxNumberLength: '5' + maxBlockLength: '3' +- name: WRONG_DECLARATIONS_ORDER + enabled: true + configuration: + sortEnum: true + sortProperty: true +- name: WRONG_MULTIPLE_MODIFIERS_ORDER + enabled: true +- name: CONFUSING_IDENTIFIER_NAMING + enabled: true +# Inspection that checks if there is a blank line before kDoc and none after +- name: WRONG_NEWLINES_AROUND_KDOC + enabled: true +# Inspection that checks if there is no blank lines before first comment +- name: FIRST_COMMENT_NO_SPACES + enabled: true +# Inspection that checks if there are blank lines between code and comment and between code start token and comment's text +- name: COMMENT_WHITE_SPACE + enabled: true + configuration: + maxSpacesBeforeComment: 2 + maxSpacesInComment: 1 +# Inspection that checks if all comment's are inside if-else code blocks. Exception is general if comment +- name: IF_ELSE_COMMENTS + enabled: true +- name: WRONG_COPYRIGHT_YEAR + enabled: true +# Inspection that checks if local variables are declared close to the first usage site +- name: LOCAL_VARIABLE_EARLY_DECLARATION + enabled: true +# Try to avoid initialize val by null (e.g. val a: Int? = null -> val a: Int = 0) +- name: NULLABLE_PROPERTY_TYPE + enabled: true +# Type aliases provide alternative names for existing types when type's reference text is longer 25 chars +- name: TYPE_ALIAS + enabled: true + configuration: + typeReferenceLength: '25' # max length of type reference +- name: SMART_CAST_NEEDED + enabled: true +- name: GENERIC_VARIABLE_WRONG_DECLARATION + enabled: true +# Inspection that checks if string template has redundant curly braces +- name: STRING_TEMPLATE_CURLY_BRACES + enabled: true +# Variables with `val` modifier - are immutable (read-only). Usage of such variables instead of `var` variables increases +# robustness and readability of code, because `var` variables can be reassigned several times in the business logic. +# This rule prohibits usage of `var`s as local variables - the only exception is accumulators and counters +- name: SAY_NO_TO_VAR + enabled: true +# Inspection that checks if string template has redundant quotes +- name: STRING_TEMPLATE_QUOTES + enabled: true +# Checks that floating-point values are not used in arithmetic expressions +- name: FLOAT_IN_ACCURATE_CALCULATIONS + enabled: true +# Checks that function length isn't too long +- name: TOO_LONG_FUNCTION + enabled: true + configuration: + maxFunctionLength: '30' # max length of function + isIncludeHeader: 'false' # count function's header +# Warns if there are nested functions +- name: AVOID_NESTED_FUNCTIONS + enabled: true +# Checks that lambda inside function parameters is in the end +- name: LAMBDA_IS_NOT_LAST_PARAMETER + enabled: true +# Checks that function doesn't contains too many parameters +- name: TOO_MANY_PARAMETERS + enabled: true + configuration: + maxParameterListSize: '5' # max parameters size +# Checks that function doesn't have too many nested blocks +- name: NESTED_BLOCK + enabled: true + configuration: + maxNestedBlockQuantity: '4' +# Checks that function use default values, instead overloading +- name: WRONG_OVERLOADING_FUNCTION_ARGUMENTS + enabled: true +# Checks that KDoc in constructor has property tag +- name: KDOC_NO_CONSTRUCTOR_PROPERTY + enabled: true +# Checks that KDoc in constructor has property tag but with comment inside constructor +- name: KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT + enabled: true +# if a class has single constructor, it should be converted to a primary constructor +- name: SINGLE_CONSTRUCTOR_SHOULD_BE_PRIMARY + enabled: true +# Checks if class can be made as data class +- name: USE_DATA_CLASS + enabled: true +# Checks that never use the name of a variable in the custom getter or setter +- name: WRONG_NAME_OF_VARIABLE_INSIDE_ACCESSOR + enabled: true +# Checks that classes have only one init block +- name: MULTIPLE_INIT_BLOCKS + enabled: true +# Checks that there are abstract functions in abstract class +- name: CLASS_SHOULD_NOT_BE_ABSTRACT + enabled: true +# Checks if there are any trivial getters or setters +- name: TRIVIAL_ACCESSORS_ARE_NOT_RECOMMENDED + enabled: true +# Checks that no custom getters and setters are used for properties. It is a more wide rule than TRIVIAL_ACCESSORS_ARE_NOT_RECOMMENDED +# Kotlin compiler automatically generates `get` and `set` methods for properties and also lets the possibility to override it. +# But in all cases it is very confusing when `get` and `set` are overridden for a developer who uses this particular class. +# Developer expects to get the value of the property, but receives some unknown value and some extra side effect hidden by the custom getter/setter. +# Use extra functions for it instead. +- name: CUSTOM_GETTERS_SETTERS + enabled: true +# Checks if null-check was used explicitly (for example: if (a == null)) +# Try to avoid explicit null checks (explicit comparison with `null`) +# Kotlin is declared as [Null-safe](https://kotlinlang.org/docs/reference/null-safety.html) language. +# But Kotlin architects wanted Kotlin to be fully compatible with Java, that's why `null` keyword was also introduced in Kotlin. +# There are several code-structures that can be used in Kotlin to avoid null-checks. For example: `?:`, `.let {}`, `.also {}`, e.t.c +- name: AVOID_NULL_CHECKS + enabled: true +# Checks if class instantiation can be wrapped in `apply` for better readability +- name: COMPACT_OBJECT_INITIALIZATION + enabled: true +# Checks explicit supertype qualification +- name: USELESS_SUPERTYPE + enabled: true +# Checks if extension function with the same signature don't have related classes +- name: EXTENSION_FUNCTION_SAME_SIGNATURE + enabled: true diff --git a/examples/gradle-kotlin-dsl/src/main/kotlin/Test.kt b/examples/gradle-kotlin-dsl/src/main/kotlin/Test.kt new file mode 100644 index 0000000000..80975f0974 --- /dev/null +++ b/examples/gradle-kotlin-dsl/src/main/kotlin/Test.kt @@ -0,0 +1,13 @@ +package incorrect + +class incorrectname: Exception { + fun INCORRECT_FUNCTION() { + throw Exception() + } + + // fun myCommentedFunction() { + // } + + val Incorrect_Val = 5 + +} diff --git a/examples/gradle/build.gradle.kts b/examples/gradle/build.gradle.kts deleted file mode 100644 index 365f2749a1..0000000000 --- a/examples/gradle/build.gradle.kts +++ /dev/null @@ -1,44 +0,0 @@ -val ktlint by configurations.creating - -repositories { - mavenCentral() - jcenter() -} - -dependencies { - ktlint("com.pinterest:ktlint:0.39.0") { - // need to exclude standard ruleset to use only diktat rules - exclude("com.pinterest.ktlint", "ktlint-ruleset-standard") - } - - // diktat ruleset - ktlint("org.cqfn.diktat:diktat-rules:0.1.3") { - } -} - -val outputDir = "${project.buildDir}/reports/diktat/" -val inputFiles = project.fileTree(mapOf("dir" to "src", "include" to "**/*.kt")) - -val diktatCheck by tasks.creating(JavaExec::class) { - inputs.files(inputFiles) - outputs.dir(outputDir) - - description = "Check Kotlin code style." - classpath = ktlint - main = "com.pinterest.ktlint.Main" - - // specify proper path to sources that should be checked here - args = listOf("src/main/kotlin/**/*.kt") -} - -val diktatFormat by tasks.creating(JavaExec::class) { - inputs.files(inputFiles) - outputs.dir(outputDir) - - description = "Fix Kotlin code style deviations." - classpath = ktlint - main = "com.pinterest.ktlint.Main" - - // specify proper path here - args = listOf("-F","src/main/kotlin/**/*.kt") -} diff --git a/examples/maven/diktat-analysis.yml b/examples/maven/diktat-analysis.yml index 99cb7048bf..17d166c0b1 100644 --- a/examples/maven/diktat-analysis.yml +++ b/examples/maven/diktat-analysis.yml @@ -282,9 +282,19 @@ # Use extra functions for it instead. - name: CUSTOM_GETTERS_SETTERS enabled: true +# Checks if null-check was used explicitly (for example: if (a == null)) +# Try to avoid explicit null checks (explicit comparison with `null`) +# Kotlin is declared as [Null-safe](https://kotlinlang.org/docs/reference/null-safety.html) language. +# But Kotlin architects wanted Kotlin to be fully compatible with Java, that's why `null` keyword was also introduced in Kotlin. +# There are several code-structures that can be used in Kotlin to avoid null-checks. For example: `?:`, `.let {}`, `.also {}`, e.t.c +- name: AVOID_NULL_CHECKS + enabled: true # Checks if class instantiation can be wrapped in `apply` for better readability - name: COMPACT_OBJECT_INITIALIZATION enabled: true # Checks explicit supertype qualification - name: USELESS_SUPERTYPE - enabled: true \ No newline at end of file + enabled: true +# Checks if extension function with the same signature don't have related classes +- name: EXTENSION_FUNCTION_SAME_SIGNATURE + enabled: true diff --git a/examples/maven/pom.xml b/examples/maven/pom.xml index b96bdd69fa..96a3500c0b 100644 --- a/examples/maven/pom.xml +++ b/examples/maven/pom.xml @@ -3,59 +3,30 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.cqfn.diktat - diktat-maven + diktat-examples-maven pom 1.0.0-SNAPSHOT - - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - - org.apache.maven.plugins - maven-antrun-plugin - 3.0.0 + org.cqfn.diktat + diktat-maven-plugin + 0.1.5 + + diktat-analysis.yml + + ${project.basedir}/src/main/kotlin + + - diktat - none - - - - - - - + diktat-check - run + check - - - com.pinterest - ktlint - 0.39.0 - - - com.pinterest.ktlint - ktlint-ruleset-standard - - - - - org.cqfn.diktat - diktat-rules - 0.1.3 - - diff --git a/examples/maven/src/main/kotlin/Test.kt b/examples/maven/src/main/kotlin/Test.kt index 885e2f69c9..80975f0974 100644 --- a/examples/maven/src/main/kotlin/Test.kt +++ b/examples/maven/src/main/kotlin/Test.kt @@ -1,11 +1,13 @@ package incorrect class incorrectname: Exception { - fun INCORRECT_FUNCTION() { - throw Exception() + fun INCORRECT_FUNCTION() { + throw Exception() } - + // fun myCommentedFunction() { // } - + + val Incorrect_Val = 5 + } diff --git a/pom.xml b/pom.xml index ffac531fdd..06edf5e88c 100644 --- a/pom.xml +++ b/pom.xml @@ -330,19 +330,6 @@ diktat-analysis.yml - - functional-test - none - - check - - - - ${project.basedir}/src/test/resources/test/funcTest - - diktat-analysis.yml - - From 205dbfc5023366fde24cbaa157bc60f6990b0661 Mon Sep 17 00:00:00 2001 From: Andrey Kuleshov Date: Fri, 20 Nov 2020 13:12:26 +0300 Subject: [PATCH 2/4] Removing complex cases from the warning related to null checks (#532) Removing complex cases from the warning related to null checks ### What's done: Null check will not trigger on the following code: 1) if (myVar == null || otherValue == 5 && isValid) {} 2) if (myVar != null) { println("not null") } else if (true) { println("Other condition") } --- .../diktat/ruleset/rules/NullChecksRule.kt | 37 ++++++++++--- .../chapter4/NullChecksRuleWarnTest.kt | 53 ++++++++++++++++++- info/guide/guide-chapter-8.md | 16 ++++++ 3 files changed, 99 insertions(+), 7 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/NullChecksRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/NullChecksRule.kt index dc31c9d782..d4ea4ea891 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/NullChecksRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/NullChecksRule.kt @@ -6,6 +6,7 @@ import com.pinterest.ktlint.core.ast.ElementType import com.pinterest.ktlint.core.ast.ElementType.BINARY_EXPRESSION import com.pinterest.ktlint.core.ast.ElementType.CONDITION import com.pinterest.ktlint.core.ast.ElementType.IF +import com.pinterest.ktlint.core.ast.ElementType.IF_KEYWORD import com.pinterest.ktlint.core.ast.ElementType.NULL import com.pinterest.ktlint.core.ast.parent import org.cqfn.diktat.common.config.rules.RulesConfig @@ -13,6 +14,7 @@ import org.cqfn.diktat.ruleset.constants.Warnings.AVOID_NULL_CHECKS import org.cqfn.diktat.ruleset.utils.* import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.psi.KtBinaryExpression +import org.jetbrains.kotlin.psi.KtIfExpression /** * This rule check and fixes explicit null checks (explicit comparison with `null`) @@ -30,8 +32,11 @@ class NullChecksRule(private val configRules: List) : Rule("null-ch if (node.elementType == CONDITION) { node.parent(IF)?.let { - // this can be autofixed as the condition stays in if-statement - conditionInIfStatement(node) + // excluding complex cases with else-if statements, because they look better with explicit null-check + if (!isComplexIfStatement(it)) { + // this can be autofixed as the condition stays in simple if-statement + conditionInIfStatement(node) + } } } @@ -44,6 +49,16 @@ class NullChecksRule(private val configRules: List) : Rule("null-ch } } + /** + * checks that if-statement is a complex condition + * You can name a statement - "complex if-statement" if it has other if in the else branch (else-if structure) + */ + private fun isComplexIfStatement(parentIf: ASTNode): Boolean { + val parentIfPsi = parentIf.psi + require(parentIfPsi is KtIfExpression) + return (parentIfPsi.`else`?.node?.firstChildNode?.elementType == IF_KEYWORD) + } + private fun conditionInIfStatement(node: ASTNode) { node.findAllNodesWithSpecificType(BINARY_EXPRESSION).forEach { binaryExprNode -> val condition = (binaryExprNode.psi as KtBinaryExpression) @@ -69,11 +84,21 @@ class NullChecksRule(private val configRules: List) : Rule("null-ch @Suppress("UnsafeCallOnNullableType") private fun isNullCheckBinaryExpession(condition: KtBinaryExpression): Boolean = - // check that binary expession has `null` as right or left operand - setOf(condition.right, condition.left).map { it!!.node.elementType }.contains(NULL) && - // checks that it is the comparison condition - setOf(ElementType.EQEQ, ElementType.EQEQEQ, ElementType.EXCLEQ, ElementType.EXCLEQEQEQ).contains(condition.operationToken) + // check that binary expession has `null` as right or left operand + setOf(condition.right, condition.left).map { it!!.node.elementType }.contains(NULL) && + // checks that it is the comparison condition + setOf(ElementType.EQEQ, ElementType.EQEQEQ, ElementType.EXCLEQ, ElementType.EXCLEQEQEQ).contains(condition.operationToken) && + // no need to raise warning or fix null checks in complex expressions + !condition.isComplexCondition() + /** + * checks if condition is a complex expression. For example: + * (a == 5) - is not a complex condition, but (a == 5 && b != 6) is a complex condition + */ + private fun KtBinaryExpression.isComplexCondition(): Boolean { + // KtBinaryExpression is complex if it has a parent that is also a binary expression + return this.parent is KtBinaryExpression + } private fun warnAndFixOnNullCheck(condition: KtBinaryExpression, canBeAutoFixed: Boolean, autofix: () -> Unit) { AVOID_NULL_CHECKS.warnAndFix( diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter4/NullChecksRuleWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter4/NullChecksRuleWarnTest.kt index 0cd9ebc139..c34596ea5b 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter4/NullChecksRuleWarnTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter4/NullChecksRuleWarnTest.kt @@ -37,7 +37,7 @@ class NullChecksRuleWarnTest : LintTestBase(::NullChecksRule) { """ | fun foo() { | var myVar: Int? = null - | if ((myVar == null) && (true)) { + | if ((myVar == null) && (true) || isValid) { | println("null") | return | } @@ -116,4 +116,55 @@ class NullChecksRuleWarnTest : LintTestBase(::NullChecksRule) { LintError(5, 19, ruleId, "${Warnings.AVOID_NULL_CHECKS.warnText()} myVar == null", false), ) } + + @Test + @Tag(WarningNames.AVOID_NULL_CHECKS) + fun `equals to null, but in complex else-if statement`() { + lintMethod( + """ + | fun foo0() { + | if (myVar != null) { + | println("not null") + | } else if (true) { + | println() + | } + | } + """.trimMargin() + ) + } + + @Test + @Tag(WarningNames.AVOID_NULL_CHECKS) + fun `equals to null, but in complex else-if statement with dummy comment`() { + lintMethod( + """ + | fun foo0() { + | if (myVar != null) { + | println("not null") + | } else /* test comment */ if (true) { + | println() + | } + | } + """.trimMargin() + ) + } + + @Test + @Tag(WarningNames.AVOID_NULL_CHECKS) + fun `equals to null, but the expression is not a else-if`() { + lintMethod( + """ + | fun foo0() { + | if (myVar != null) { + | println("not null") + | } else { + | if (true) { + | println() + | } + | } + | } + """.trimMargin(), + LintError(2, 10, ruleId, "${Warnings.AVOID_NULL_CHECKS.warnText()} myVar != null", true), + ) + } } diff --git a/info/guide/guide-chapter-8.md b/info/guide/guide-chapter-8.md index b133365b85..7c19482263 100644 --- a/info/guide/guide-chapter-8.md +++ b/info/guide/guide-chapter-8.md @@ -65,3 +65,19 @@ myVar?.let { println("null") } ?: run { println("not null") } ``` + +**Exceptions:** in case of complex expressions like multiple `else-if` structures, or a long conditional statement there is a common sense to use explicit comparison with `null`. + +**Valid examples:** + +```kotlin +if (myVar != null) { + println("not null") +} else if (anotherCondition) { + println("Other condition") +} +``` + +```kotlin +if (myVar == null || otherValue == 5 && isValid) {} +``` From 052bd9de61a0f4e371805dde0c5e8af0f0064eaf Mon Sep 17 00:00:00 2001 From: Andrey Kuleshov Date: Mon, 23 Nov 2020 10:51:22 +0300 Subject: [PATCH 3/4] Adding warnings to rules mapping in enum (#558) Adding warnings to rules mapping in enum ### What's done: 1) cosmetic changes in codestyle 2) warnings enum extended 3) small generator and test --- .../org/cqfn/diktat/common/cli/CliArgument.kt | 2 +- .../org/cqfn/diktat/test/ConfigReaderTest.kt | 3 +- .../cqfn/diktat/ruleset/constants/Warnings.kt | 210 +++++++++--------- .../constants/WarningsTableGenerator.kt | 32 +++ .../ruleset/utils/WarningsGenerationTest.kt | 13 ++ info/guide/guide-chapter-1.md | 3 +- info/guide/guide-chapter-2.md | 6 +- info/guide/guide-chapter-3.md | 4 +- info/guide/guide-chapter-4.md | 2 +- info/rules-mapping.md | 104 +++++++++ 10 files changed, 266 insertions(+), 113 deletions(-) create mode 100644 diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/WarningsTableGenerator.kt create mode 100644 diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/WarningsGenerationTest.kt create mode 100644 info/rules-mapping.md diff --git a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/cli/CliArgument.kt b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/cli/CliArgument.kt index c391df76c0..8e7176dd4a 100644 --- a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/cli/CliArgument.kt +++ b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/cli/CliArgument.kt @@ -2,7 +2,7 @@ package org.cqfn.diktat.common.cli import org.apache.commons.cli.Option -import kotlinx.serialization.* +import kotlinx.serialization.Serializable /** * This class is used to serialize/deserialize json representation diff --git a/diktat-common/src/test/kotlin/org/cqfn/diktat/test/ConfigReaderTest.kt b/diktat-common/src/test/kotlin/org/cqfn/diktat/test/ConfigReaderTest.kt index 5a07324021..f989351a28 100644 --- a/diktat-common/src/test/kotlin/org/cqfn/diktat/test/ConfigReaderTest.kt +++ b/diktat-common/src/test/kotlin/org/cqfn/diktat/test/ConfigReaderTest.kt @@ -7,7 +7,8 @@ import org.junit.jupiter.api.Test class ConfigReaderTest { @Test fun `testing json reading`() { - val rulesConfigList: List? = RulesConfigReader(javaClass.classLoader).readResource("src/test/resources/test-rules-config.yml") + val rulesConfigList: List? = RulesConfigReader(javaClass.classLoader) + .readResource("src/test/resources/test-rules-config.yml") require(rulesConfigList != null) assert(rulesConfigList.any { it.name == "CLASS_NAME_INCORRECT" && it.enabled }) assert(rulesConfigList.find { it.name == "CLASS_NAME_INCORRECT" }?.configuration == mapOf()) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt index 7bc8ea670c..29eb0cf743 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt @@ -12,126 +12,124 @@ typealias EmitType = ((offset: Int, errorMessage: String, canBeAutoCorrected: Bo * This class represent individual inspections of diktat code style. * A [Warnings] entry contains rule name, warning message and is used in code check. */ -@Suppress("ForbiddenComment", "MagicNumber", "WRONG_DECLARATIONS_ORDER") -enum class Warnings(private val canBeAutoCorrected: Boolean, private val warn: String) : Rule { +@Suppress("ForbiddenComment", "MagicNumber", "WRONG_DECLARATIONS_ORDER", "MaxLineLength") +enum class Warnings(val canBeAutoCorrected: Boolean, val ruleId: String, private val warn: String) : Rule { // ======== chapter 1 ======== - PACKAGE_NAME_MISSING(true, "no package name declared in a file"), - PACKAGE_NAME_INCORRECT_CASE(true, "package name should be completely in a lower case"), - PACKAGE_NAME_INCORRECT_PREFIX(true, "package name should start from company's domain"), + PACKAGE_NAME_MISSING(true, "1.2.1", "no package name declared in a file"), + PACKAGE_NAME_INCORRECT_CASE(true, "1.2.1", "package name should be completely in a lower case"), + PACKAGE_NAME_INCORRECT_PREFIX(true, "1.2.1", "package name should start from company's domain"), // FixMe: should add autofix - PACKAGE_NAME_INCORRECT_SYMBOLS(false, "package name should contain only latin (ASCII) letters or numbers. For separation of words use dot"), - PACKAGE_NAME_INCORRECT_PATH(true, "package name does not match the directory hierarchy for this file, the real package name should be"), - INCORRECT_PACKAGE_SEPARATOR(true, "package name parts should be separated only by dots - there should be no other symbols like underscores (_)"), - CLASS_NAME_INCORRECT(true, "class/enum/interface name should be in PascalCase and should contain only latin (ASCII) letters or numbers"), - OBJECT_NAME_INCORRECT(true, "object structure name should be in PascalCase and should contain only latin (ASCII) letters or numbers"), - VARIABLE_NAME_INCORRECT_FORMAT(true, "variable name should be in camel case (correct: checkIpConfig, incorrect: CheckIPConfig)" + - " should contain only latin (ASCII) letters or numbers and should start from lower letter"), - VARIABLE_NAME_INCORRECT(false, "variable name should contain more than one letter"), - CONSTANT_UPPERCASE(true, " properties from companion object or on file level mostly in all cases are constants - please use upper snake case for them"), - VARIABLE_HAS_PREFIX(true, "variable has prefix (like mVariable or M_VARIABLE), generally it is a bad code style (Android - is the only exception)"), - IDENTIFIER_LENGTH(false, "identifier's length is incorrect, it should be in range of [2, 64] symbols"), - ENUM_VALUE(true, "enum values should be in selected UPPER_CASE snake/PascalCase format"), - GENERIC_NAME(true, "generic name should contain only one single capital letter, it can be followed by a number"), - FUNCTION_NAME_INCORRECT_CASE(true, "function/method name should be in lowerCamelCase"), - FUNCTION_BOOLEAN_PREFIX(true, "functions that return the value of Boolean type should have or prefix"), - FILE_NAME_INCORRECT(true, "file name is incorrect - it should end with .kt extension and be in PascalCase"), - FILE_NAME_MATCH_CLASS(true, "file name is incorrect - it should match with the class described in it if there is the only one class declared"), - EXCEPTION_SUFFIX(true, "all exception classes should have \"Exception\" suffix"), - CONFUSING_IDENTIFIER_NAMING(false, "it's a bad name for identifier"), + PACKAGE_NAME_INCORRECT_SYMBOLS(false, "1.2.1", "package name should contain only latin (ASCII) letters or numbers. For separation of words use dot"), + PACKAGE_NAME_INCORRECT_PATH(true, "1.2.1", "package name does not match the directory hierarchy for this file, the real package name should be"), + INCORRECT_PACKAGE_SEPARATOR(true, "1.2.1", "package name parts should be separated only by dots - there should be no other symbols like underscores (_)"), + CLASS_NAME_INCORRECT(true, "1.3.1", "class/enum/interface name should be in PascalCase and should contain only latin (ASCII) letters or numbers"), + OBJECT_NAME_INCORRECT(true, "1.3.1", "object structure name should be in PascalCase and should contain only latin (ASCII) letters or numbers"), + VARIABLE_NAME_INCORRECT_FORMAT(true, "1.6.1", "variable name should be in lowerCamelCase and should contain only latin (ASCII) letters or numbers and should start from lower letter"), + VARIABLE_NAME_INCORRECT(false, "1.1.1", "variable name should contain more than one letter"), + CONSTANT_UPPERCASE(true, "1.5.1", " properties from companion object or on file level mostly in all cases are constants - please use upper snake case for them"), + VARIABLE_HAS_PREFIX(true, "1.1.1", "variable has prefix (like mVariable or M_VARIABLE), generally it is a bad code style (Android - is the only exception)"), + IDENTIFIER_LENGTH(false, "1.1.1", "identifier's length is incorrect, it should be in range of [2, 64] symbols"), + ENUM_VALUE(true, "1.3.1", "enum values should be in selected UPPER_CASE snake/PascalCase format"), + GENERIC_NAME(true, "1.1.1", "generic name should contain only one single capital letter, it can be followed by a number"), + BACKTICKS_PROHIBITED(false, "1.1.1", "backticks should not be used in identifier's naming. The only exception test methods marked with @Test annotation"), + FUNCTION_NAME_INCORRECT_CASE(true, "1.4.1", "function/method name should be in lowerCamelCase"), + FUNCTION_BOOLEAN_PREFIX(true, "1.6.2", "functions that return the value of Boolean type should have or prefix"), + FILE_NAME_INCORRECT(true, "1.1.1", "file name is incorrect - it should end with .kt extension and be in PascalCase"), + EXCEPTION_SUFFIX(true, "1.1.1", "all exception classes should have \"Exception\" suffix"), + CONFUSING_IDENTIFIER_NAMING(false, "1.1.1", "it's a bad name for identifier"), // ======== chapter 2 ======== - MISSING_KDOC_TOP_LEVEL(false, "all public and internal top-level classes and functions should have Kdoc"), - MISSING_KDOC_CLASS_ELEMENTS(false, "all public, internal and protected classes, functions and variables inside the class should have Kdoc"), - MISSING_KDOC_ON_FUNCTION(true, "all public, internal and protected functions should have Kdoc with proper tags"), - KDOC_TRIVIAL_KDOC_ON_FUNCTION(false, "KDocs should not be trivial (e.g. method getX should not de documented as 'returns X')"), - KDOC_WITHOUT_PARAM_TAG(true, "all methods which take arguments should have @param tags in KDoc"), - KDOC_WITHOUT_RETURN_TAG(true, "all methods which return values should have @return tag in KDoc"), - KDOC_WITHOUT_THROWS_TAG(true, "all methods which throw exceptions should have @throws tag in KDoc"), - KDOC_EMPTY_KDOC(false, "KDoc should never be empty"), - KDOC_WRONG_SPACES_AFTER_TAG(true, "there should be exactly one white space after tag name in KDoc"), - KDOC_WRONG_TAGS_ORDER(true, "in KDoc standard tags are arranged in order @param, @return, @throws, but are"), - KDOC_NEWLINES_BEFORE_BASIC_TAGS(true, "in KDoc block of standard tags @param, @return, @throws should contain newline before only if there is other content before it"), - KDOC_NO_NEWLINES_BETWEEN_BASIC_TAGS(true, "in KDoc standard tags @param, @return, @throws should not containt newline between them, but these tags do"), - KDOC_NO_NEWLINE_AFTER_SPECIAL_TAGS(true, "in KDoc there should be exactly one empty line after special tags"), - KDOC_NO_EMPTY_TAGS(false, "no empty descriptions in tag blocks are allowed"), - KDOC_NO_DEPRECATED_TAG(true, "KDoc doesn't support @deprecated tag, use @Deprecated annotation instead"), - KDOC_NO_CONSTRUCTOR_PROPERTY(true, "all properties from the primary constructor should be documented in a @property tag in KDoc"), - KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT(true, "replace comment before property with @property tag in class KDoc"), - HEADER_WRONG_FORMAT(true, "file header comments should be properly formatted"), - HEADER_MISSING_OR_WRONG_COPYRIGHT(true, "file header comment must include copyright information inside a block comment"), - WRONG_COPYRIGHT_YEAR(true, "year defined in copyright and current year are different"), - HEADER_CONTAINS_DATE_OR_AUTHOR(false, "file header comment should not contain creation date and author name"), - HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE(false, "files that contain multiple or no classes should contain description of what is inside of this file"), - HEADER_NOT_BEFORE_PACKAGE(true, "header KDoc should be placed before package and imports"), - COMMENTED_OUT_CODE(false, "you should not comment out code, use VCS to save it in history and delete this block"), - WRONG_NEWLINES_AROUND_KDOC(true, "there should be a blank line above the kDoc and there should not be no blank lines after kDoc"), - FIRST_COMMENT_NO_SPACES(true, "there should not be any spaces before first comment"), - COMMENT_WHITE_SPACE(true, "there should be a white space between code and comment also between code start token and comment text"), - IF_ELSE_COMMENTS(true, "invalid comments structure. Comment should be inside the block"), + MISSING_KDOC_TOP_LEVEL(false, "2.1.1", "all public and internal top-level classes and functions should have Kdoc"), + MISSING_KDOC_CLASS_ELEMENTS(false, "2.1.1", "all public, internal and protected classes, functions and variables inside the class should have Kdoc"), + MISSING_KDOC_ON_FUNCTION(true, "2.1.1", "all public, internal and protected functions should have Kdoc with proper tags"), + KDOC_TRIVIAL_KDOC_ON_FUNCTION(false, "2.3.1", "KDocs should not be trivial (e.g. method getX should not de documented as 'returns X')"), + KDOC_WITHOUT_PARAM_TAG(true, "2.1.2", "all methods which take arguments should have @param tags in KDoc"), + KDOC_WITHOUT_RETURN_TAG(true, "2.1.2", "all methods which return values should have @return tag in KDoc"), + KDOC_WITHOUT_THROWS_TAG(true, "2.1.2", "all methods which throw exceptions should have @throws tag in KDoc"), + KDOC_EMPTY_KDOC(false, "2.1.3", "KDoc should never be empty"), + KDOC_WRONG_SPACES_AFTER_TAG(true, "2.1.3", "there should be exactly one white space after tag name in KDoc"), + KDOC_WRONG_TAGS_ORDER(true, "2.1.3", "in KDoc standard tags are arranged in order @param, @return, @throws, but are"), + KDOC_NEWLINES_BEFORE_BASIC_TAGS(true, "2.1.3", "in KDoc block of standard tags @param, @return, @throws should contain newline before only if there is other content before it"), + KDOC_NO_NEWLINES_BETWEEN_BASIC_TAGS(true, "2.1.3", "in KDoc standard tags @param, @return, @throws should not containt newline between them, but these tags do"), + KDOC_NO_NEWLINE_AFTER_SPECIAL_TAGS(true, "2.1.3", "in KDoc there should be exactly one empty line after special tags"), + KDOC_NO_EMPTY_TAGS(false, "2.2.1", "no empty descriptions in tag blocks are allowed"), + KDOC_NO_DEPRECATED_TAG(true, "2.1.3", "KDoc doesn't support @deprecated tag, use @Deprecated annotation instead"), + KDOC_NO_CONSTRUCTOR_PROPERTY(true, "2.1.1", "all properties from the primary constructor should be documented in a @property tag in KDoc"), + KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT(true, "2.1.1", "replace comment before property with @property tag in class KDoc"), + HEADER_WRONG_FORMAT(true, "2.2.1", "file header comments should be properly formatted"), + HEADER_MISSING_OR_WRONG_COPYRIGHT(true, "2.2.1", "file header comment must include copyright information inside a block comment"), + WRONG_COPYRIGHT_YEAR(true, "2.2.1", "year defined in copyright and current year are different"), + HEADER_CONTAINS_DATE_OR_AUTHOR(false, "2.2.1", "file header comment should not contain creation date and author name"), + HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE(false, "2.2.1", "files that contain multiple or no classes should contain description of what is inside of this file"), + HEADER_NOT_BEFORE_PACKAGE(true, "2.2.1", "header KDoc should be placed before package and imports"), + COMMENTED_OUT_CODE(false, "2.4.2", "you should not comment out code, use VCS to save it in history and delete this block"), + WRONG_NEWLINES_AROUND_KDOC(true, "2.4.1", "there should be a blank line above the kDoc and there should not be no blank lines after kDoc"), + FIRST_COMMENT_NO_SPACES(true, "2.4.1", "there should not be any spaces before first comment"), + COMMENT_WHITE_SPACE(true, "2.4.1", "there should be a white space between code and comment also between code start token and comment text"), + IF_ELSE_COMMENTS(true, "2.4.1", "invalid comments structure. Comment should be inside the block"), // ======== chapter 3 ======== - FILE_IS_TOO_LONG(false, "file has more number of lines than expected"), - FILE_CONTAINS_ONLY_COMMENTS(false, "source code files which contain only comments should be avoided"), - FILE_INCORRECT_BLOCKS_ORDER(true, "general structure of kotlin source file is wrong, parts are in incorrect order"), - FILE_NO_BLANK_LINE_BETWEEN_BLOCKS(true, "general structure of kotlin source file is wrong, general code blocks sohuld be separated by empty lines"), - FILE_UNORDERED_IMPORTS(true, "imports should be ordered alphabetically and shouldn't be separated by newlines"), - FILE_WILDCARD_IMPORTS(false, "wildcard imports should not be used"), - NO_BRACES_IN_CONDITIONALS_AND_LOOPS(true, "in if, else, when, for, do, and while statements braces should be used. Exception: single line if statement."), - WRONG_ORDER_IN_CLASS_LIKE_STRUCTURES(true, "the declaration part of a class-like code structures (class/interface/etc.) should be in the proper order"), - BLANK_LINE_BETWEEN_PROPERTIES(true, "there should be no blank lines between properties without comments; comment or KDoc on property should have blank line before"), - BRACES_BLOCK_STRUCTURE_ERROR(true, "braces should follow 1TBS style"), - WRONG_INDENTATION(true, "only spaces are allowed for indentation and each indentation should equal to 4 spaces (tabs are not allowed)"), - EMPTY_BLOCK_STRUCTURE_ERROR(true, "incorrect format of empty block"), - MORE_THAN_ONE_STATEMENT_PER_LINE(true, "There should not be more than one code statement in one line"), - LONG_LINE(true, "This line is longer than allowed"), - BACKTICKS_PROHIBITED(false, "Backticks should not be used in identifier's naming. The only exception test methods marked with @Test annotation"), - REDUNDANT_SEMICOLON(true, "there should be no redundant semicolon at the end of lines"), - WRONG_NEWLINES(true, "incorrect line breaking"), + FILE_IS_TOO_LONG(false, "3.1.1", "file has more number of lines than expected"), + FILE_CONTAINS_ONLY_COMMENTS(false, "3.1.2", "empty files or files that contain only comments should be avoided"), + FILE_INCORRECT_BLOCKS_ORDER(true, "3.1.2", "general structure of kotlin source file is wrong, parts are in incorrect order"), + FILE_NO_BLANK_LINE_BETWEEN_BLOCKS(true, "3.1.2", "general structure of kotlin source file is wrong, general code blocks sohuld be separated by empty lines"), + FILE_UNORDERED_IMPORTS(true, "3.1.2", "imports should be ordered alphabetically and shouldn't be separated by newlines"), + FILE_WILDCARD_IMPORTS(false, "3.1.2", "wildcard imports should not be used"), + NO_BRACES_IN_CONDITIONALS_AND_LOOPS(true, "3.2.1", "in if, else, when, for, do, and while statements braces should be used. Exception: single line if statement."), + WRONG_ORDER_IN_CLASS_LIKE_STRUCTURES(true, "3.1.4", "the declaration part of a class-like code structures (class/interface/etc.) should be in the proper order"), + BLANK_LINE_BETWEEN_PROPERTIES(true, "3.1.4", "there should be no blank lines between properties without comments; comment or KDoc on property should have blank line before"), + BRACES_BLOCK_STRUCTURE_ERROR(true, "3.2.2", "braces should follow 1TBS style"), + WRONG_INDENTATION(true, "3.3.1", "only spaces are allowed for indentation and each indentation should equal to 4 spaces (tabs are not allowed)"), + EMPTY_BLOCK_STRUCTURE_ERROR(true, "3.4.1", "incorrect format of empty block"), + MORE_THAN_ONE_STATEMENT_PER_LINE(true, "3.6.1", "there should not be more than one code statement in one line"), + LONG_LINE(true, "3.5.1", "this line is longer than allowed"), + REDUNDANT_SEMICOLON(true, "3.6.2", "there should be no redundant semicolon at the end of lines"), + WRONG_NEWLINES(true, "3.6.2", "incorrect line breaking"), // FixMe: autofixing will be added for this rule - STRING_CONCATENATION(false, "strings should not be concatenated using plus operator - use string templates instead if the statement fits one line"), - TOO_MANY_BLANK_LINES(true, "too many consecutive blank lines"), - WRONG_WHITESPACE(true, "incorrect usage of whitespaces for code separation"), - TOO_MANY_CONSECUTIVE_SPACES(true, "too many consecutive spaces"), - ANNOTATION_NEW_LINE(true, "annotations must be on new line"), - ENUMS_SEPARATED(true, "split enumeration error"), - WHEN_WITHOUT_ELSE(true, "each when statement must have else at the end"), - LONG_NUMERICAL_VALUES_SEPARATED(true, "long numerical values should be separated with underscore"), - WRONG_DECLARATIONS_ORDER(true, "declarations of constants and enum members should be sorted alphabetically"), - WRONG_MULTIPLE_MODIFIERS_ORDER(true, "sequence of modifiers is incorrect"), - LOCAL_VARIABLE_EARLY_DECLARATION(false, "local variables should be declared close to the line where they are first used"), + STRING_CONCATENATION(false, "3.15.1", "strings should not be concatenated using plus operator - use string templates instead if the statement fits one line"), + TOO_MANY_BLANK_LINES(true, "3.7.1", "too many consecutive blank lines"), + WRONG_WHITESPACE(true, "3.8.1", "incorrect usage of whitespaces for code separation"), + TOO_MANY_CONSECUTIVE_SPACES(true, "3.8.1", "too many consecutive spaces"), + ANNOTATION_NEW_LINE(true, "3.12.1", "annotations must be on new line"), + ENUMS_SEPARATED(true, "3.9.1", "enum is incorrectly formatted"), + WHEN_WITHOUT_ELSE(true, "3.11.1", "each 'when' statement must have else at the end"), + LONG_NUMERICAL_VALUES_SEPARATED(true, "3.14.2", "long numerical values should be separated with underscore"), + WRONG_DECLARATIONS_ORDER(true, "3.1.4", "declarations of constants and enum members should be sorted alphabetically"), + WRONG_MULTIPLE_MODIFIERS_ORDER(true, "3.14.1", "sequence of modifier-keywords is incorrect"), + LOCAL_VARIABLE_EARLY_DECLARATION(false, "3.10.2", "local variables should be declared close to the line where they are first used"), + STRING_TEMPLATE_CURLY_BRACES(true, "3.15.2", "string template has redundant curly braces"), + STRING_TEMPLATE_QUOTES(true, "3.15.2", "string template has redundant quotes"), + FILE_NAME_MATCH_CLASS(true, "3.1.2", "file name is incorrect - it should match with the class described in it if there is the only one class declared"), // ======== chapter 4 ======== - NULLABLE_PROPERTY_TYPE(true, "try to avoid use of nullable types"), - TYPE_ALIAS(false, "variable's type is too complex and should be replaced with typealias"), - SMART_CAST_NEEDED(true, "You can omit explicit casting"), - SAY_NO_TO_VAR(false, "Usage of a mutable variables with [var] modifier - is a bad style, use [val] instead"), - GENERIC_VARIABLE_WRONG_DECLARATION(true, "variable should have explicit type declaration"), - STRING_TEMPLATE_CURLY_BRACES(true, "string template has redundant curly braces"), - STRING_TEMPLATE_QUOTES(true, "string template has redundant quotes"), + NULLABLE_PROPERTY_TYPE(true, "4.3.1", "try to avoid use of nullable types"), + TYPE_ALIAS(false, "4.2.2", "variable's type is too complex and should be replaced with typealias"), + SMART_CAST_NEEDED(true, "4.2.1", "you can omit explicit casting"), + SAY_NO_TO_VAR(false, "4.1.3", "Usage of a mutable variables with [var] modifier - is a bad style, use [val] instead"), + GENERIC_VARIABLE_WRONG_DECLARATION(true, "4.3.2", "variable should have explicit type declaration"), // FixMe: change float literal to BigDecimal? Or kotlin equivalent? - FLOAT_IN_ACCURATE_CALCULATIONS(false, "floating-point values shouldn't be used in accurate calculations"), - AVOID_NULL_CHECKS(false, "Try to avoid explicit null-checks. Use '.let/.also/?:/e.t.c' instead of"), - + FLOAT_IN_ACCURATE_CALCULATIONS(false, "4.1.1", "floating-point values shouldn't be used in accurate calculations"), + AVOID_NULL_CHECKS(false, "4.3.3", "Try to avoid explicit null-checks. Use '.let/.also/?:/e.t.c' instead of"), // ======== chapter 5 ======== - TOO_LONG_FUNCTION(false, "function is too long: split it or make more primitive"), - AVOID_NESTED_FUNCTIONS(true, "try to avoid using nested functions"), - LAMBDA_IS_NOT_LAST_PARAMETER(false, "lambda inside function parameters should be in the end"), - TOO_MANY_PARAMETERS(false, "function has too many parameters"), - NESTED_BLOCK(false, "function has too many nested blocks and should be simplified"), - WRONG_OVERLOADING_FUNCTION_ARGUMENTS(false, "use default argument instead of function overloading"), + TOO_LONG_FUNCTION(false, "5.1.1", "function is too long: split it or make more primitive"), + AVOID_NESTED_FUNCTIONS(true, "5.1.3", "try to avoid using nested functions"), + LAMBDA_IS_NOT_LAST_PARAMETER(false, "5.2.1", "lambda inside function parameters should be in the end"), + TOO_MANY_PARAMETERS(false, "5.2.2", "function has too many parameters"), + NESTED_BLOCK(false, "5.1.2", "function has too many nested blocks and should be simplified"), + WRONG_OVERLOADING_FUNCTION_ARGUMENTS(false, "5.2.3", "use default argument instead of function overloading"), // ======== chapter 6 ======== - SINGLE_CONSTRUCTOR_SHOULD_BE_PRIMARY(true, "if a class has single constructor, it should be converted to a primary constructor"), - USE_DATA_CLASS(false, "this class can be converted to a data class"), - WRONG_NAME_OF_VARIABLE_INSIDE_ACCESSOR(false, "Use `field` keyword instead of property name inside property accessors"), - MULTIPLE_INIT_BLOCKS(true, "Avoid using multiple `init` blocks, this logic can be moved to constructors or properties declarations"), - CLASS_SHOULD_NOT_BE_ABSTRACT(true, "class should not be abstract, because it has no abstract functions"), - CUSTOM_GETTERS_SETTERS(false, "Custom getters and setters are not recommended, use class methods instead"), - COMPACT_OBJECT_INITIALIZATION(true, "class instance can be initialized in `apply` block"), - USELESS_SUPERTYPE(true,"unnecessary supertype specification"), - TRIVIAL_ACCESSORS_ARE_NOT_RECOMMENDED(true, "trivial property accessors are not recommended"), - EXTENSION_FUNCTION_SAME_SIGNATURE(false, "extension functions should not have same signature if their receiver classes are related"), + SINGLE_CONSTRUCTOR_SHOULD_BE_PRIMARY(true, "6.1.1", "if a class has single constructor, it should be converted to a primary constructor"), + USE_DATA_CLASS(false, "6.1.2", "this class can be converted to a data class"), + WRONG_NAME_OF_VARIABLE_INSIDE_ACCESSOR(false, "6.1.9", "use `field` keyword instead of property name inside property accessors"), + MULTIPLE_INIT_BLOCKS(true, "6.1.4", "avoid using multiple `init` blocks, this logic can be moved to constructors or properties declarations"), + CLASS_SHOULD_NOT_BE_ABSTRACT(true, "6.1.6", "class should not be abstract, because it has no abstract functions"), + CUSTOM_GETTERS_SETTERS(false, "6.1.8", "custom getters and setters are not recommended, use class methods instead"), + COMPACT_OBJECT_INITIALIZATION(true, "6.1.11", "class instance can be initialized in `apply` block"), + USELESS_SUPERTYPE(true, "6.1.5", "unnecessary supertype specification"), + TRIVIAL_ACCESSORS_ARE_NOT_RECOMMENDED(true, "6.1.10", "trivial property accessors are not recommended"), + EXTENSION_FUNCTION_SAME_SIGNATURE(false, "6.2.2", "extension functions should not have same signature if their receiver classes are related"), ; /** diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/WarningsTableGenerator.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/WarningsTableGenerator.kt new file mode 100644 index 0000000000..c20c83f731 --- /dev/null +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/WarningsTableGenerator.kt @@ -0,0 +1,32 @@ +package cqfn.diktat.ruleset.constants + +import org.cqfn.diktat.ruleset.constants.Warnings +import java.io.File + +const val DIKTAT_GUIDE: String = "guide/diktat-coding-convention.md#" + +@Suppress("MagicNumber") +fun main() { + val allWarnings = Warnings.values() + allWarnings.sortBy { warn -> + val numbers = warn.ruleId.split(".") + val chapter = numbers[0].toInt() + val subChapter = numbers[1].toInt() + val rule = numbers[2].toInt() + + // small hacky trick to compare rules like 1.1.13 properly (sorting using numbers instead of lexicographically) + chapter * 100000 + subChapter * 100 + rule + } + + val maxRuleIdLength = allWarnings.maxBy { it.ruleId.length }?.ruleId?.length ?: 0 + val maxRuleNameLength = allWarnings.maxBy { it.name.length }?.name?.length ?: 0 + val separator = "| ${"-".repeat(maxRuleNameLength)} | ${"-".repeat(maxRuleIdLength)} | --- |\n" + + val header = "| Diktat Rule | Code Style | Auto-fixed? |\n" + + val tableWithWarnings = allWarnings.map { warn -> + "| ${warn.name} | [${warn.ruleId}](${DIKTAT_GUIDE}r${warn.ruleId}) | ${if (warn.canBeAutoCorrected) "yes" else "no"} |" + }.joinToString("\n") + + File("info/rules-mapping.md").writeText("$header$separator$tableWithWarnings") +} diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/WarningsGenerationTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/WarningsGenerationTest.kt new file mode 100644 index 0000000000..fddc80fb86 --- /dev/null +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/WarningsGenerationTest.kt @@ -0,0 +1,13 @@ +package org.cqfn.diktat.ruleset.utils + +import org.cqfn.diktat.ruleset.constants.Warnings +import org.junit.jupiter.api.Test + +class WarningsGenerationTest { + @Test + fun `checking that warnings has all proper fields filled`() { + Warnings.values().forEach { warn -> + assert(warn.ruleId.split(".").size == 3) + } + } +} diff --git a/info/guide/guide-chapter-1.md b/info/guide/guide-chapter-1.md index 74f60c863d..24af29a974 100644 --- a/info/guide/guide-chapter-1.md +++ b/info/guide/guide-chapter-1.md @@ -66,6 +66,7 @@ Note that prefixing can also negatively affect the style, as well as the auto ge ### 1.2 Packages names ### Rule 1.2.1: Package names are in lower case and separated by dots. Code developed within your company should start with `your.company.domain`, and numbers are permitted in package names. +Each file should have a `package` directive. Package names are all written in lowercase, and consecutive words are simply concatenated together (no underscores). Package names should contain both the product and module names, as well as the department or team name to prevent conflicts with other teams. Numbers are not permitted. For example: `org.apache.commons.lang3`, `xxx.yyy.v2`. **Exceptions:** @@ -160,7 +161,7 @@ fun addKeyListener(Listener) ### 1.5 Constants ### Rule 1.5.1 Constant names should be in UPPER case, words separated by underscore -1. Constants are attributes created with the const keyword, or top-level/`val` local variables of an object that holds immutable data. In most cases, constants can be identified as a `const val` property from the `object`/`companion object`/file top level. These variables contain a fixed constant value that typically should never be changed by programmers. This includes basic types, strings, immutable types, and immutable collections of immutable types. If an object state can be changed, the value is not a constant. +1. Constants are attributes created with the const keyword, or top-level/`val` local variables of an object that holds immutable data. In most cases, constants can be identified as a `const val` property from the `object`/`companion object`/file top level. These variables contain a fixed constant value that typically should never be changed by programmers. This includes basic types, strings, immutable types, and immutable collections of immutable types. If an object state can be changed, the value is not a constant. 2. Constant names should contain only uppercase letters separated by underscores. They should have a val or const val modifier to explicitly make them final. In most cases, if you need to specify a constant value, then you need to create it with the "const val" modifier. Note that not all `val` variables are constants. diff --git a/info/guide/guide-chapter-2.md b/info/guide/guide-chapter-2.md index 1b3e5cdba4..07d55b63e9 100644 --- a/info/guide/guide-chapter-2.md +++ b/info/guide/guide-chapter-2.md @@ -33,8 +33,10 @@ When the entire KDoc block can be stored in one line (and there is no KDoc mark ### Rule 2.1.1: KDoc is used for each public, protected or internal code element At a minimum, KDoc should be used for every public, protected, or internal decorated class, interface, enumeration, method, and member field (property). Other code blocks can also have KDocs if needed. +Instead of using comments before properties in class - use `@property` tag in a KDoc of a class. +All properties of the primary constructor should be also documented in a KDoc with a `@property` tag. -Exceptions: +**Exceptions:** 1. For setters/getters of properties obvious comments are optional. (Note that simple `get/set` methods are generated by Kotlin under the hood). For example, getFoo can also be `return foo`. @@ -53,7 +55,7 @@ fun isEmptyList(list: List) = list.size == 0 3. You can skip KDocs for a method's override if the method is almost like the super class method. -### Rule 2.1.2: When the method has arguments, return value, can throw exceptions, etc., it must be described in the KDoc block: with @param, @return, @throws, etc. +### Rule 2.1.2: When the method has arguments, return value, can throw exceptions, etc., it must be described in the KDoc block: with @param, @return, @throws, etc. **Valid examples**: diff --git a/info/guide/guide-chapter-3.md b/info/guide/guide-chapter-3.md index 01f440d06a..a0913bf562 100644 --- a/info/guide/guide-chapter-3.md +++ b/info/guide/guide-chapter-3.md @@ -24,6 +24,8 @@ c) Import statements are alphabetically arranged, without using line breaks and d) **Recommendation**: One `.kt` source file should contain only one class declaration, and its name should match the filename +e) Avoid empty files that do not contain the code or contain only imports/comments/package name + ### Recommendation 3.1.3: Import statements should appear in the following order: Android, internal company imports, external imports, java core dependencies, and Kotlin standard library. Each group should be separated by a blank line. From top to bottom, the order is the following: @@ -636,7 +638,7 @@ val n1: Int; val n2: Int ### Recommendation 3.10.2: Variables should be declared close to the line where they are first used. To minimize their scope, local variables should be declared close to the point where they are first used. This will also increase readability of the code. Local variables are usually initialized during declaration or initialized immediately after. -The member fields of the class should be declared collectively (see [Rule 3.1.2](#s3.1.2) for details on class structure). +The member fields of the class should be declared collectively (see [Rule 3.1.2](#r3.1.2) for details on class structure). ### 3.11 When expression diff --git a/info/guide/guide-chapter-4.md b/info/guide/guide-chapter-4.md index 0a497bbe01..d8d54b1344 100644 --- a/info/guide/guide-chapter-4.md +++ b/info/guide/guide-chapter-4.md @@ -166,7 +166,7 @@ val a: Int = 0 Nevertheless, if you use Java libraries extensively, you will have to use nullable types and enrich your code with `!!` and `?` symbols. Avoid using nullable types for Kotlin stdlib (declared in [official documentation](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/)) avoid using nullable types. -Try to use initializers for empty collections. ), and try using initializers for empty collections. For example: If you want to initialize a list instead of using `null` use `emptyList()`. +Try to use initializers for empty collections), and try using initializers for empty collections. For example: If you want to initialize a list instead of using `null` use `emptyList()`. **Invalid example**: ```kotlin diff --git a/info/rules-mapping.md b/info/rules-mapping.md new file mode 100644 index 0000000000..7ed2128362 --- /dev/null +++ b/info/rules-mapping.md @@ -0,0 +1,104 @@ +| Diktat Rule | Code Style | Auto-fixed? | +| ----------------------------------------- | ------ | --- | +| VARIABLE_NAME_INCORRECT | [1.1.1](guide/diktat-coding-convention.md#r1.1.1) | no | +| VARIABLE_HAS_PREFIX | [1.1.1](guide/diktat-coding-convention.md#r1.1.1) | yes | +| IDENTIFIER_LENGTH | [1.1.1](guide/diktat-coding-convention.md#r1.1.1) | no | +| GENERIC_NAME | [1.1.1](guide/diktat-coding-convention.md#r1.1.1) | yes | +| BACKTICKS_PROHIBITED | [1.1.1](guide/diktat-coding-convention.md#r1.1.1) | no | +| FILE_NAME_INCORRECT | [1.1.1](guide/diktat-coding-convention.md#r1.1.1) | yes | +| EXCEPTION_SUFFIX | [1.1.1](guide/diktat-coding-convention.md#r1.1.1) | yes | +| CONFUSING_IDENTIFIER_NAMING | [1.1.1](guide/diktat-coding-convention.md#r1.1.1) | no | +| PACKAGE_NAME_MISSING | [1.2.1](guide/diktat-coding-convention.md#r1.2.1) | yes | +| PACKAGE_NAME_INCORRECT_CASE | [1.2.1](guide/diktat-coding-convention.md#r1.2.1) | yes | +| PACKAGE_NAME_INCORRECT_PREFIX | [1.2.1](guide/diktat-coding-convention.md#r1.2.1) | yes | +| PACKAGE_NAME_INCORRECT_SYMBOLS | [1.2.1](guide/diktat-coding-convention.md#r1.2.1) | no | +| PACKAGE_NAME_INCORRECT_PATH | [1.2.1](guide/diktat-coding-convention.md#r1.2.1) | yes | +| INCORRECT_PACKAGE_SEPARATOR | [1.2.1](guide/diktat-coding-convention.md#r1.2.1) | yes | +| CLASS_NAME_INCORRECT | [1.3.1](guide/diktat-coding-convention.md#r1.3.1) | yes | +| OBJECT_NAME_INCORRECT | [1.3.1](guide/diktat-coding-convention.md#r1.3.1) | yes | +| ENUM_VALUE | [1.3.1](guide/diktat-coding-convention.md#r1.3.1) | yes | +| FUNCTION_NAME_INCORRECT_CASE | [1.4.1](guide/diktat-coding-convention.md#r1.4.1) | yes | +| CONSTANT_UPPERCASE | [1.5.1](guide/diktat-coding-convention.md#r1.5.1) | yes | +| VARIABLE_NAME_INCORRECT_FORMAT | [1.6.1](guide/diktat-coding-convention.md#r1.6.1) | yes | +| FUNCTION_BOOLEAN_PREFIX | [1.6.2](guide/diktat-coding-convention.md#r1.6.2) | yes | +| MISSING_KDOC_TOP_LEVEL | [2.1.1](guide/diktat-coding-convention.md#r2.1.1) | no | +| MISSING_KDOC_CLASS_ELEMENTS | [2.1.1](guide/diktat-coding-convention.md#r2.1.1) | no | +| MISSING_KDOC_ON_FUNCTION | [2.1.1](guide/diktat-coding-convention.md#r2.1.1) | yes | +| KDOC_NO_CONSTRUCTOR_PROPERTY | [2.1.1](guide/diktat-coding-convention.md#r2.1.1) | yes | +| KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT | [2.1.1](guide/diktat-coding-convention.md#r2.1.1) | yes | +| KDOC_WITHOUT_PARAM_TAG | [2.1.2](guide/diktat-coding-convention.md#r2.1.2) | yes | +| KDOC_WITHOUT_RETURN_TAG | [2.1.2](guide/diktat-coding-convention.md#r2.1.2) | yes | +| KDOC_WITHOUT_THROWS_TAG | [2.1.2](guide/diktat-coding-convention.md#r2.1.2) | yes | +| KDOC_EMPTY_KDOC | [2.1.3](guide/diktat-coding-convention.md#r2.1.3) | no | +| KDOC_WRONG_SPACES_AFTER_TAG | [2.1.3](guide/diktat-coding-convention.md#r2.1.3) | yes | +| KDOC_WRONG_TAGS_ORDER | [2.1.3](guide/diktat-coding-convention.md#r2.1.3) | yes | +| KDOC_NEWLINES_BEFORE_BASIC_TAGS | [2.1.3](guide/diktat-coding-convention.md#r2.1.3) | yes | +| KDOC_NO_NEWLINES_BETWEEN_BASIC_TAGS | [2.1.3](guide/diktat-coding-convention.md#r2.1.3) | yes | +| KDOC_NO_NEWLINE_AFTER_SPECIAL_TAGS | [2.1.3](guide/diktat-coding-convention.md#r2.1.3) | yes | +| KDOC_NO_DEPRECATED_TAG | [2.1.3](guide/diktat-coding-convention.md#r2.1.3) | yes | +| KDOC_NO_EMPTY_TAGS | [2.2.1](guide/diktat-coding-convention.md#r2.2.1) | no | +| HEADER_WRONG_FORMAT | [2.2.1](guide/diktat-coding-convention.md#r2.2.1) | yes | +| HEADER_MISSING_OR_WRONG_COPYRIGHT | [2.2.1](guide/diktat-coding-convention.md#r2.2.1) | yes | +| WRONG_COPYRIGHT_YEAR | [2.2.1](guide/diktat-coding-convention.md#r2.2.1) | yes | +| HEADER_CONTAINS_DATE_OR_AUTHOR | [2.2.1](guide/diktat-coding-convention.md#r2.2.1) | no | +| HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE | [2.2.1](guide/diktat-coding-convention.md#r2.2.1) | no | +| HEADER_NOT_BEFORE_PACKAGE | [2.2.1](guide/diktat-coding-convention.md#r2.2.1) | yes | +| KDOC_TRIVIAL_KDOC_ON_FUNCTION | [2.3.1](guide/diktat-coding-convention.md#r2.3.1) | no | +| WRONG_NEWLINES_AROUND_KDOC | [2.4.1](guide/diktat-coding-convention.md#r2.4.1) | yes | +| FIRST_COMMENT_NO_SPACES | [2.4.1](guide/diktat-coding-convention.md#r2.4.1) | yes | +| COMMENT_WHITE_SPACE | [2.4.1](guide/diktat-coding-convention.md#r2.4.1) | yes | +| IF_ELSE_COMMENTS | [2.4.1](guide/diktat-coding-convention.md#r2.4.1) | yes | +| COMMENTED_OUT_CODE | [2.4.2](guide/diktat-coding-convention.md#r2.4.2) | no | +| FILE_IS_TOO_LONG | [3.1.1](guide/diktat-coding-convention.md#r3.1.1) | no | +| FILE_CONTAINS_ONLY_COMMENTS | [3.1.2](guide/diktat-coding-convention.md#r3.1.2) | no | +| FILE_INCORRECT_BLOCKS_ORDER | [3.1.2](guide/diktat-coding-convention.md#r3.1.2) | yes | +| FILE_NO_BLANK_LINE_BETWEEN_BLOCKS | [3.1.2](guide/diktat-coding-convention.md#r3.1.2) | yes | +| FILE_UNORDERED_IMPORTS | [3.1.2](guide/diktat-coding-convention.md#r3.1.2) | yes | +| FILE_WILDCARD_IMPORTS | [3.1.2](guide/diktat-coding-convention.md#r3.1.2) | no | +| FILE_NAME_MATCH_CLASS | [3.1.2](guide/diktat-coding-convention.md#r3.1.2) | yes | +| WRONG_ORDER_IN_CLASS_LIKE_STRUCTURES | [3.1.4](guide/diktat-coding-convention.md#r3.1.4) | yes | +| BLANK_LINE_BETWEEN_PROPERTIES | [3.1.4](guide/diktat-coding-convention.md#r3.1.4) | yes | +| WRONG_DECLARATIONS_ORDER | [3.1.4](guide/diktat-coding-convention.md#r3.1.4) | yes | +| NO_BRACES_IN_CONDITIONALS_AND_LOOPS | [3.2.1](guide/diktat-coding-convention.md#r3.2.1) | yes | +| BRACES_BLOCK_STRUCTURE_ERROR | [3.2.2](guide/diktat-coding-convention.md#r3.2.2) | yes | +| WRONG_INDENTATION | [3.3.1](guide/diktat-coding-convention.md#r3.3.1) | yes | +| EMPTY_BLOCK_STRUCTURE_ERROR | [3.4.1](guide/diktat-coding-convention.md#r3.4.1) | yes | +| LONG_LINE | [3.5.1](guide/diktat-coding-convention.md#r3.5.1) | yes | +| MORE_THAN_ONE_STATEMENT_PER_LINE | [3.6.1](guide/diktat-coding-convention.md#r3.6.1) | yes | +| REDUNDANT_SEMICOLON | [3.6.2](guide/diktat-coding-convention.md#r3.6.2) | yes | +| WRONG_NEWLINES | [3.6.2](guide/diktat-coding-convention.md#r3.6.2) | yes | +| TOO_MANY_BLANK_LINES | [3.7.1](guide/diktat-coding-convention.md#r3.7.1) | yes | +| WRONG_WHITESPACE | [3.8.1](guide/diktat-coding-convention.md#r3.8.1) | yes | +| TOO_MANY_CONSECUTIVE_SPACES | [3.8.1](guide/diktat-coding-convention.md#r3.8.1) | yes | +| ENUMS_SEPARATED | [3.9.1](guide/diktat-coding-convention.md#r3.9.1) | yes | +| LOCAL_VARIABLE_EARLY_DECLARATION | [3.10.2](guide/diktat-coding-convention.md#r3.10.2) | no | +| WHEN_WITHOUT_ELSE | [3.11.1](guide/diktat-coding-convention.md#r3.11.1) | yes | +| ANNOTATION_NEW_LINE | [3.12.1](guide/diktat-coding-convention.md#r3.12.1) | yes | +| WRONG_MULTIPLE_MODIFIERS_ORDER | [3.14.1](guide/diktat-coding-convention.md#r3.14.1) | yes | +| LONG_NUMERICAL_VALUES_SEPARATED | [3.14.2](guide/diktat-coding-convention.md#r3.14.2) | yes | +| STRING_CONCATENATION | [3.15.1](guide/diktat-coding-convention.md#r3.15.1) | no | +| STRING_TEMPLATE_CURLY_BRACES | [3.15.2](guide/diktat-coding-convention.md#r3.15.2) | yes | +| STRING_TEMPLATE_QUOTES | [3.15.2](guide/diktat-coding-convention.md#r3.15.2) | yes | +| FLOAT_IN_ACCURATE_CALCULATIONS | [4.1.1](guide/diktat-coding-convention.md#r4.1.1) | no | +| SAY_NO_TO_VAR | [4.1.3](guide/diktat-coding-convention.md#r4.1.3) | no | +| SMART_CAST_NEEDED | [4.2.1](guide/diktat-coding-convention.md#r4.2.1) | yes | +| TYPE_ALIAS | [4.2.2](guide/diktat-coding-convention.md#r4.2.2) | no | +| NULLABLE_PROPERTY_TYPE | [4.3.1](guide/diktat-coding-convention.md#r4.3.1) | yes | +| GENERIC_VARIABLE_WRONG_DECLARATION | [4.3.2](guide/diktat-coding-convention.md#r4.3.2) | yes | +| AVOID_NULL_CHECKS | [4.3.3](guide/diktat-coding-convention.md#r4.3.3) | no | +| TOO_LONG_FUNCTION | [5.1.1](guide/diktat-coding-convention.md#r5.1.1) | no | +| NESTED_BLOCK | [5.1.2](guide/diktat-coding-convention.md#r5.1.2) | no | +| AVOID_NESTED_FUNCTIONS | [5.1.3](guide/diktat-coding-convention.md#r5.1.3) | yes | +| LAMBDA_IS_NOT_LAST_PARAMETER | [5.2.1](guide/diktat-coding-convention.md#r5.2.1) | no | +| TOO_MANY_PARAMETERS | [5.2.2](guide/diktat-coding-convention.md#r5.2.2) | no | +| WRONG_OVERLOADING_FUNCTION_ARGUMENTS | [5.2.3](guide/diktat-coding-convention.md#r5.2.3) | no | +| SINGLE_CONSTRUCTOR_SHOULD_BE_PRIMARY | [6.1.1](guide/diktat-coding-convention.md#r6.1.1) | yes | +| USE_DATA_CLASS | [6.1.2](guide/diktat-coding-convention.md#r6.1.2) | no | +| MULTIPLE_INIT_BLOCKS | [6.1.4](guide/diktat-coding-convention.md#r6.1.4) | yes | +| USELESS_SUPERTYPE | [6.1.5](guide/diktat-coding-convention.md#r6.1.5) | yes | +| CLASS_SHOULD_NOT_BE_ABSTRACT | [6.1.6](guide/diktat-coding-convention.md#r6.1.6) | yes | +| CUSTOM_GETTERS_SETTERS | [6.1.8](guide/diktat-coding-convention.md#r6.1.8) | no | +| WRONG_NAME_OF_VARIABLE_INSIDE_ACCESSOR | [6.1.9](guide/diktat-coding-convention.md#r6.1.9) | no | +| TRIVIAL_ACCESSORS_ARE_NOT_RECOMMENDED | [6.1.10](guide/diktat-coding-convention.md#r6.1.10) | yes | +| COMPACT_OBJECT_INITIALIZATION | [6.1.11](guide/diktat-coding-convention.md#r6.1.11) | yes | +| EXTENSION_FUNCTION_SAME_SIGNATURE | [6.2.2](guide/diktat-coding-convention.md#r6.2.2) | no | \ No newline at end of file From 4d17fe057e8160218185de3535898cd66a06500d Mon Sep 17 00:00:00 2001 From: Peter Trifanov Date: Mon, 23 Nov 2020 11:43:12 +0300 Subject: [PATCH 4/4] Created tests for diktat-gradle-plugin (#561) ### What's done: * Added tests * Added jacoco config --- diktat-gradle-plugin/build.gradle.kts | 34 +++++++++++-- diktat-gradle-plugin/pom.xml | 23 ++++++++- .../plugin/gradle/DiktatGradlePluginTest.kt | 31 +++++++++++ .../plugin/gradle/DiktatJavaExecTaskTest.kt | 51 +++++++++++++++++++ .../cqfn/diktat/plugin/gradle/UtilsTest.kt | 24 +++++++++ 5 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePluginTest.kt create mode 100644 diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskTest.kt create mode 100644 diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/UtilsTest.kt diff --git a/diktat-gradle-plugin/build.gradle.kts b/diktat-gradle-plugin/build.gradle.kts index 9837d19807..e6f0258bd0 100644 --- a/diktat-gradle-plugin/build.gradle.kts +++ b/diktat-gradle-plugin/build.gradle.kts @@ -3,6 +3,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { `java-gradle-plugin` kotlin("jvm") version "1.4.10" + jacoco } repositories { @@ -14,8 +15,9 @@ repositories { jcenter() } -val ktlintVersion: String by project -val diktatVersion = project.version +val ktlintVersion = project.properties.getOrDefault("ktlintVersion", "0.39.0") as String +val diktatVersion = project.version.takeIf { it.toString() != Project.DEFAULT_VERSION } ?: "0.1.6-SNAPSHOT" +val junitVersion = project.properties.getOrDefault("junitVersion", "5.7.0") as String dependencies { implementation(kotlin("gradle-plugin-api")) @@ -23,11 +25,14 @@ dependencies { exclude("com.pinterest.ktlint", "ktlint-ruleset-standard") } implementation("com.pinterest.ktlint:ktlint-reporter-plain:$ktlintVersion") - implementation("org.cqfn.diktat:diktat-rules:$version") + implementation("org.cqfn.diktat:diktat-rules:$diktatVersion") + + testImplementation("org.junit.jupiter:junit-jupiter-api:$junitVersion") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junitVersion") } val generateVersionsFile by tasks.registering { - val versionsFile = File("$buildDir/generated/src/main/generated/Versions.kt") + val versionsFile = File("$buildDir/generated/src/generated/Versions.kt") outputs.file(versionsFile) @@ -43,14 +48,16 @@ val generateVersionsFile by tasks.registering { ) } } -sourceSets.main.get().java.srcDir("$buildDir/generated/src/main") +sourceSets.main.get().java.srcDir("$buildDir/generated/src") tasks.withType { kotlinOptions { // fixme: kotlin 1.3 is required for gradle <6.8 languageVersion = "1.3" apiVersion = "1.3" + jvmTarget = "1.8" } + dependsOn.add(generateVersionsFile) } @@ -66,3 +73,20 @@ gradlePlugin { java { withSourcesJar() } + +// === testing & code coverage, consistent with maven +tasks.withType { + useJUnitPlatform() + extensions.configure(JacocoTaskExtension::class) { + setDestinationFile(file("target/jacoco.exec")) + } +} + +tasks.jacocoTestReport { + dependsOn(tasks.test) + reports { + // xml report is used by codecov + xml.isEnabled = true + xml.destination = file("target/site/jacoco/jacoco.xml") + } +} diff --git a/diktat-gradle-plugin/pom.xml b/diktat-gradle-plugin/pom.xml index 2fb387649f..27ffb001f6 100644 --- a/diktat-gradle-plugin/pom.xml +++ b/diktat-gradle-plugin/pom.xml @@ -62,18 +62,39 @@ exec-maven-plugin false + + gradle-test + test + + ${gradle.executable} + + clean + jacocoTestReport + -Pgroup=${project.groupId} + -Pversion=${project.version} + -Pdescription=${project.description} + -PktlintVersion=${ktlint.version} + -PjunitVersion=${junit.version} + -S + + ${skip.gradle.build} + + + exec + + gradle prepare-package ${gradle.executable} - clean ${gradle.task} -Pgroup=${project.groupId} -Pversion=${project.version} -Pdescription=${project.description} -PktlintVersion=${ktlint.version} + -PjunitVersion=${junit.version} -S ${skip.gradle.build} diff --git a/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePluginTest.kt b/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePluginTest.kt new file mode 100644 index 0000000000..d489d99dec --- /dev/null +++ b/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePluginTest.kt @@ -0,0 +1,31 @@ +package org.cqfn.diktat.plugin.gradle + +import org.gradle.api.Project +import org.gradle.testfixtures.ProjectBuilder +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class DiktatGradlePluginTest { + private val projectBuilder = ProjectBuilder.builder() + private lateinit var project: Project + + @BeforeEach + fun setUp() { + project = projectBuilder.build() + project.pluginManager.apply(DiktatGradlePlugin::class.java) + } + + @Test + fun `check that tasks are registered`() { + Assertions.assertTrue(project.tasks.findByName("diktatCheck") != null) + Assertions.assertTrue(project.tasks.findByName("diktatFix") != null) + } + + @Test + fun `check default extension properties`() { + val diktatExtension = project.extensions.getByName("diktat") as DiktatExtension + Assertions.assertFalse(diktatExtension.debug) + Assertions.assertIterableEquals(project.fileTree("src").files, diktatExtension.inputs.files) + } +} diff --git a/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskTest.kt b/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskTest.kt new file mode 100644 index 0000000000..24f34cdc93 --- /dev/null +++ b/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskTest.kt @@ -0,0 +1,51 @@ +package org.cqfn.diktat.plugin.gradle + +import org.gradle.api.Project +import org.gradle.testfixtures.ProjectBuilder +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import java.io.File + +class DiktatJavaExecTaskTest { + private val projectBuilder = ProjectBuilder.builder() + private lateinit var project: Project + + @BeforeEach + fun setUp() { + project = projectBuilder.build() + } + + @Test + fun `check command line for various inputs`() { + val pwd = project.file(".") + assertCommandLineEquals( + listOf(null, "$pwd" + listOf("src", "**", "*.kt").joinToString(File.separator, prefix = File.separator)), + DiktatExtension().apply { + inputs = project.files("src/**/*.kt") + } + ) + } + + @Test + fun `check command line in debug mode`() { + val pwd = project.file(".") + assertCommandLineEquals( + listOf(null, "--debug", "$pwd${listOf("src", "**", "*.kt").joinToString(File.separator, prefix = File.separator)}"), + DiktatExtension().apply { + inputs = project.files("src/**/*.kt") + debug = true + } + ) + } + + private fun registerDiktatTask(extension: DiktatExtension) = project.tasks.register( + "test", DiktatJavaExecTaskBase::class.java, + "6.7", extension, project.configurations.create("diktat") + ) + + private fun assertCommandLineEquals(expected: List, extension: DiktatExtension) { + val task = registerDiktatTask(extension).get() + Assertions.assertIterableEquals(expected, task.commandLine) + } +} diff --git a/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/UtilsTest.kt b/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/UtilsTest.kt new file mode 100644 index 0000000000..5347b7e123 --- /dev/null +++ b/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/UtilsTest.kt @@ -0,0 +1,24 @@ +package org.cqfn.diktat.plugin.gradle + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class UtilsTest { + @Test + fun `test gradle version`() { + Assertions.assertEquals( + GradleVersion.fromString("6.6.1"), + GradleVersion(6, 6, 1, null) + ) + + Assertions.assertEquals( + GradleVersion.fromString("6.7"), + GradleVersion(6, 7, 0, null) + ) + + Assertions.assertEquals( + GradleVersion.fromString("6.7-rc-5"), + GradleVersion(6, 7, 0, "rc-5") + ) + } +}