diff --git a/diktat-analysis.yml b/diktat-analysis.yml index af33e6558d..6ec429c6d7 100644 --- a/diktat-analysis.yml +++ b/diktat-analysis.yml @@ -297,6 +297,28 @@ maxNumberLength: '5' # Maximum number of digits between separators maxBlockLength: '3' +# Checks magic number +- name: MAGIC_NUMBER + enabled: true + configuration: + # Ignore numbers + ignoreNumbers: "-1, 1, 0, 2" + # Is ignore override hashCode function + ignoreHashCodeFunction: "true" + # Is ignore property + ignorePropertyDeclaration: "false" + # Is ignore local variable + ignoreLocalVariableDeclaration: "false" + # Is ignore constant + ignoreConstantDeclaration: "true" + # Is ignore property in companion object + ignoreCompanionObjectPropertyDeclaration: "true" + # Is ignore numbers in enum + ignoreEnums: "false" + # Is ignore number in ranges + ignoreRanges: "false" + # Is ignore number in extension function + ignoreExtensionFunctions: "false" # Checks that order of enum values or constant property inside companion is correct - name: WRONG_DECLARATIONS_ORDER enabled: true 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 ca7e2829e8..8d3287aa42 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 @@ -23,12 +23,15 @@ class ConfigReaderTest { fun `testing kotlin version`() { val rulesConfigList: List? = RulesConfigReader(javaClass.classLoader) .readResource("src/test/resources/test-rules-config.yml") - val kotlinVersionForTest = KotlinVersion(1, 4, 21) requireNotNull(rulesConfigList) - assert(rulesConfigList.getCommonConfiguration().kotlinVersion == kotlinVersionForTest) + assert(rulesConfigList.getCommonConfiguration().kotlinVersion == kotlinVersion) assert(rulesConfigList.find { it.name == DIKTAT_COMMON } ?.configuration ?.get("kotlinVersion") - ?.kotlinVersion() == kotlinVersionForTest) + ?.kotlinVersion() == kotlinVersion) + } + + companion object { + private val kotlinVersion = KotlinVersion(1, 4, 21) } } diff --git a/diktat-rules/src/main/kotlin/generated/WarningNames.kt b/diktat-rules/src/main/kotlin/generated/WarningNames.kt index 7a5faa6fc2..5e7861546d 100644 --- a/diktat-rules/src/main/kotlin/generated/WarningNames.kt +++ b/diktat-rules/src/main/kotlin/generated/WarningNames.kt @@ -169,6 +169,8 @@ public object WarningNames { public const val LONG_NUMERICAL_VALUES_SEPARATED: String = "LONG_NUMERICAL_VALUES_SEPARATED" + public const val MAGIC_NUMBER: String = "MAGIC_NUMBER" + public const val WRONG_DECLARATIONS_ORDER: String = "WRONG_DECLARATIONS_ORDER" public const val WRONG_MULTIPLE_MODIFIERS_ORDER: String = "WRONG_MULTIPLE_MODIFIERS_ORDER" 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 4d3f53052e..52031ff781 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 @@ -123,6 +123,7 @@ enum class Warnings( 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"), + MAGIC_NUMBER(false, "3.14.3", "avoid using magic numbers, instead define constants with clear names describing what the magic number means"), 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"), diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt index bc108fe1e2..f97829a82d 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt @@ -23,6 +23,7 @@ import org.cqfn.diktat.ruleset.rules.chapter3.EmptyBlock import org.cqfn.diktat.ruleset.rules.chapter3.EnumsSeparated import org.cqfn.diktat.ruleset.rules.chapter3.LineLength import org.cqfn.diktat.ruleset.rules.chapter3.LongNumericalValuesSeparatedRule +import org.cqfn.diktat.ruleset.rules.chapter3.MagicNumberRule import org.cqfn.diktat.ruleset.rules.chapter3.MultipleModifiersSequence import org.cqfn.diktat.ruleset.rules.chapter3.NullableTypeRule import org.cqfn.diktat.ruleset.rules.chapter3.SingleLineStatementsRule @@ -193,6 +194,7 @@ class DiktatRuleSetProvider(private var diktatConfigFile: String = DIKTAT_ANALYS ::TypeAliasRule, ::OverloadingArgumentsFunction, ::FunctionLength, + ::MagicNumberRule, ::LambdaParameterOrder, ::FunctionArgumentsSize, ::BlankLinesRule, diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/LineLength.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/LineLength.kt index a259e7c3f4..f35eff56be 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/LineLength.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/LineLength.kt @@ -384,6 +384,7 @@ class LineLength(configRules: List) : DiktatRule( } /** + * * [RuleConfiguration] for maximum line length */ class LineLengthConfiguration(config: Map) : RuleConfiguration(config) { diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/MagicNumberRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/MagicNumberRule.kt new file mode 100644 index 0000000000..828016e2c2 --- /dev/null +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/MagicNumberRule.kt @@ -0,0 +1,107 @@ +package org.cqfn.diktat.ruleset.rules.chapter3 + +import org.cqfn.diktat.common.config.rules.RuleConfiguration +import org.cqfn.diktat.common.config.rules.RulesConfig +import org.cqfn.diktat.common.config.rules.getRuleConfig +import org.cqfn.diktat.ruleset.constants.Warnings.MAGIC_NUMBER +import org.cqfn.diktat.ruleset.rules.DiktatRule +import org.cqfn.diktat.ruleset.utils.* + +import com.pinterest.ktlint.core.ast.ElementType.BINARY_EXPRESSION +import com.pinterest.ktlint.core.ast.ElementType.ENUM_ENTRY +import com.pinterest.ktlint.core.ast.ElementType.FLOAT_CONSTANT +import com.pinterest.ktlint.core.ast.ElementType.FUN +import com.pinterest.ktlint.core.ast.ElementType.INTEGER_CONSTANT +import com.pinterest.ktlint.core.ast.ElementType.MINUS +import com.pinterest.ktlint.core.ast.ElementType.OPERATION_REFERENCE +import com.pinterest.ktlint.core.ast.ElementType.PROPERTY +import com.pinterest.ktlint.core.ast.ElementType.RANGE +import com.pinterest.ktlint.core.ast.parent +import org.jetbrains.kotlin.com.intellij.lang.ASTNode +import org.jetbrains.kotlin.psi.KtFunction +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.psiUtil.isExtensionDeclaration +import org.jetbrains.kotlin.psi.psiUtil.parents + +/** + * Rule for magic number + */ +class MagicNumberRule(configRules: List) : DiktatRule( + "magic-number", + configRules, + listOf(MAGIC_NUMBER) +) { + private val configuration by lazy { + MagicNumberConfiguration( + configRules.getRuleConfig(MAGIC_NUMBER)?.configuration ?: emptyMap() + ) + } + override fun logic(node: ASTNode) { + if (node.elementType == INTEGER_CONSTANT || node.elementType == FLOAT_CONSTANT) { + checkNumber(node, configuration) + } + } + + @Suppress("ComplexMethod") + private fun checkNumber(node: ASTNode, configuration: MagicNumberConfiguration) { + val nodeText = node.treePrev?.let { if (it.elementType == OPERATION_REFERENCE && it.hasChildOfType(MINUS)) "-${node.text}" else node.text } ?: node.text + val isIgnoreNumber = configuration.ignoreNumbers.contains(nodeText) + val isHashFunction = node.parent({ it.elementType == FUN && it.isHashFun() }) != null + val isPropertyDeclaration = node.parent({ it.elementType == PROPERTY && !it.isNodeFromCompanionObject() }) != null + val isLocalVariable = node.parent({ it.isVarProperty() && (it.psi as KtProperty).isLocal }) != null + val isConstant = node.parent({ it.elementType == PROPERTY && it.isConstant() }) != null + val isCompanionObjectProperty = node.parent({ it.elementType == PROPERTY && it.isNodeFromCompanionObject() }) != null + val isEnums = node.parent({ it.elementType == ENUM_ENTRY }) != null + val isRanges = node.treeParent.run { + this.elementType == BINARY_EXPRESSION && + this.findChildByType(OPERATION_REFERENCE)?.hasChildOfType(RANGE) ?: false + } + val isExtensionFunctions = node.parent({ it.elementType == FUN && (it.psi as KtFunction).isExtensionDeclaration() }) != null && + node.parents().find { it.elementType == PROPERTY } == null + val result = listOf(isHashFunction, isPropertyDeclaration, isLocalVariable, isConstant, + isCompanionObjectProperty, isEnums, isRanges, isExtensionFunctions).zip(mapConfiguration.map { configuration.getParameter(it.key) }) + if (result.any { it.first && it.first != it.second } && !isIgnoreNumber) { + MAGIC_NUMBER.warn(configRules, emitWarn, isFixMode, nodeText, node.startOffset, node) + } + } + + private fun ASTNode.isHashFun() = + (this.psi as KtFunction).run { + this.nameIdentifier?.text == "hashCode" && this.annotationEntries.map { it.text }.contains("@Override") + } + + /** + * [RuleConfiguration] for configuration + */ + class MagicNumberConfiguration(config: Map) : RuleConfiguration(config) { + /** + * List of ignored numbers + */ + val ignoreNumbers = config["ignoreNumbers"]?.split(",")?.map { it.trim() }?.filter { it.isNumber() } ?: ignoreNumbersList + + /** + * @param param parameter from config + * @return value of parameter + */ + @Suppress("UnsafeCallOnNullableType") + fun getParameter(param: String) = config[param]?.toBoolean() ?: mapConfiguration[param]!! + + /** + * Check if string is number + */ + private fun String.isNumber() = (this.toLongOrNull() ?: this.toFloatOrNull()) != null + } + + companion object { + val ignoreNumbersList = listOf("-1", "1", "0", "2") + val mapConfiguration = mapOf( + "ignoreHashCodeFunction" to true, + "ignorePropertyDeclaration" to false, + "ignoreLocalVariableDeclaration" to false, + "ignoreConstantDeclaration" to true, + "ignoreCompanionObjectPropertyDeclaration" to true, + "ignoreEnums" to false, + "ignoreRanges" to false, + "ignoreExtensionFunctions" to false) + } +} diff --git a/diktat-rules/src/main/resources/diktat-analysis-huawei.yml b/diktat-rules/src/main/resources/diktat-analysis-huawei.yml index 5c89bffb56..fd01f2544f 100644 --- a/diktat-rules/src/main/resources/diktat-analysis-huawei.yml +++ b/diktat-rules/src/main/resources/diktat-analysis-huawei.yml @@ -297,6 +297,28 @@ maxNumberLength: '5' # Maximum number of digits between separators maxBlockLength: '3' +# Checks magic number +- name: MAGIC_NUMBER + enabled: true + configuration: + # Ignore numbers + ignoreNumbers: "-1, 1, 0, 2" + # Is ignore override hashCode function + ignoreHashCodeFunction: "true" + # Is ignore property + ignorePropertyDeclaration: "false" + # Is ignore local variable + ignoreLocalVariableDeclaration: "false" + # Is ignore constant + ignoreConstantDeclaration: "true" + # Is ignore property in companion object + ignoreCompanionObjectPropertyDeclaration: "true" + # Is ignore numbers in enum + ignoreEnums: "false" + # Is ignore number in ranges + ignoreRanges: "false" + # Is ignore number in extension function + ignoreExtensionFunctions: "false" # Checks that order of enum values or constant property inside companion is correct - name: WRONG_DECLARATIONS_ORDER enabled: true diff --git a/diktat-rules/src/main/resources/diktat-analysis.yml b/diktat-rules/src/main/resources/diktat-analysis.yml index ad1ed749f5..1bfce0581c 100644 --- a/diktat-rules/src/main/resources/diktat-analysis.yml +++ b/diktat-rules/src/main/resources/diktat-analysis.yml @@ -293,6 +293,28 @@ maxNumberLength: '5' # Maximum number of digits between separators maxBlockLength: '3' +# Checks magic number +- name: MAGIC_NUMBER + enabled: true + configuration: + # Ignore numbers + ignoreNumbers: "-1, 1, 0, 2" + # Is ignore override hashCode function + ignoreHashCodeFunction: "true" + # Is ignore property + ignorePropertyDeclaration: "false" + # Is ignore local variable + ignoreLocalVariableDeclaration: "false" + # Is ignore constant + ignoreConstantDeclaration: "true" + # Is ignore property in companion object + ignoreCompanionObjectPropertyDeclaration: "true" + # Is ignore numbers in enum + ignoreEnums: "false" + # Is ignore number in ranges + ignoreRanges: "false" + # Is ignore number in extension function + ignoreExtensionFunctions: "false" # Checks that order of enum values or constant property inside companion is correct - name: WRONG_DECLARATIONS_ORDER enabled: true diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/FileSizeWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/FileSizeWarnTest.kt index 17b5ddadb1..c94418d858 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/FileSizeWarnTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/FileSizeWarnTest.kt @@ -118,11 +118,15 @@ class FileSizeWarnTest : LintTestBase(::FileSize) { lintMethod(file.readText(), fileName = file.absolutePath, rulesConfigList = rulesConfigListTwoIgnoreFolders) path = javaClass.classLoader.getResource("test/paragraph3/src/main/C/FileSizeC.kt") file = File(path!!.file) - val size = 10 + val size = SIZE lintMethod(file.readText(), LintError(1, 1, "$DIKTAT_RULE_SET_ID:file-size", "${Warnings.FILE_IS_TOO_LONG.warnText()} $size", false), fileName = file.absolutePath, rulesConfigList = rulesConfigListTwoIgnoreFolders) } + + companion object { + private const val SIZE = 10 + } } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/MagicNumberRuleWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/MagicNumberRuleWarnTest.kt new file mode 100644 index 0000000000..731a4a4250 --- /dev/null +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/MagicNumberRuleWarnTest.kt @@ -0,0 +1,128 @@ +package org.cqfn.diktat.ruleset.chapter3 + +import org.cqfn.diktat.common.config.rules.RulesConfig +import org.cqfn.diktat.ruleset.constants.Warnings.MAGIC_NUMBER +import org.cqfn.diktat.ruleset.rules.DIKTAT_RULE_SET_ID +import org.cqfn.diktat.ruleset.rules.chapter3.MagicNumberRule +import org.cqfn.diktat.util.LintTestBase + +import com.pinterest.ktlint.core.LintError +import generated.WarningNames +import org.junit.jupiter.api.Tag +import org.junit.jupiter.api.Test + +class MagicNumberRuleWarnTest : LintTestBase(::MagicNumberRule) { + private val ruleId = "$DIKTAT_RULE_SET_ID:magic-number" + private val rulesConfigMagicNumber: List = listOf( + RulesConfig( + MAGIC_NUMBER.name, true, + mapOf( + "ignoreHashCodeFunction" to "true", + "ignorePropertyDeclaration" to "true", + "ignoreLocalVariableDeclaration" to "true", + "ignoreConstantDeclaration" to "true", + "ignoreCompanionObjectPropertyDeclaration" to "true", + "ignoreEnums" to "true", + "ignoreRanges" to "true", + "ignoreExtensionFunctions" to "true")) + ) + private val rulesConfigIgnoreNumbersMagicNumber: List = listOf( + RulesConfig( + MAGIC_NUMBER.name, true, + mapOf( + "ignoreNumbers" to "50,-240, 128L, -3.5f")) + ) + + @Test + @Tag(WarningNames.MAGIC_NUMBER) + fun `simple check`() { + lintMethod( + """ + |fun f1oo() { + | val a: Byte = 4 + | val b = 128L + | val e = 3.4f + | val g = 4/3 + | val f = "qwe\$\{12\}hhe" + |} + | + |@Override + |fun hashCode(): Int { + | return 32 + |} + | + |class Cl{ + | companion object { + | private const val AA = 4 + | } + |} + """.trimMargin(), + LintError(2, 18, ruleId, "${MAGIC_NUMBER.warnText()} 4", false), + LintError(3, 12, ruleId, "${MAGIC_NUMBER.warnText()} 128L", false), + LintError(4, 12, ruleId, "${MAGIC_NUMBER.warnText()} 3.4f", false), + LintError(5, 12, ruleId, "${MAGIC_NUMBER.warnText()} 4", false), + LintError(5, 14, ruleId, "${MAGIC_NUMBER.warnText()} 3", false) + ) + } + + @Test + @Tag(WarningNames.MAGIC_NUMBER) + fun `check ignore numbers`() { + lintMethod( + """ + |fun f1oo() { + | val m = -1 + | val a: Byte = 4 + | val b = 0xff + |} + | + |enum class A(b:Int) { + | A(-240), + | B(50), + | C(3), + |} + |@Override + |fun hashCode(): Int { + | return 32 + |} + """.trimMargin(), + LintError(2, 13, ruleId, "${MAGIC_NUMBER.warnText()} -1", false), + LintError(3, 18, ruleId, "${MAGIC_NUMBER.warnText()} 4", false), + LintError(4, 12, ruleId, "${MAGIC_NUMBER.warnText()} 0xff", false), + LintError(10, 6, ruleId, "${MAGIC_NUMBER.warnText()} 3", false), + rulesConfigList = rulesConfigIgnoreNumbersMagicNumber + ) + } + + @Test + @Tag(WarningNames.MAGIC_NUMBER) + fun `check all param in config true`() { + lintMethod( + """ + |fun foo() { + | var a = 3 + |} + | + |const val aa = 21.5f + | + |class A { + | companion object { + | val b = 2 + | } + |} + | + |enum class A(b:Int) { + | A(1), + | B(2), + | C(3), + |} + | + |fun goo() { + | val q = 100.1000 + |} + | + |fun Int.foo() = 2 + """.trimMargin(), rulesConfigList = rulesConfigMagicNumber + ) + } +} diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/AvailableRulesDocTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/AvailableRulesDocTest.kt index 1d4896edce..6ac304775e 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/AvailableRulesDocTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/AvailableRulesDocTest.kt @@ -16,7 +16,7 @@ class AvailableRulesDocTest { val splitMarkDown = line .split("|") - val ruleName = splitMarkDown.get(3).trim() + val ruleName = splitMarkDown.get(SPLIT_MARK).trim() if (!ruleName.startsWith(TABLE_DELIMITER) && !ruleName.startsWith(RULE_NAME_HEADER)) { @@ -59,6 +59,7 @@ class AvailableRulesDocTest { companion object { const val AVAILABLE_RULES_FILE = "../info/available-rules.md" const val RULE_NAME_HEADER = "Rule name" + const val SPLIT_MARK = 3 const val TABLE_DELIMITER = "-----" } } diff --git a/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/processing/TestProcessingFactory.kt b/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/processing/TestProcessingFactory.kt index 21c5ee09fa..30740e676d 100644 --- a/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/processing/TestProcessingFactory.kt +++ b/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/processing/TestProcessingFactory.kt @@ -28,7 +28,7 @@ class TestProcessingFactory(private val argReader: TestArgumentsReader) { ?: run { log.error("Not able to get directory with test configuration files: " + argReader.properties.testConfigsRelativePath) - exitProcess(5) + exitProcess(STATUS_FIVE) } try { resource @@ -38,7 +38,7 @@ class TestProcessingFactory(private val argReader: TestArgumentsReader) { .toList() } catch (e: IOException) { log.error("Got -all option, but cannot read config files ", e) - exitProcess(3) + exitProcess(STATUS_THREE) } } @@ -91,5 +91,7 @@ class TestProcessingFactory(private val argReader: TestArgumentsReader) { companion object { private val log = LoggerFactory.getLogger(TestProcessingFactory::class.java) + private const val STATUS_FIVE = 5 + private const val STATUS_THREE = 3 } } diff --git a/info/available-rules.md b/info/available-rules.md index f3b7e78b38..f730c1b2d6 100644 --- a/info/available-rules.md +++ b/info/available-rules.md @@ -79,6 +79,7 @@ | 3 | 3.11.1 | WHEN_WITHOUT_ELSE | Check: warns if when statement don't have else in the end.
Fix: adds else if when doesn't have it. | yes | no | - | If a when statement of type enum or sealed contains all values of a enum - there is no need to have "else" branch. | | 3 | 3.10.2 | LOCAL_VARIABLE_EARLY_DECLARATION | Check: warns if local variable is declared not immediately before it's usage
Fix (not implemented yet): moves variable declaration | no | no | add auto fix | | 3 | 3.14.2 | LONG_NUMERICAL_VALUES_SEPARATED | Check: warns if value on integer or float constant is too big | no | maxNumberLength maxBlockLength | - | +| 3 | 3.14.3 | MAGIC_NUMBER | Check: warns if there is magic numbers in the code | no | ignoreNumbers, ignoreHashCodeFunction, ignorePropertyDeclaration, ignoreLocalVariableDeclaration, ignoreConstantDeclaration, ignoreCompanionObjectPropertyDeclaration, ignoreEnums, ignoreRanges, ignoreExtensionFunctions | no | 3 | 3.1.4 | WRONG_DECLARATIONS_ORDER | Check: if order of enum values or constant property inside companion isn't correct | yes | no | - | | 3 | 3.12.1 | ANNOTATION_NEW_LINE | Check: warns if annotation not on a new single line | yes | no | - | | 3 | 3.14.1 | WRONG_MULTIPLE_MODIFIERS_ORDER | Check: if multiple modifiers sequence is in the wrong order | yes | no | - | diff --git a/info/guide/guide-chapter-3.md b/info/guide/guide-chapter-3.md index 69d855e7c5..41455eca45 100644 --- a/info/guide/guide-chapter-3.md +++ b/info/guide/guide-chapter-3.md @@ -819,6 +819,24 @@ val socialSecurityNumber = 999_99_9999L val hexBytes = 0xFF_EC_DE_5E val bytes = 0b11010010_01101001_10010100_10010010 ``` +#### 3.14.3: Magic number +Prefer defining constants with clear names describing what the magic number means. +**Valid example**: +```kotlin +class Person() { + fun isAdult(age: Int): Boolean = age >= majority + + companion object { + private const val majority = 18 + } +} +``` +**Invalid example**: +```kotlin +class Person() { + fun isAdult(age: Int): Boolean = age >= 18 +} +``` ### 3.15 Strings