From 4dce6b68bccf84bcfe694eda1d21de8aec3a7afa Mon Sep 17 00:00:00 2001 From: Pleshkova Daria Date: Tue, 14 Nov 2023 13:30:52 +0300 Subject: [PATCH 1/3] ### What's done: - fixed warning `VARIABLE_NAME_INCORRECT_FORMAT` on `backing field`. - added warning test. Closes #1711 --- .../rules/chapter1/IdentifierNaming.kt | 86 ++++++++++++------- .../chapter1/IdentifierNamingWarnTest.kt | 34 ++++++++ .../chapter6/CustomGetterSetterWarnTest.kt | 23 +++++ 3 files changed, 113 insertions(+), 30 deletions(-) diff --git a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter1/IdentifierNaming.kt b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter1/IdentifierNaming.kt index c74031d75d..65ca7f03e4 100644 --- a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter1/IdentifierNaming.kt +++ b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter1/IdentifierNaming.kt @@ -30,10 +30,15 @@ import org.jetbrains.kotlin.KtNodeTypes.CLASS import org.jetbrains.kotlin.KtNodeTypes.DESTRUCTURING_DECLARATION import org.jetbrains.kotlin.KtNodeTypes.DESTRUCTURING_DECLARATION_ENTRY import org.jetbrains.kotlin.KtNodeTypes.FUNCTION_TYPE +import org.jetbrains.kotlin.KtNodeTypes.MODIFIER_LIST +import org.jetbrains.kotlin.KtNodeTypes.NULLABLE_TYPE import org.jetbrains.kotlin.KtNodeTypes.OBJECT_DECLARATION +import org.jetbrains.kotlin.KtNodeTypes.PROPERTY +import org.jetbrains.kotlin.KtNodeTypes.PROPERTY_ACCESSOR import org.jetbrains.kotlin.KtNodeTypes.REFERENCE_EXPRESSION import org.jetbrains.kotlin.KtNodeTypes.TYPE_PARAMETER import org.jetbrains.kotlin.KtNodeTypes.TYPE_REFERENCE +import org.jetbrains.kotlin.KtNodeTypes.USER_TYPE import org.jetbrains.kotlin.KtNodeTypes.VALUE_PARAMETER_LIST import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement @@ -43,6 +48,7 @@ import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens.CATCH_KEYWORD import org.jetbrains.kotlin.lexer.KtTokens.IDENTIFIER +import org.jetbrains.kotlin.lexer.KtTokens.PRIVATE_KEYWORD import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.KtPrimaryConstructor import org.jetbrains.kotlin.psi.KtProperty @@ -149,9 +155,12 @@ class IdentifierNaming(configRules: List) : DiktatRule( "ComplexMethod", "UnsafeCallOnNullableType", ) + private fun checkVariableName(node: ASTNode): List { // special case for Destructuring declarations that can be treated as parameters in lambda: var namesOfVariables = extractVariableIdentifiers(node) + val propertyNodes = node.treeParent.getAllChildrenWithType(KtNodeTypes.PROPERTY) + // Only local private properties will be autofix in order not to break code if there are usages in other files. // Destructuring declarations are only allowed for local variables/values, so we don't need to calculate `isFix` for every node in `namesOfVariables` val isPublicOrNonLocalProperty = if (node.elementType == KtNodeTypes.PROPERTY) (node.psi as KtProperty).run { !isLocal && !isPrivate() } else false @@ -189,38 +198,55 @@ class IdentifierNaming(configRules: List) : DiktatRule( } } } else if (variableName.text != "_" && !variableName.text.isLowerCamelCase()) { + var isCorrectBackingField = false + if (variableName.text.commonPrefixWith("_").length == 1 && variableName.text.drop(1).isLowerCamelCase()) { + val matchingNode = propertyNodes.find { propertyNode -> + val nodeType = node.getFirstChildWithType(TYPE_REFERENCE) + val propertyType = propertyNode.getFirstChildWithType(TYPE_REFERENCE) + propertyNode.getFirstChildWithType(IDENTIFIER)?.text == variableName.text.drop(1) && + (propertyType?.text == nodeType?.text || + propertyType?.getFirstChildWithType(USER_TYPE)?.text == + nodeType?.getFirstChildWithType(NULLABLE_TYPE)?.getFirstChildWithType(USER_TYPE)?.text) && + node.getFirstChildWithType(MODIFIER_LIST)?.getFirstChildWithType(PRIVATE_KEYWORD) != null && + node.getFirstChildWithType(PROPERTY_ACCESSOR) == null && + propertyNode.getFirstChildWithType(PROPERTY_ACCESSOR) != null + } + matchingNode.let { isCorrectBackingField = true } + } // variable name should be in camel case. The only exception is a list of industry standard variables like i, j, k. - VARIABLE_NAME_INCORRECT_FORMAT.warnOnlyOrWarnAndFix( - configRules = configRules, - emit = emitWarn, - freeText = variableName.text, - offset = variableName.startOffset, - node = node, - shouldBeAutoCorrected = shouldBeAutoCorrected, - isFixMode = isFixMode, - ) { - // FixMe: cover fixes with tests - val correctVariableName = variableName.text.toDeterministic { toLowerCamelCase() } - variableName - .parent { it.elementType == KtFileElementType.INSTANCE } - ?.findAllVariablesWithUsages { it.name == variableName.text } - ?.flatMap { it.value.toList() } - ?.forEach { (it.node.firstChildNode as LeafPsiElement).rawReplaceWithText(correctVariableName) } - if (variableName.treeParent.psi.run { - (this is KtProperty && isMember) || - (this is KtParameter && getParentOfType(true)?.valueParameters?.contains(this) == true) - }) { - // For class members also check `@property` KDoc tag. - // If we are here, then `variableName` is definitely a node from a class or an object. - (variableName.parent(CLASS) ?: variableName.parent(OBJECT_DECLARATION))?.findChildByType(KDOC)?.kDocTags() - ?.firstOrNull { - it.knownTag == KDocKnownTag.PROPERTY && it.getSubjectName() == variableName.text - } - ?.run { - (getSubjectLink()!!.node.findAllDescendantsWithSpecificType(IDENTIFIER).single() as LeafPsiElement).rawReplaceWithText(correctVariableName) - } + if (!isCorrectBackingField) { + VARIABLE_NAME_INCORRECT_FORMAT.warnOnlyOrWarnAndFix( + configRules = configRules, + emit = emitWarn, + freeText = variableName.text, + offset = variableName.startOffset, + node = node, + shouldBeAutoCorrected = shouldBeAutoCorrected, + isFixMode = isFixMode, + ) { + // FixMe: cover fixes with tests + val correctVariableName = variableName.text.toDeterministic { toLowerCamelCase() } + variableName + .parent { it.elementType == KtFileElementType.INSTANCE } + ?.findAllVariablesWithUsages { it.name == variableName.text } + ?.flatMap { it.value.toList() } + ?.forEach { (it.node.firstChildNode as LeafPsiElement).rawReplaceWithText(correctVariableName) } + if (variableName.treeParent.psi.run { + (this is KtProperty && isMember) || + (this is KtParameter && getParentOfType(true)?.valueParameters?.contains(this) == true) + }) { + // For class members also check `@property` KDoc tag. + // If we are here, then `variableName` is definitely a node from a class or an object. + (variableName.parent(CLASS) ?: variableName.parent(OBJECT_DECLARATION))?.findChildByType(KDOC)?.kDocTags() + ?.firstOrNull { + it.knownTag == KDocKnownTag.PROPERTY && it.getSubjectName() == variableName.text + } + ?.run { + (getSubjectLink()!!.node.findAllDescendantsWithSpecificType(IDENTIFIER).single() as LeafPsiElement).rawReplaceWithText(correctVariableName) + } + } + (variableName as LeafPsiElement).rawReplaceWithText(correctVariableName) } - (variableName as LeafPsiElement).rawReplaceWithText(correctVariableName) } } } diff --git a/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter1/IdentifierNamingWarnTest.kt b/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter1/IdentifierNamingWarnTest.kt index 39352bd853..0b1ecff036 100644 --- a/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter1/IdentifierNamingWarnTest.kt +++ b/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter1/IdentifierNamingWarnTest.kt @@ -23,7 +23,14 @@ import com.saveourtool.diktat.ruleset.rules.chapter1.IdentifierNaming import com.saveourtool.diktat.util.LintTestBase import com.saveourtool.diktat.api.DiktatError +import com.saveourtool.diktat.ruleset.constants.Warnings +import com.saveourtool.diktat.ruleset.utils.getFirstChildWithType +import com.saveourtool.diktat.ruleset.utils.hasAnyChildOfTypes +import com.saveourtool.diktat.ruleset.utils.prettyPrint import generated.WarningNames +import org.jetbrains.kotlin.KtNodeTypes +import org.jetbrains.kotlin.com.intellij.lang.ASTNode +import org.jetbrains.kotlin.lexer.KtTokens import org.junit.jupiter.api.Tag import org.junit.jupiter.api.Tags import org.junit.jupiter.api.Test @@ -629,6 +636,33 @@ class IdentifierNamingWarnTest : LintTestBase(::IdentifierNaming) { lintMethod(code) } + @Test + @Tag(WarningNames.VARIABLE_NAME_INCORRECT_FORMAT) + fun `should not trigger on backing field`() { + //language=kotlin + lintMethod( + """ + |package com.example + | + |class MutableTableContainer { + | private var _table: Map? = null + | + | val table: Map + | get() { + | if (_table == null) { + | _table = hashMapOf() + | } + | return _table ?: throw AssertionError("Set to null by another thread") + | } + | set(value) { + | field = value + | } + | + |} + """.trimMargin(), + ) + } + @Test @Tag(WarningNames.CONFUSING_IDENTIFIER_NAMING) fun `confusing identifier naming`() { diff --git a/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter6/CustomGetterSetterWarnTest.kt b/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter6/CustomGetterSetterWarnTest.kt index 5a12249803..a91ea66ee8 100644 --- a/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter6/CustomGetterSetterWarnTest.kt +++ b/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter6/CustomGetterSetterWarnTest.kt @@ -102,4 +102,27 @@ class CustomGetterSetterWarnTest : LintTestBase(::CustomGetterSetterRule) { DiktatError(7, 9, ruleId, "${Warnings.CUSTOM_GETTERS_SETTERS.warnText()} get"), ) } + + @Test + @Tag(CUSTOM_GETTERS_SETTERS) + fun `exception for accessors or modifiers of property`() { + lintMethod( + """ + |package com.example + | + |class MutableTableContainer { + | private var _table: Map? = null + | + | val table: Map + | get() { + | if (_table == null) { + | _table = hashMapOf() + | } + | return _table ?: throw AssertionError("Set to null by another thread") + | } + |} + """.trimMargin(), + ) + } } + From 96aeac1eeaff5c2a753d8cf949b951891ca712ae Mon Sep 17 00:00:00 2001 From: Pleshkova Daria Date: Mon, 20 Nov 2023 17:01:09 +0300 Subject: [PATCH 2/3] Closes #1711 --- .../rules/chapter1/IdentifierNaming.kt | 111 ++++++++++-------- .../chapter6/CustomGetterSetterWarnTest.kt | 22 ---- 2 files changed, 61 insertions(+), 72 deletions(-) diff --git a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter1/IdentifierNaming.kt b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter1/IdentifierNaming.kt index 65ca7f03e4..54fc7a0a19 100644 --- a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter1/IdentifierNaming.kt +++ b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter1/IdentifierNaming.kt @@ -145,6 +145,34 @@ class IdentifierNaming(configRules: List) : DiktatRule( return false } + /** + * method checks that identifier is correct backing field + */ + private fun ASTNode.isCorrectBackingField(variableName: ASTNode): Boolean { + val propertyNodes = this.treeParent.getAllChildrenWithType(KtNodeTypes.PROPERTY) + // check that backing field name is correct + if (variableName.text.commonPrefixWith("_").length == 1 && variableName.text.drop(1).isLowerCamelCase()) { + val matchingNode = propertyNodes.find { propertyNode -> + val nodeType = this.getFirstChildWithType(TYPE_REFERENCE) + val propertyType = propertyNode.getFirstChildWithType(TYPE_REFERENCE) + // check that property and backing field has same type + val sameType = propertyType?.text == nodeType?.text + // check that property USER_TYPE is same as backing field NULLABLE_TYPE + val nodeNullableType = nodeType?.getFirstChildWithType(NULLABLE_TYPE) + val sameTypeWithNullable = propertyType?.getFirstChildWithType(USER_TYPE)?.text == + nodeNullableType?.getFirstChildWithType(USER_TYPE)?.text + val matchingNames = propertyNode.getFirstChildWithType(IDENTIFIER)?.text == variableName.text.drop(1) + val isPrivate = this.getFirstChildWithType(MODIFIER_LIST)?.getFirstChildWithType(PRIVATE_KEYWORD) != null + + matchingNames && (sameType || sameTypeWithNullable) && isPrivate && + this.getFirstChildWithType(PROPERTY_ACCESSOR) == null && + propertyNode.getFirstChildWithType(PROPERTY_ACCESSOR) != null + } + return matchingNode.let { true } + } + return false + } + /** * all checks for case and naming for vals/vars/constants */ @@ -159,7 +187,6 @@ class IdentifierNaming(configRules: List) : DiktatRule( private fun checkVariableName(node: ASTNode): List { // special case for Destructuring declarations that can be treated as parameters in lambda: var namesOfVariables = extractVariableIdentifiers(node) - val propertyNodes = node.treeParent.getAllChildrenWithType(KtNodeTypes.PROPERTY) // Only local private properties will be autofix in order not to break code if there are usages in other files. // Destructuring declarations are only allowed for local variables/values, so we don't need to calculate `isFix` for every node in `namesOfVariables` @@ -197,56 +224,40 @@ class IdentifierNaming(configRules: List) : DiktatRule( (variableName as LeafPsiElement).rawReplaceWithText(variableName.text.toDeterministic { toUpperSnakeCase() }) } } - } else if (variableName.text != "_" && !variableName.text.isLowerCamelCase()) { - var isCorrectBackingField = false - if (variableName.text.commonPrefixWith("_").length == 1 && variableName.text.drop(1).isLowerCamelCase()) { - val matchingNode = propertyNodes.find { propertyNode -> - val nodeType = node.getFirstChildWithType(TYPE_REFERENCE) - val propertyType = propertyNode.getFirstChildWithType(TYPE_REFERENCE) - propertyNode.getFirstChildWithType(IDENTIFIER)?.text == variableName.text.drop(1) && - (propertyType?.text == nodeType?.text || - propertyType?.getFirstChildWithType(USER_TYPE)?.text == - nodeType?.getFirstChildWithType(NULLABLE_TYPE)?.getFirstChildWithType(USER_TYPE)?.text) && - node.getFirstChildWithType(MODIFIER_LIST)?.getFirstChildWithType(PRIVATE_KEYWORD) != null && - node.getFirstChildWithType(PROPERTY_ACCESSOR) == null && - propertyNode.getFirstChildWithType(PROPERTY_ACCESSOR) != null - } - matchingNode.let { isCorrectBackingField = true } - } - // variable name should be in camel case. The only exception is a list of industry standard variables like i, j, k. - if (!isCorrectBackingField) { - VARIABLE_NAME_INCORRECT_FORMAT.warnOnlyOrWarnAndFix( - configRules = configRules, - emit = emitWarn, - freeText = variableName.text, - offset = variableName.startOffset, - node = node, - shouldBeAutoCorrected = shouldBeAutoCorrected, - isFixMode = isFixMode, - ) { - // FixMe: cover fixes with tests - val correctVariableName = variableName.text.toDeterministic { toLowerCamelCase() } - variableName - .parent { it.elementType == KtFileElementType.INSTANCE } - ?.findAllVariablesWithUsages { it.name == variableName.text } - ?.flatMap { it.value.toList() } - ?.forEach { (it.node.firstChildNode as LeafPsiElement).rawReplaceWithText(correctVariableName) } - if (variableName.treeParent.psi.run { - (this is KtProperty && isMember) || - (this is KtParameter && getParentOfType(true)?.valueParameters?.contains(this) == true) - }) { - // For class members also check `@property` KDoc tag. - // If we are here, then `variableName` is definitely a node from a class or an object. - (variableName.parent(CLASS) ?: variableName.parent(OBJECT_DECLARATION))?.findChildByType(KDOC)?.kDocTags() - ?.firstOrNull { - it.knownTag == KDocKnownTag.PROPERTY && it.getSubjectName() == variableName.text - } - ?.run { - (getSubjectLink()!!.node.findAllDescendantsWithSpecificType(IDENTIFIER).single() as LeafPsiElement).rawReplaceWithText(correctVariableName) - } - } - (variableName as LeafPsiElement).rawReplaceWithText(correctVariableName) + } else if (variableName.text != "_" && !variableName.text.isLowerCamelCase() && + // variable name should be in camel case. The only exception is a list of industry standard variables like i, j, k. + !node.isCorrectBackingField(variableName)) { + VARIABLE_NAME_INCORRECT_FORMAT.warnOnlyOrWarnAndFix( + configRules = configRules, + emit = emitWarn, + freeText = variableName.text, + offset = variableName.startOffset, + node = node, + shouldBeAutoCorrected = shouldBeAutoCorrected, + isFixMode = isFixMode, + ) { + // FixMe: cover fixes with tests + val correctVariableName = variableName.text.toDeterministic { toLowerCamelCase() } + variableName + .parent { it.elementType == KtFileElementType.INSTANCE } + ?.findAllVariablesWithUsages { it.name == variableName.text } + ?.flatMap { it.value.toList() } + ?.forEach { (it.node.firstChildNode as LeafPsiElement).rawReplaceWithText(correctVariableName) } + if (variableName.treeParent.psi.run { + (this is KtProperty && isMember) || + (this is KtParameter && getParentOfType(true)?.valueParameters?.contains(this) == true) + }) { + // For class members also check `@property` KDoc tag. + // If we are here, then `variableName` is definitely a node from a class or an object. + (variableName.parent(CLASS) ?: variableName.parent(OBJECT_DECLARATION))?.findChildByType(KDOC)?.kDocTags() + ?.firstOrNull { + it.knownTag == KDocKnownTag.PROPERTY && it.getSubjectName() == variableName.text + } + ?.run { + (getSubjectLink()!!.node.findAllDescendantsWithSpecificType(IDENTIFIER).single() as LeafPsiElement).rawReplaceWithText(correctVariableName) + } } + (variableName as LeafPsiElement).rawReplaceWithText(correctVariableName) } } } diff --git a/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter6/CustomGetterSetterWarnTest.kt b/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter6/CustomGetterSetterWarnTest.kt index a91ea66ee8..20244f6ec9 100644 --- a/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter6/CustomGetterSetterWarnTest.kt +++ b/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter6/CustomGetterSetterWarnTest.kt @@ -102,27 +102,5 @@ class CustomGetterSetterWarnTest : LintTestBase(::CustomGetterSetterRule) { DiktatError(7, 9, ruleId, "${Warnings.CUSTOM_GETTERS_SETTERS.warnText()} get"), ) } - - @Test - @Tag(CUSTOM_GETTERS_SETTERS) - fun `exception for accessors or modifiers of property`() { - lintMethod( - """ - |package com.example - | - |class MutableTableContainer { - | private var _table: Map? = null - | - | val table: Map - | get() { - | if (_table == null) { - | _table = hashMapOf() - | } - | return _table ?: throw AssertionError("Set to null by another thread") - | } - |} - """.trimMargin(), - ) - } } From d84c98c272c3dd04fb83089c454036de1c660d5d Mon Sep 17 00:00:00 2001 From: Pleshkova Daria Date: Tue, 21 Nov 2023 15:04:31 +0300 Subject: [PATCH 3/3] ### What's done: - added `ASTNode.isCorrectBackingField(ASTNode)` method. - added warning tests, which trigger on incorrect `backing field` format. Closes #1711 --- .../rules/chapter1/IdentifierNaming.kt | 8 +- .../chapter1/IdentifierNamingWarnTest.kt | 88 ++++++++++++++++++- 2 files changed, 92 insertions(+), 4 deletions(-) diff --git a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter1/IdentifierNaming.kt b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter1/IdentifierNaming.kt index 54fc7a0a19..5dc57cd8dc 100644 --- a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter1/IdentifierNaming.kt +++ b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter1/IdentifierNaming.kt @@ -150,8 +150,10 @@ class IdentifierNaming(configRules: List) : DiktatRule( */ private fun ASTNode.isCorrectBackingField(variableName: ASTNode): Boolean { val propertyNodes = this.treeParent.getAllChildrenWithType(KtNodeTypes.PROPERTY) + val variableNameCut = variableName.text.drop(1) // check that backing field name is correct - if (variableName.text.commonPrefixWith("_").length == 1 && variableName.text.drop(1).isLowerCamelCase()) { + + if (variableName.text.startsWith("_") && variableNameCut.isLowerCamelCase()) { val matchingNode = propertyNodes.find { propertyNode -> val nodeType = this.getFirstChildWithType(TYPE_REFERENCE) val propertyType = propertyNode.getFirstChildWithType(TYPE_REFERENCE) @@ -161,14 +163,14 @@ class IdentifierNaming(configRules: List) : DiktatRule( val nodeNullableType = nodeType?.getFirstChildWithType(NULLABLE_TYPE) val sameTypeWithNullable = propertyType?.getFirstChildWithType(USER_TYPE)?.text == nodeNullableType?.getFirstChildWithType(USER_TYPE)?.text - val matchingNames = propertyNode.getFirstChildWithType(IDENTIFIER)?.text == variableName.text.drop(1) + val matchingNames = propertyNode.getFirstChildWithType(IDENTIFIER)?.text == variableNameCut val isPrivate = this.getFirstChildWithType(MODIFIER_LIST)?.getFirstChildWithType(PRIVATE_KEYWORD) != null matchingNames && (sameType || sameTypeWithNullable) && isPrivate && this.getFirstChildWithType(PROPERTY_ACCESSOR) == null && propertyNode.getFirstChildWithType(PROPERTY_ACCESSOR) != null } - return matchingNode.let { true } + return matchingNode?.let { true } ?: false } return false } diff --git a/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter1/IdentifierNamingWarnTest.kt b/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter1/IdentifierNamingWarnTest.kt index 0b1ecff036..180f0e64e8 100644 --- a/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter1/IdentifierNamingWarnTest.kt +++ b/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter1/IdentifierNamingWarnTest.kt @@ -639,7 +639,6 @@ class IdentifierNamingWarnTest : LintTestBase(::IdentifierNaming) { @Test @Tag(WarningNames.VARIABLE_NAME_INCORRECT_FORMAT) fun `should not trigger on backing field`() { - //language=kotlin lintMethod( """ |package com.example @@ -663,6 +662,93 @@ class IdentifierNamingWarnTest : LintTestBase(::IdentifierNaming) { ) } + @Test + @Tag(WarningNames.VARIABLE_NAME_INCORRECT_FORMAT) + fun `should trigger on backing field with setter`() { + val code = + """ + |package com.example + | + |class MutableTableContainer { + | private var _table: Map? = null + | set(value) { + | field = value + | } + | + | val table: Map + | get() { + | if (_table == null) { + | _table = hashMapOf() + | } + | return _table ?: throw AssertionError("Set to null by another thread") + | } + | set(value) { + | field = value + | } + | + |} + """.trimMargin() + lintMethod(code, + DiktatError(4, 16, ruleId, "${VARIABLE_NAME_INCORRECT_FORMAT.warnText()} _table", true), + ) + } + + @Test + @Tag(WarningNames.VARIABLE_NAME_INCORRECT_FORMAT) + fun `should trigger on backing field with no matching property`() { + val code = + """ + |package com.example + | + |class MutableTableContainer { + | private var __table: Map? = null + | + | val table: Map + | get() { + | if (_table == null) { + | _table = hashMapOf() + | } + | return _table ?: throw AssertionError("Set to null by another thread") + | } + | set(value) { + | field = value + | } + | + |} + """.trimMargin() + lintMethod(code, + DiktatError(4, 16, ruleId, "${VARIABLE_NAME_INCORRECT_FORMAT.warnText()} __table", true), + ) + } + + @Test + @Tag(WarningNames.VARIABLE_NAME_INCORRECT_FORMAT) + fun `should trigger on backing field with unmatched type`() { + val code = + """ + |package com.example + | + |class MutableTableContainer { + | private var _table: Map? = null + | + | val table: Map + | get() { + | if (_table == null) { + | _table = hashMapOf() + | } + | return _table ?: throw AssertionError("Set to null by another thread") + | } + | set(value) { + | field = value + | } + | + |} + """.trimMargin() + lintMethod(code, + DiktatError(4, 16, ruleId, "${VARIABLE_NAME_INCORRECT_FORMAT.warnText()} _table", true), + ) + } + @Test @Tag(WarningNames.CONFUSING_IDENTIFIER_NAMING) fun `confusing identifier naming`() {