Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rule 6.4.1: Avoid using utility classes/objects #508

Merged
merged 18 commits into from
Dec 6, 2020
Merged
4 changes: 4 additions & 0 deletions diktat-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -352,5 +352,9 @@
configuration: {}
# Checks if there are any trivial getters or setters
- name: TRIVIAL_ACCESSORS_ARE_NOT_RECOMMENDED
enabled: true
configuration: {}
# Checks if there is class/object that can be replace with extension function
- name: AVOID_USING_UTILITY_CLASS
enabled: true
configuration: {}
2 changes: 2 additions & 0 deletions diktat-rules/src/main/kotlin/generated/WarningNames.kt
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,6 @@ public object WarningNames {

public const val TRIVIAL_ACCESSORS_ARE_NOT_RECOMMENDED: String =
"TRIVIAL_ACCESSORS_ARE_NOT_RECOMMENDED"

public const val AVOID_USING_UTILITY_CLASS: String = "AVOID_USING_UTILITY_CLASS"
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ enum class Warnings(private val canBeAutoCorrected: Boolean, private val warn: S
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"),
TRIVIAL_ACCESSORS_ARE_NOT_RECOMMENDED(true, "trivial property accessors are not recommended")
TRIVIAL_ACCESSORS_ARE_NOT_RECOMMENDED(true, "trivial property accessors are not recommended"),
AVOID_USING_UTILITY_CLASS(false, "avoid using utility classes/objects, use extensions functions"),
;

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.cqfn.diktat.ruleset.rules

import com.pinterest.ktlint.core.Rule
import com.pinterest.ktlint.core.ast.ElementType.CLASS
import com.pinterest.ktlint.core.ast.ElementType.CLASS_BODY
import com.pinterest.ktlint.core.ast.ElementType.FUN
import com.pinterest.ktlint.core.ast.ElementType.IDENTIFIER
import com.pinterest.ktlint.core.ast.ElementType.LBRACE
import com.pinterest.ktlint.core.ast.ElementType.OBJECT_DECLARATION
import com.pinterest.ktlint.core.ast.ElementType.PRIMARY_CONSTRUCTOR
import com.pinterest.ktlint.core.ast.ElementType.RBRACE
import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.ruleset.constants.EmitType
import org.cqfn.diktat.ruleset.constants.Warnings.AVOID_USING_UTILITY_CLASS
import org.cqfn.diktat.ruleset.utils.hasChildOfType
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.psi.psiUtil.children

/**
* Rule 6.4.1 checks that class/object, with a word "util" in its name, has only functions.
*/
class AvoidUtilityClass(private val configRules: List<RulesConfig>) : Rule("avoid-utility-class") {

companion object {
private val UTILITY_CLASS_CHILDREN = listOf(LBRACE, WHITE_SPACE, FUN, RBRACE)
kentr0w marked this conversation as resolved.
Show resolved Hide resolved
}

private var isFixMode: Boolean = false
private lateinit var emitWarn: EmitType

override fun visit(node: ASTNode, autoCorrect: Boolean, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit) {
kentr0w marked this conversation as resolved.
Show resolved Hide resolved
emitWarn = emit
isFixMode = autoCorrect

if (node.elementType == OBJECT_DECLARATION || node.elementType == CLASS ) {
checkClass(node)
}
}

private fun checkClass(node: ASTNode) {
if (node.hasChildOfType(PRIMARY_CONSTRUCTOR)
|| !node.findChildByType(IDENTIFIER)!!.text.toLowerCase().contains("util")) return
node.findChildByType(CLASS_BODY)
?.children()
?.toList()
?.takeIf { childList -> childList.all { it.elementType in UTILITY_CLASS_CHILDREN } }
?.filter { it.elementType == FUN }
kentr0w marked this conversation as resolved.
Show resolved Hide resolved
?.ifEmpty { return }
?: return
AVOID_USING_UTILITY_CLASS.warn(configRules, emitWarn, isFixMode, node.findChildByType(IDENTIFIER)!!.text, node.startOffset, node)
kentr0w marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class DiktatRuleSetProvider(private val diktatConfigFile: String = "diktat-analy
::DataClassesRule,
::LocalVariablesRule,
::SmartCastRule,
::AvoidUtilityClass,
::PropertyAccessorFields,
::AbstractClassesRule,
::SingleInitRule,
Expand Down
4 changes: 4 additions & 0 deletions diktat-rules/src/main/resources/diktat-analysis-huawei.yml
Original file line number Diff line number Diff line change
Expand Up @@ -351,5 +351,9 @@
configuration: {}
# Checks if there are any trivial getters or setters
- name: TRIVIAL_ACCESSORS_ARE_NOT_RECOMMENDED
enabled: true
configuration: {}
# Checks if there is class/object that can be replace with extension function
- name: AVOID_USING_UTILITY_CLASS
enabled: true
configuration: {}
4 changes: 4 additions & 0 deletions diktat-rules/src/main/resources/diktat-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -353,5 +353,9 @@
configuration: {}
# Checks if there are any trivial getters or setters
- name: TRIVIAL_ACCESSORS_ARE_NOT_RECOMMENDED
enabled: true
configuration: {}
# Checks if there is class/object that can be replace with extension function
- name: AVOID_USING_UTILITY_CLASS
enabled: true
configuration: {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.cqfn.diktat.ruleset.chapter6

import com.pinterest.ktlint.core.LintError
import generated.WarningNames
import org.cqfn.diktat.ruleset.constants.Warnings.AVOID_USING_UTILITY_CLASS
import org.cqfn.diktat.ruleset.rules.AvoidUtilityClass
import org.cqfn.diktat.ruleset.rules.DIKTAT_RULE_SET_ID
import org.cqfn.diktat.util.LintTestBase
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test

class AvoidUtilityClassWarnTest: LintTestBase(::AvoidUtilityClass) {
private val ruleId = "$DIKTAT_RULE_SET_ID:avoid-utility-class"

@Test
@Tag(WarningNames.AVOID_USING_UTILITY_CLASS)
fun `simple test`() {
lintMethod(
"""
|object StringUtil {
| fun stringInfo(myString: String): Int {
| return myString.count{ "something".contains(it) }
| }
|}
|
|class A() {
| fun foo() { }
|}
|
|class StringUtils {
| fun goo(tex: String): Int {
| return myString.count{ "something".contains(it) }
| }
|}
|
|class StringUtil {
| val z = "hello"
| fun goo(tex: String): Int {
| return myString.count{ "something".contains(it) }
| }
|}
""".trimMargin(),
LintError(1,1, ruleId, "${AVOID_USING_UTILITY_CLASS.warnText()} StringUtil"),
LintError(11,1, ruleId, "${AVOID_USING_UTILITY_CLASS.warnText()} StringUtils")
)
}
}
3 changes: 2 additions & 1 deletion info/available-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,5 @@
| 6 | 6.1.4 | MULTIPLE_INIT_BLOCKS | Checks that classes have only one init block | yes | no | - |
| 6 | 6.1.6 | CLASS_SHOULD_NOT_BE_ABSTRACT | Checks: if abstract class has any abstract method. If not, warns that class should not be abstract<br>Fix: deletes abstract modifier | yes | - | - |
| 6 | 6.1.9 | WRONG_NAME_OF_VARIABLE_INSIDE_ACCESSOR | Check: used the name of a variable in the custom getter or setter | no | - |
| 6 | 6.1.10 | TRIVIAL_ACCESSORS_ARE_NOT_RECOMMENDED | Check: if there are any trivial getters or setters <br> Fix: Delete trivial getter or setter | yes | - | - |
| 6 | 6.1.10 | TRIVIAL_ACCESSORS_ARE_NOT_RECOMMENDED | Check: if there are any trivial getters or setters <br> Fix: Delete trivial getter or setter | yes | - | - |
| 6 | 6.4.1 | AVOID_USING_UTILITY_CLASS | Checks if there is class/object that can be replace with extension function | no | - | - |