diff --git a/diktat-analysis.yml b/diktat-analysis.yml index f2f7531a8a..fc8c6764ee 100644 --- a/diktat-analysis.yml +++ b/diktat-analysis.yml @@ -38,6 +38,9 @@ # Checks that function/method name is in lowerCamelCase - name: FUNCTION_NAME_INCORRECT_CASE enabled: true +# Checks that typealias name is in PascalCase +- name: TYPEALIAS_NAME_INCORRECT_CASE + enabled: true # Checks that generic name doesn't contain more than 1 letter (capital). It can be followed by numbers, example: T12, T - name: GENERIC_NAME enabled: true diff --git a/diktat-common/src/test/resources/test-rules-config.yml b/diktat-common/src/test/resources/test-rules-config.yml index be6c9f20ee..d2ae232fa9 100644 --- a/diktat-common/src/test/resources/test-rules-config.yml +++ b/diktat-common/src/test/resources/test-rules-config.yml @@ -20,6 +20,8 @@ enabled: true - name: FUNCTION_NAME_INCORRECT_CASE enabled: true +- name: TYPEALIAS_NAME_INCORRECT_CASE + enabled: true - name: GENERIC_NAME enabled: true - name: IDENTIFIER_LENGTH diff --git a/diktat-rules/src/main/kotlin/generated/WarningNames.kt b/diktat-rules/src/main/kotlin/generated/WarningNames.kt index 6ad881fcb7..762f6198b2 100644 --- a/diktat-rules/src/main/kotlin/generated/WarningNames.kt +++ b/diktat-rules/src/main/kotlin/generated/WarningNames.kt @@ -41,6 +41,8 @@ public object WarningNames { public const val FUNCTION_NAME_INCORRECT_CASE: String = "FUNCTION_NAME_INCORRECT_CASE" + public const val TYPEALIAS_NAME_INCORRECT_CASE: String = "TYPEALIAS_NAME_INCORRECT_CASE" + public const val FUNCTION_BOOLEAN_PREFIX: String = "FUNCTION_BOOLEAN_PREFIX" public const val FILE_NAME_INCORRECT: String = "FILE_NAME_INCORRECT" 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 40e95bc62b..b55ae05d2e 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 @@ -55,6 +55,7 @@ enum class Warnings( 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"), + TYPEALIAS_NAME_INCORRECT_CASE(true, "1.3.1", "typealias name should be in pascalCase"), 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"), diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter1/IdentifierNaming.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter1/IdentifierNaming.kt index 5cfe7f9740..65c54a71d9 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter1/IdentifierNaming.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter1/IdentifierNaming.kt @@ -14,6 +14,7 @@ import org.cqfn.diktat.ruleset.constants.Warnings.FUNCTION_NAME_INCORRECT_CASE import org.cqfn.diktat.ruleset.constants.Warnings.GENERIC_NAME import org.cqfn.diktat.ruleset.constants.Warnings.IDENTIFIER_LENGTH import org.cqfn.diktat.ruleset.constants.Warnings.OBJECT_NAME_INCORRECT +import org.cqfn.diktat.ruleset.constants.Warnings.TYPEALIAS_NAME_INCORRECT_CASE import org.cqfn.diktat.ruleset.constants.Warnings.VARIABLE_HAS_PREFIX import org.cqfn.diktat.ruleset.constants.Warnings.VARIABLE_NAME_INCORRECT import org.cqfn.diktat.ruleset.constants.Warnings.VARIABLE_NAME_INCORRECT_FORMAT @@ -85,7 +86,7 @@ class IdentifierNaming(configRules: List) : DiktatRule( listOf(BACKTICKS_PROHIBITED, VARIABLE_NAME_INCORRECT, VARIABLE_NAME_INCORRECT_FORMAT, CONSTANT_UPPERCASE, VARIABLE_HAS_PREFIX, CONFUSING_IDENTIFIER_NAMING, GENERIC_NAME, CLASS_NAME_INCORRECT, ENUM_VALUE, EXCEPTION_SUFFIX, FUNCTION_BOOLEAN_PREFIX, FUNCTION_NAME_INCORRECT_CASE, - IDENTIFIER_LENGTH, OBJECT_NAME_INCORRECT)) { + IDENTIFIER_LENGTH, OBJECT_NAME_INCORRECT, TYPEALIAS_NAME_INCORRECT_CASE)) { private val allMethodPrefixes by lazy { if (configuration.allowedBooleanPrefixes.isEmpty()) { booleanMethodPrefixes @@ -119,6 +120,8 @@ class IdentifierNaming(configRules: List) : DiktatRule( ElementType.ENUM_ENTRY -> Pair(checkEnumValues(node), false) // covers global functions, extensions and class methods ElementType.FUN -> Pair(checkFunctionName(node), false) + // covers case of typeAlias values + ElementType.TYPEALIAS -> Pair(checkTypeAliases(node), false) else -> Pair(null, false) } @@ -385,6 +388,18 @@ class IdentifierNaming(configRules: List) : DiktatRule( return listOf(functionName) } + @Suppress("UnsafeCallOnNullableType") + private fun checkTypeAliases(node: ASTNode): List { + val aliasName = node.getIdentifierName()!! + + if (!aliasName.text.isPascalCase()) { + TYPEALIAS_NAME_INCORRECT_CASE.warnAndFix(configRules, emitWarn, isFixMode, aliasName.text, aliasName.startOffset, aliasName) { + (aliasName as LeafPsiElement).replaceWithText(aliasName.text.toPascalCase()) + } + } + return listOf(aliasName) + } + /** * check that generic name has single capital letter, can be followed by a number * this method will check it for both generic classes and generic methods diff --git a/diktat-rules/src/main/resources/diktat-analysis-huawei.yml b/diktat-rules/src/main/resources/diktat-analysis-huawei.yml index 33cc02e6aa..e49788b609 100644 --- a/diktat-rules/src/main/resources/diktat-analysis-huawei.yml +++ b/diktat-rules/src/main/resources/diktat-analysis-huawei.yml @@ -38,6 +38,9 @@ # Checks that function/method name is in lowerCamelCase - name: FUNCTION_NAME_INCORRECT_CASE enabled: true +# Checks that typealias name is in PascalCase +- name: TYPEALIAS_NAME_INCORRECT_CASE + enabled: true # Checks that generic name doesn't contain more than 1 letter (capital). It can be followed by numbers, example: T12, T - name: GENERIC_NAME enabled: true diff --git a/diktat-rules/src/main/resources/diktat-analysis.yml b/diktat-rules/src/main/resources/diktat-analysis.yml index ea78879e62..3f953a53ed 100644 --- a/diktat-rules/src/main/resources/diktat-analysis.yml +++ b/diktat-rules/src/main/resources/diktat-analysis.yml @@ -36,6 +36,9 @@ # Checks that function/method name is in lowerCamelCase - name: FUNCTION_NAME_INCORRECT_CASE enabled: true +# Checks that typealias name is in PascalCase +- name: TYPEALIAS_NAME_INCORRECT_CASE + enabled: true # Checks that generic name doesn't contain more than 1 letter (capital). It can be followed by numbers, example: T12, T - name: GENERIC_NAME enabled: true diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter1/IdentifierNamingFixTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter1/IdentifierNamingFixTest.kt index e9377e4973..5769ee761a 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter1/IdentifierNamingFixTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter1/IdentifierNamingFixTest.kt @@ -57,4 +57,10 @@ class IdentifierNamingFixTest : FixTestBase( fun `incorrect lambda argument case fix`() { fixAndCompare("identifiers/LambdaArgExpected.kt", "identifiers/LambdaArgTest.kt") } + + @Test + @Tag(WarningNames.TYPEALIAS_NAME_INCORRECT_CASE) + fun `typeAlias name incorrect`() { + fixAndCompare("identifiers/TypeAliasNameExpected.kt", "identifiers/TypeAliasNameTest.kt") + } } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter1/MethodNamingWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter1/MethodNamingWarnTest.kt index f72978e59d..09ddd208e3 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter1/MethodNamingWarnTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter1/MethodNamingWarnTest.kt @@ -2,6 +2,7 @@ package org.cqfn.diktat.ruleset.chapter1 import org.cqfn.diktat.ruleset.constants.Warnings.FUNCTION_BOOLEAN_PREFIX import org.cqfn.diktat.ruleset.constants.Warnings.FUNCTION_NAME_INCORRECT_CASE +import org.cqfn.diktat.ruleset.constants.Warnings.TYPEALIAS_NAME_INCORRECT_CASE import org.cqfn.diktat.ruleset.rules.DIKTAT_RULE_SET_ID import org.cqfn.diktat.ruleset.rules.chapter1.IdentifierNaming import org.cqfn.diktat.util.LintTestBase @@ -85,6 +86,30 @@ class MethodNamingWarnTest : LintTestBase(::IdentifierNaming) { lintMethod(code, LintError(2, 7, ruleId, "${FUNCTION_NAME_INCORRECT_CASE.warnText()} methODTREE", true)) } + @Test + @Tag(WarningNames.TYPEALIAS_NAME_INCORRECT_CASE) + fun `typeAlias name incorrect, part 1`() { + val code = + """ + class TestPackageName { + typealias relatedClasses = List> + } + """.trimIndent() + lintMethod(code, LintError(2, 13, ruleId, "${TYPEALIAS_NAME_INCORRECT_CASE.warnText()} relatedClasses", true)) + } + + @Test + @Tag(WarningNames.TYPEALIAS_NAME_INCORRECT_CASE) + fun `typeAlias name incorrect, part 2`() { + lintMethod( + """ + class TestPackageName { + typealias RelatedClasses = List> + } + """.trimIndent() + ) + } + @Test @Tag(WarningNames.FUNCTION_BOOLEAN_PREFIX) fun `boolean method name incorrect`() { diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTest.kt index 5e852122e6..0eea44680a 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTest.kt @@ -31,7 +31,7 @@ import kotlin.io.path.ExperimentalPathApi import kotlin.io.path.createTempFile import kotlinx.serialization.encodeToString -typealias ruleToConfig = Map> +typealias RuleToConfig = Map> /** * Test for [DiktatRuleSetProvider] in autocorrect mode as a whole. All rules are applied to a file. @@ -48,7 +48,7 @@ class DiktatSmokeTest : FixTestBase("test/smoke/src/main/kotlin", * Disable some of the rules. */ @Suppress("UnsafeCallOnNullableType") - private fun overrideRulesConfig(rulesToDisable: List, rulesToOverride: ruleToConfig = emptyMap()) { + private fun overrideRulesConfig(rulesToDisable: List, rulesToOverride: RuleToConfig = emptyMap()) { val rulesConfig = RulesConfigReader(javaClass.classLoader).readResource(configFilePath)!! .toMutableList() .also { rulesConfig -> diff --git a/diktat-rules/src/test/resources/test/paragraph1/naming/identifiers/TypeAliasNameExpected.kt b/diktat-rules/src/test/resources/test/paragraph1/naming/identifiers/TypeAliasNameExpected.kt new file mode 100644 index 0000000000..899175ce40 --- /dev/null +++ b/diktat-rules/src/test/resources/test/paragraph1/naming/identifiers/TypeAliasNameExpected.kt @@ -0,0 +1,5 @@ +package test.paragraph1.naming.identifiers + +class TypeAliasName { + typealias RelatedClasses = List> +} \ No newline at end of file diff --git a/diktat-rules/src/test/resources/test/paragraph1/naming/identifiers/TypeAliasNameTest.kt b/diktat-rules/src/test/resources/test/paragraph1/naming/identifiers/TypeAliasNameTest.kt new file mode 100644 index 0000000000..f27211bd14 --- /dev/null +++ b/diktat-rules/src/test/resources/test/paragraph1/naming/identifiers/TypeAliasNameTest.kt @@ -0,0 +1,5 @@ +package test.paragraph1.naming.identifiers + +class TypeAliasName { + typealias relatedClasses = List> +} \ No newline at end of file diff --git a/info/available-rules.md b/info/available-rules.md index e85a0be855..a218bcadf5 100644 --- a/info/available-rules.md +++ b/info/available-rules.md @@ -18,6 +18,7 @@ | 1 | 1.3.1 | ENUM_VALUE | Check: warns if enum value is in non UPPER_SNAKE_CASE or non PascalCase depending on the config, UPPER_SNAKE_CASE by default
Fix: automatically converting case to selected case | yes | enumStyle: snakeCase, pascalCase | Recursively update usages of this identifier in the project| | 1 | 1.1.1 | GENERIC_NAME | Check: warns if generic name contains more than 1 letter (capital). It can be followed by numbers, example: T12, T
Fix: | yes | no | Recursively update usages of this identifier in the project| | 1 | 1.4.1 | FUNCTION_NAME_INCORRECT_CASE | Check: function/method name should be in lowerCamelCase:
Fix: | yes | no | Recursively update usages of this function in the project| +| 1 | 1.3.1 | TYPEALIAS_NAME_INCORRECT_CASE | Check: typealias name should be in pascalCase:
Fix: | yes | no | Recursively update usages of this typealias in the project| | 1 | 1.6.2 | FUNCTION_BOOLEAN_PREFIX | Check: functions/methods that return boolean should have special prefix like "is/should/e.t.c"
Fix: | yes | no | Recursively update usages of this function in the projectAggressive fix - what if new name will not be valid| | 1 | 1.1.1 | FILE_NAME_INCORRECT | Check: warns if file name does not have extension .kt/.kts
Fix: no | no | no | Extensions should be configurableIt can be aggressively autofixed| | 1 | 3.1.2 | FILE_NAME_MATCH_CLASS | Check: warns
Fix: no | no | no | Probably it can be autofixed, but it will be aggressive fix| diff --git a/info/guide/diktat-coding-convention.md b/info/guide/diktat-coding-convention.md index d0208c183b..59434cd19c 100644 --- a/info/guide/diktat-coding-convention.md +++ b/info/guide/diktat-coding-convention.md @@ -273,9 +273,9 @@ package your.company.domain.mobilecontrol.views ``` -### 1.3 Classes, enumerations, interfaces +### 1.3 Classes, enumerations, typealias, interfaces This section describes the general rules for naming classes, enumerations, and interfaces. -### 1.3.1 Classes, enumerations, interface names use Camel case +### 1.3.1 Classes, enumerations, typealias, interface names use Camel case Classes, enumerations, and interface names use `UpperCamelCase` nomenclature. Follow the naming rules described below: 1. A class name is usually a noun (or a noun phrase) denoted using the camel case nomenclature, such as UpperCamelCase. For example: `Character` or `ImmutableList`. An interface name can also be a noun or noun phrase (such as `List`) or an adjective or adjective phrase (such as `Readable`). diff --git a/info/guide/guide-chapter-1.md b/info/guide/guide-chapter-1.md index df5ac11528..b8f6cc5262 100644 --- a/info/guide/guide-chapter-1.md +++ b/info/guide/guide-chapter-1.md @@ -86,9 +86,9 @@ package your.company.domain.mobilecontrol.views ``` -### 1.3 Classes, enumerations, interfaces +### 1.3 Classes, enumerations, typealias, interfaces This section describes the general rules for naming classes, enumerations, and interfaces. -### 1.3.1 Classes, enumerations, interface names use Camel case +### 1.3.1 Classes, enumerations, typealias, interface names use Camel case Classes, enumerations, and interface names use `UpperCamelCase` nomenclature. Follow the naming rules described below: 1. A class name is usually a noun (or a noun phrase) denoted using the camel case nomenclature, such as UpperCamelCase. For example: `Character` or `ImmutableList`. An interface name can also be a noun or noun phrase (such as `List`) or an adjective or adjective phrase (such as `Readable`). diff --git a/info/rules-mapping.md b/info/rules-mapping.md index 012f0fa8fa..7719e2ef68 100644 --- a/info/rules-mapping.md +++ b/info/rules-mapping.md @@ -17,6 +17,7 @@ | CLASS_NAME_INCORRECT | [1.3.1](guide/diktat-coding-convention.md#r1.3.1) | yes | Naming | | OBJECT_NAME_INCORRECT | [1.3.1](guide/diktat-coding-convention.md#r1.3.1) | yes | Naming | | ENUM_VALUE | [1.3.1](guide/diktat-coding-convention.md#r1.3.1) | yes | Naming | +| TYPEALIAS_NAME_INCORRECT_CASE | [1.3.1](guide/diktat-coding-convention.md#r1.3.1) | yes | Naming | | FUNCTION_NAME_INCORRECT_CASE | [1.4.1](guide/diktat-coding-convention.md#r1.4.1) | yes | Naming | | CONSTANT_UPPERCASE | [1.5.1](guide/diktat-coding-convention.md#r1.5.1) | yes | Naming | | VARIABLE_NAME_INCORRECT_FORMAT | [1.6.1](guide/diktat-coding-convention.md#r1.6.1) | yes | Naming |