diff --git a/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/ClassNaming.kt b/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/ClassNaming.kt index ed3b209aa6e..122966a22fd 100644 --- a/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/ClassNaming.kt +++ b/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/ClassNaming.kt @@ -32,6 +32,9 @@ class ClassNaming(config: Config = Config.empty) : Rule(config) { private val classPattern: Regex by config("[A-Z][a-zA-Z0-9]*") { it.toRegex() } override fun visitClassOrObject(classOrObject: KtClassOrObject) { + if (classOrObject.nameAsSafeName.isSpecial || classOrObject.nameIdentifier?.parent?.javaClass == null) { + return + } if (!classOrObject.identifierName().removeSurrounding("`").matches(classPattern)) { report( CodeSmell( diff --git a/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/ConstructorParameterNaming.kt b/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/ConstructorParameterNaming.kt index 93e22644765..83fed496c18 100644 --- a/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/ConstructorParameterNaming.kt +++ b/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/ConstructorParameterNaming.kt @@ -13,6 +13,7 @@ import io.gitlab.arturbosch.detekt.api.internal.Configuration import io.gitlab.arturbosch.detekt.rules.identifierName import io.gitlab.arturbosch.detekt.rules.isOverride import io.gitlab.arturbosch.detekt.rules.naming.util.isContainingExcludedClassOrObject +import org.jetbrains.kotlin.psi.KtConstructor import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.psiUtil.isPrivate @@ -42,22 +43,16 @@ class ConstructorParameterNaming(config: Config = Config.empty) : Rule(config) { private val ignoreOverridden: Boolean by config(true) override fun visitParameter(parameter: KtParameter) { - if (parameter.isContainingExcludedClassOrObject(excludeClassPattern) || isIgnoreOverridden(parameter)) { + if (!parameter.isConstructor() || + parameter.isContainingExcludedClassOrObject(excludeClassPattern) || + isIgnoreOverridden(parameter) + ) { return } val identifier = parameter.identifierName() if (parameter.isPrivate()) { - if (!identifier.matches(privateParameterPattern)) { - report( - CodeSmell( - issue, - Entity.from(parameter), - message = "Constructor private parameter names should " + - "match the pattern: $privateParameterPattern" - ) - ) - } + visitPrivateParameter(parameter) } else { if (!identifier.matches(parameterPattern)) { report( @@ -71,5 +66,22 @@ class ConstructorParameterNaming(config: Config = Config.empty) : Rule(config) { } } + private fun visitPrivateParameter(parameter: KtParameter) { + val identifier = parameter.identifierName() + if (!identifier.matches(privateParameterPattern)) { + report( + CodeSmell( + issue, + Entity.from(parameter), + message = "Constructor private parameter names should match the pattern: $privateParameterPattern" + ) + ) + } + } + private fun isIgnoreOverridden(parameter: KtParameter) = ignoreOverridden && parameter.isOverride() + + private fun KtParameter.isConstructor(): Boolean { + return this.ownerFunction is KtConstructor<*> + } } diff --git a/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/FunctionParameterNaming.kt b/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/FunctionParameterNaming.kt index b4bce92d201..27c4e60e7c1 100644 --- a/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/FunctionParameterNaming.kt +++ b/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/FunctionParameterNaming.kt @@ -15,6 +15,7 @@ import io.gitlab.arturbosch.detekt.api.internal.Configuration import io.gitlab.arturbosch.detekt.rules.identifierName import io.gitlab.arturbosch.detekt.rules.isOverride import io.gitlab.arturbosch.detekt.rules.naming.util.isContainingExcludedClass +import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtParameter /** @@ -46,7 +47,7 @@ class FunctionParameterNaming(config: Config = Config.empty) : Rule(config) { private val ignoreOverridden: Boolean by configWithFallback(::ignoreOverriddenFunctions, true) override fun visitParameter(parameter: KtParameter) { - if (parameter.isContainingExcludedClass(excludeClassPattern)) { + if (parameter.isParameterInFunction()) { return } @@ -65,4 +66,11 @@ class FunctionParameterNaming(config: Config = Config.empty) : Rule(config) { ) } } + + private fun KtParameter.isParameterInFunction(): Boolean { + return this.nameAsSafeName.isSpecial || + (this.nameIdentifier?.parent?.javaClass == null) || + (this.ownerFunction !is KtNamedFunction) || + this.isContainingExcludedClass(excludeClassPattern) + } } diff --git a/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/NamingProvider.kt b/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/NamingProvider.kt index 65a4017f5c0..f6b6e242029 100644 --- a/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/NamingProvider.kt +++ b/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/NamingProvider.kt @@ -18,9 +18,25 @@ class NamingProvider : DefaultRuleSetProvider { listOf( MatchingDeclarationName(config), MemberNameEqualsClassName(config), - NamingRules(config), InvalidPackageDeclaration(config), - NoNameShadowing(config) + NoNameShadowing(config), + TopLevelPropertyNaming(config), + BooleanPropertyNaming(config), + LambdaParameterNaming(config), + ConstructorParameterNaming(config), + ForbiddenClassName(config), + ClassNaming(config), + PackageNaming(config), + EnumNaming(config), + ObjectPropertyNaming(config), + FunctionParameterNaming(config), + FunctionNaming(config), + FunctionMinLength(config), + FunctionMaxLength(config), + VariableMaxLength(config), + VariableMinLength(config), + VariableNaming(config), + NonBooleanPropertyPrefixedWithIs(config) ) ) } diff --git a/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/NamingRules.kt b/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/NamingRules.kt deleted file mode 100644 index cc675cb5260..00000000000 --- a/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/NamingRules.kt +++ /dev/null @@ -1,120 +0,0 @@ -package io.gitlab.arturbosch.detekt.rules.naming - -import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Rule -import org.jetbrains.kotlin.psi.KtClassOrObject -import org.jetbrains.kotlin.psi.KtConstructor -import org.jetbrains.kotlin.psi.KtEnumEntry -import org.jetbrains.kotlin.psi.KtLambdaExpression -import org.jetbrains.kotlin.psi.KtNamedDeclaration -import org.jetbrains.kotlin.psi.KtNamedFunction -import org.jetbrains.kotlin.psi.KtObjectDeclaration -import org.jetbrains.kotlin.psi.KtPackageDirective -import org.jetbrains.kotlin.psi.KtParameter -import org.jetbrains.kotlin.psi.KtProperty -import org.jetbrains.kotlin.psi.KtVariableDeclaration -import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType - -class NamingRules(config: Config = Config.empty) : - @Suppress("DEPRECATION") - io.gitlab.arturbosch.detekt.api.MultiRule() { - - private val variableNamingRule = VariableNaming(config) - private val variableMinNameLengthRule = VariableMinLength(config) - private val variableMaxNameLengthRule = VariableMaxLength(config) - private val topLevelPropertyRule = TopLevelPropertyNaming(config) - private val objectConstantNamingRule = ObjectPropertyNaming(config) - private val nonBooleanPropertyPrefixedWithIsRule = NonBooleanPropertyPrefixedWithIs(config) - private val packageNamingRule = PackageNaming(config) - private val classOrObjectNamingRule = ClassNaming(config) - private val enumEntryNamingRule = EnumNaming(config) - private val functionNamingRule = FunctionNaming(config) - private val functionMaxNameLengthRule = FunctionMaxLength(config) - private val functionMinNameLengthRule = FunctionMinLength(config) - private val forbiddenClassNameRule = ForbiddenClassName(config) - private val constructorParameterNamingRule = ConstructorParameterNaming(config) - private val functionParameterNamingRule = FunctionParameterNaming(config) - private val lambdaParameterNamingRule = LambdaParameterNaming(config) - private val booleanPropertyNamingRule = BooleanPropertyNaming(config) - - override val rules: List = listOf( - variableNamingRule, - variableMinNameLengthRule, - variableMaxNameLengthRule, - topLevelPropertyRule, - objectConstantNamingRule, - nonBooleanPropertyPrefixedWithIsRule, - packageNamingRule, - classOrObjectNamingRule, - enumEntryNamingRule, - functionNamingRule, - functionMaxNameLengthRule, - functionMinNameLengthRule, - forbiddenClassNameRule, - constructorParameterNamingRule, - functionParameterNamingRule, - lambdaParameterNamingRule, - booleanPropertyNamingRule, - ) - - override fun visitPackageDirective(directive: KtPackageDirective) { - super.visitPackageDirective(directive) - packageNamingRule.runIfActive { visitPackageDirective(directive) } - } - - override fun visitNamedDeclaration(declaration: KtNamedDeclaration) { - if (declaration.nameAsSafeName.isSpecial) { - return - } - if (declaration.nameIdentifier?.parent?.javaClass != null) { - when (declaration) { - is KtProperty -> handleProperty(declaration) - is KtNamedFunction -> handleFunction(declaration) - is KtEnumEntry -> enumEntryNamingRule.runIfActive { visitEnumEntry(declaration) } - is KtClassOrObject -> handleClassOrObject(declaration) - is KtParameter -> handleParameter(declaration) - } - } - super.visitNamedDeclaration(declaration) - } - - private fun handleClassOrObject(declaration: KtClassOrObject) { - classOrObjectNamingRule.runIfActive { visitClassOrObject(declaration) } - forbiddenClassNameRule.runIfActive { visitClassOrObject(declaration) } - } - - private fun handleFunction(declaration: KtNamedFunction) { - functionNamingRule.runIfActive { visitNamedFunction(declaration) } - functionMaxNameLengthRule.runIfActive { visitNamedFunction(declaration) } - functionMinNameLengthRule.runIfActive { visitNamedFunction(declaration) } - } - - private fun handleProperty(declaration: KtProperty) { - variableMaxNameLengthRule.runIfActive { visitProperty(declaration) } - variableMinNameLengthRule.runIfActive { visitProperty(declaration) } - nonBooleanPropertyPrefixedWithIsRule.runIfActive { visitProperty(declaration) } - booleanPropertyNamingRule.runIfActive { visitProperty(declaration) } - - when { - declaration.isTopLevel -> topLevelPropertyRule.runIfActive { visitProperty(declaration) } - declaration.withinObjectDeclaration() -> objectConstantNamingRule.runIfActive { visitProperty(declaration) } - else -> variableNamingRule.runIfActive { visitProperty(declaration) } - } - } - - private fun handleParameter(declaration: KtParameter) { - when (declaration.ownerFunction) { - is KtConstructor<*> -> constructorParameterNamingRule.runIfActive { visitParameter(declaration) } - is KtNamedFunction -> functionParameterNamingRule.runIfActive { visitParameter(declaration) } - } - } - - override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) { - super.visitLambdaExpression(lambdaExpression) - lambdaExpression.valueParameters - .forEach { lambdaParameterNamingRule.runIfActive { visitParameter(it) } } - } - - private fun KtVariableDeclaration.withinObjectDeclaration(): Boolean = - this.getNonStrictParentOfType() != null -} diff --git a/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/VariableNaming.kt b/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/VariableNaming.kt index d47f1e8991d..1c13e70c016 100644 --- a/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/VariableNaming.kt +++ b/detekt-rules-naming/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/naming/VariableNaming.kt @@ -13,7 +13,9 @@ import io.gitlab.arturbosch.detekt.api.internal.Configuration import io.gitlab.arturbosch.detekt.rules.identifierName import io.gitlab.arturbosch.detekt.rules.isOverride import io.gitlab.arturbosch.detekt.rules.naming.util.isContainingExcludedClassOrObject +import org.jetbrains.kotlin.psi.KtObjectDeclaration import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.isPrivate import org.jetbrains.kotlin.resolve.calls.util.isSingleUnderscore @@ -43,6 +45,9 @@ class VariableNaming(config: Config = Config.empty) : Rule(config) { private val ignoreOverridden: Boolean by config(true) override fun visitProperty(property: KtProperty) { + if (property.isPropertyTopLevelOrInCompanion()) { + return + } if (property.isSingleUnderscore || property.isContainingExcludedClassOrObject(excludeClassPattern)) { return } @@ -63,6 +68,13 @@ class VariableNaming(config: Config = Config.empty) : Rule(config) { } } + private fun KtProperty.isPropertyTopLevelOrInCompanion(): Boolean { + return this.nameAsSafeName.isSpecial || + this.getNonStrictParentOfType() != null || + this.isTopLevel || + this.nameIdentifier?.parent?.javaClass == null + } + private fun report(property: KtProperty, message: String) { report( CodeSmell( @@ -74,7 +86,6 @@ class VariableNaming(config: Config = Config.empty) : Rule(config) { } companion object { - const val VARIABLE_PATTERN = "variablePattern" const val EXCLUDE_CLASS_PATTERN = "excludeClassPattern" } } diff --git a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/BooleanPropertyNamingSpec.kt b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/BooleanPropertyNamingSpec.kt index 954012b7c34..feefd0690d5 100644 --- a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/BooleanPropertyNamingSpec.kt +++ b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/BooleanPropertyNamingSpec.kt @@ -180,7 +180,7 @@ class BooleanPropertyNamingSpec(val env: KotlinCoreEnvironment) { } @Test - fun `should not warn about Java Boolean override by default`() { + fun `should not warn about Java Boolean override in data class by default`() { val code = """ interface Test { val default: java.lang.Boolean @@ -193,6 +193,22 @@ class BooleanPropertyNamingSpec(val env: KotlinCoreEnvironment) { assertThat(findings).hasSize(1) } + @Test + fun `should not warn about Java Boolean override by default`() { + val code = """ + interface Test { + val default: java.lang.Boolean + } + + class TestImpl : Test { + override val default: java.lang.Boolean = java.lang.Boolean(true) + } + """.trimIndent() + val findings = subject.compileAndLintWithContext(env, code) + + assertThat(findings).hasSize(1) + } + @Test fun `should warn about Java Boolean override if ignoreOverridden is false`() { val code = """ diff --git a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/ClassNamingSpec.kt b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/ClassNamingSpec.kt index 42b6677e57b..43b5d5b89cd 100644 --- a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/ClassNamingSpec.kt +++ b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/ClassNamingSpec.kt @@ -1,11 +1,38 @@ package io.gitlab.arturbosch.detekt.rules.naming +import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.assertThat import io.gitlab.arturbosch.detekt.test.compileAndLint import org.junit.jupiter.api.Test class ClassNamingSpec { + @Test + fun `should detekt no violations for abstract class implementation`() { + val code = """ + abstract class AbstractClass { + abstract fun foo() + } + val foo = object : AbstractClass() { + override fun foo() {} + } + """.trimIndent() + + assertThat(ClassNaming().compileAndLint(code)).isEmpty() + } + + @Test + fun `should use custom name for method and class`() { + val config = TestConfig(mapOf(ClassNaming.CLASS_PATTERN to "^aBbD$")) + assertThat( + ClassNaming(config).compileAndLint( + """ + class aBbD{} + """.trimIndent() + ) + ).isEmpty() + } + @Test fun `should detect no violations class with numbers`() { val code = """ @@ -64,4 +91,16 @@ class ClassNamingSpec { """.trimIndent() assertThat(ClassNaming().compileAndLint(code)).isEmpty() } + + @Test + fun `should not detect any`() { + val code = """ + data class D(val i: Int, val j: Int) + fun doStuff() { + val (_, HOLY_GRAIL) = D(5, 4) + } + """.trimIndent() + + assertThat(ClassNaming().compileAndLint(code)).isEmpty() + } } diff --git a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/EnumNamingSpec.kt b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/EnumNamingSpec.kt index 44e11f0c509..3c82f6faa37 100644 --- a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/EnumNamingSpec.kt +++ b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/EnumNamingSpec.kt @@ -1,11 +1,26 @@ package io.gitlab.arturbosch.detekt.rules.naming +import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.assertThat import io.gitlab.arturbosch.detekt.test.compileAndLint import org.junit.jupiter.api.Test class EnumNamingSpec { + @Test + fun `should use custom name for enum`() { + val rule = EnumNaming(TestConfig(mapOf(EnumNaming.ENUM_PATTERN to "^(enum1)|(enum2)$"))) + assertThat( + rule.compileAndLint( + """ + enum class aBbD { + enum1, enum2 + } + """.trimIndent() + ) + ).isEmpty() + } + @Test fun `should detect no violation`() { val findings = EnumNaming().compileAndLint( @@ -25,7 +40,7 @@ class EnumNamingSpec { default } """.trimIndent() - assertThat(NamingRules().compileAndLint(code)).hasSize(1) + assertThat(EnumNaming().compileAndLint(code)).hasSize(1) } @Test diff --git a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/FunctionMaxLengthSpec.kt b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/FunctionMaxLengthSpec.kt new file mode 100644 index 00000000000..40cfd15441a --- /dev/null +++ b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/FunctionMaxLengthSpec.kt @@ -0,0 +1,31 @@ +package io.gitlab.arturbosch.detekt.rules.naming + +import io.gitlab.arturbosch.detekt.test.TestConfig +import io.gitlab.arturbosch.detekt.test.assertThat +import io.gitlab.arturbosch.detekt.test.compileAndLint +import org.junit.jupiter.api.Test + +class FunctionMaxLengthSpec { + + @Test + fun `should report a function name that is too long base on config`() { + val code = "fun thisFunctionLongName() = 3" + assertThat( + FunctionMaxLength(TestConfig(mapOf("maximumFunctionNameLength" to 10))) + .compileAndLint(code) + ) + .hasSize(1) + } + + @Test + fun `should report a function name that is too long`() { + val code = "fun thisFunctionIsDefinitelyWayTooLongAndShouldBeMuchShorter() = 3" + assertThat(FunctionMaxLength().compileAndLint(code)).hasSize(1) + } + + @Test + fun `should not report a function name that is okay`() { + val code = "fun three() = 3" + assertThat(FunctionMaxLength().compileAndLint(code)).isEmpty() + } +} diff --git a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/FunctionMinLengthSpec.kt b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/FunctionMinLengthSpec.kt new file mode 100644 index 00000000000..62760f3e6ed --- /dev/null +++ b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/FunctionMinLengthSpec.kt @@ -0,0 +1,30 @@ +package io.gitlab.arturbosch.detekt.rules.naming + +import io.gitlab.arturbosch.detekt.test.TestConfig +import io.gitlab.arturbosch.detekt.test.compileAndLint +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class FunctionMinLengthSpec { + + @Test + fun `should report a function name that is too short`() { + val code = "fun a() = 3" + assertThat(FunctionMinLength().compileAndLint(code)).hasSize(1) + } + + @Test + fun `should report a function name that is too short base on config`() { + val code = "fun four() = 3" + assertThat( + FunctionMinLength(TestConfig(mapOf("minimumFunctionNameLength" to 5))) + .compileAndLint(code) + ).hasSize(1) + } + + @Test + fun `should not report a function name that is okay`() { + val code = "fun three() = 3" + assertThat(FunctionMinLength().compileAndLint(code)).isEmpty() + } +} diff --git a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/FunctionNamingSpec.kt b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/FunctionNamingSpec.kt index cb04cd31a86..74ed8abd2db 100644 --- a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/FunctionNamingSpec.kt +++ b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/FunctionNamingSpec.kt @@ -4,7 +4,10 @@ import io.gitlab.arturbosch.detekt.api.SourceLocation import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.assertThat import io.gitlab.arturbosch.detekt.test.compileAndLint +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test +import java.util.regex.PatternSyntaxException class FunctionNamingSpec { @@ -109,4 +112,85 @@ class FunctionNamingSpec { """.trimIndent() assertThat(FunctionNaming().compileAndLint(code)).hasStartSourceLocations(SourceLocation(1, 5)) } + + @Test + fun `should use custom name for method`() { + val config = TestConfig(mapOf(FunctionNaming.FUNCTION_PATTERN to "^`.+`$")) + assertThat( + FunctionNaming(config).compileAndLint( + """ + class Foo { + fun `name with back ticks`(){ + + } + } + """.trimIndent() + ) + ).isEmpty() + } + + @Test + fun shouldExcludeClassesFromFunctionNaming() { + val code = """ + class Bar { + fun MYFun() {} + } + + object Foo { + fun MYFun() {} + } + """.trimIndent() + val config = TestConfig(mapOf(FunctionNaming.EXCLUDE_CLASS_PATTERN to "Foo|Bar")) + assertThat(FunctionNaming(config).compileAndLint(code)).isEmpty() + } + + @Test + fun `should report a function name that begins with a backtick, capitals, and spaces`() { + val subject = FunctionNaming() + val code = "fun `Hi bye`() = 3" + subject.compileAndLint(code) + assertThat(subject.findings).hasSize(1) + } + + @Nested + inner class `exclude class pattern function regex code cases` { + private val excludeClassPatternFunctionRegexCode = """ + class Bar { + fun MYFun() {} + } + + object Foo { + fun MYFun() {} + } + """.trimIndent() + + @Test + fun shouldFailWithInvalidRegexFunctionNaming() { + val config = TestConfig(mapOf(FunctionNaming.EXCLUDE_CLASS_PATTERN to "*Foo")) + assertThatExceptionOfType(PatternSyntaxException::class.java).isThrownBy { + FunctionNaming(config).compileAndLint(excludeClassPatternFunctionRegexCode) + } + } + + @Test + fun shouldNotFailWithInvalidRegexWhenDisabledFunctionNaming() { + val configRules = mapOf( + "active" to "false", + FunctionNaming.EXCLUDE_CLASS_PATTERN to "*Foo" + ) + val config = TestConfig(configRules) + assertThat(FunctionNaming(config).compileAndLint(excludeClassPatternFunctionRegexCode)).isEmpty() + } + } + + @Test + fun `should not detect any`() { + val code = """ + data class D(val i: Int, val j: Int) + fun doStuff() { + val (_, HOLY_GRAIL) = D(5, 4) + } + """.trimIndent() + assertThat(FunctionNaming().compileAndLint(code)).isEmpty() + } } diff --git a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/NamingConventionCustomPatternSpec.kt b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/NamingConventionCustomPatternSpec.kt deleted file mode 100644 index 545e2519537..00000000000 --- a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/NamingConventionCustomPatternSpec.kt +++ /dev/null @@ -1,190 +0,0 @@ -package io.gitlab.arturbosch.detekt.rules.naming - -import io.gitlab.arturbosch.detekt.test.TestConfig -import io.gitlab.arturbosch.detekt.test.compileAndLint -import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.assertThatExceptionOfType -import org.junit.jupiter.api.Test -import java.util.regex.PatternSyntaxException - -class NamingConventionCustomPatternSpec { - - val configCustomRules = - object : TestConfig() { - override fun subConfig(key: String): TestConfig = this - - @Suppress("UNCHECKED_CAST") - override fun valueOrDefault(key: String, default: T): T = - when (key) { - FunctionNaming.FUNCTION_PATTERN -> "^`.+`$" as T - ClassNaming.CLASS_PATTERN -> "^aBbD$" as T - VariableNaming.VARIABLE_PATTERN -> "^123var$" as T - TopLevelPropertyNaming.CONSTANT_PATTERN -> "^lowerCaseConst$" as T - EnumNaming.ENUM_PATTERN -> "^(enum1)|(enum2)$" as T - PackageNaming.PACKAGE_PATTERN -> "^(package_1)$" as T - FunctionMaxLength.MAXIMUM_FUNCTION_NAME_LENGTH -> 50 as T - else -> default - } - } - - val testConfig = - object : TestConfig() { - override fun subConfig(key: String): TestConfig = - when (key) { - FunctionNaming::class.simpleName -> configCustomRules - FunctionMaxLength::class.simpleName -> configCustomRules - ClassNaming::class.simpleName -> configCustomRules - VariableNaming::class.simpleName -> configCustomRules - TopLevelPropertyNaming::class.simpleName -> configCustomRules - EnumNaming::class.simpleName -> configCustomRules - PackageNaming::class.simpleName -> configCustomRules - else -> this - } - - override fun valueOrDefault(key: String, default: T): T = default - } - - val excludeClassPatternVariableRegexCode = """ - class Bar { - val MYVar = 3 - } - - object Foo { - val MYVar = 3 - } - """.trimIndent() - - val excludeClassPatternFunctionRegexCode = """ - class Bar { - fun MYFun() {} - } - - object Foo { - fun MYFun() {} - } - """.trimIndent() - - @Test - fun `should use custom name for method and class`() { - val rule = NamingRules(testConfig) - assertThat( - rule.compileAndLint( - """ - class aBbD{ - fun `name with back ticks`(){ - val `123var` = "" - } - - companion object { - const val lowerCaseConst = "" - } - } - """.trimIndent() - ) - ).isEmpty() - } - - @Test - fun `should use custom name for constant`() { - val rule = NamingRules(testConfig) - assertThat( - rule.compileAndLint( - """ - class aBbD{ - companion object { - const val lowerCaseConst = "" - } - } - """.trimIndent() - ) - ).isEmpty() - } - - @Test - fun `should use custom name for enum`() { - val rule = NamingRules(testConfig) - assertThat( - rule.compileAndLint( - """ - class aBbD{ - enum class aBbD { - enum1, enum2 - } - } - """.trimIndent() - ) - ).isEmpty() - } - - @Test - fun `should use custom name for package`() { - val rule = NamingRules(testConfig) - assertThat(rule.compileAndLint("package package_1")).isEmpty() - } - - @Test - fun shouldExcludeClassesFromVariableNaming() { - val code = """ - class Bar { - val MYVar = 3 - } - - object Foo { - val MYVar = 3 - } - """.trimIndent() - val config = TestConfig(mapOf(VariableNaming.EXCLUDE_CLASS_PATTERN to "Foo|Bar")) - assertThat(VariableNaming(config).compileAndLint(code)).isEmpty() - } - - @Test - fun shouldNotFailWithInvalidRegexWhenDisabledVariableNaming() { - val configValues = mapOf( - "active" to "false", - VariableNaming.EXCLUDE_CLASS_PATTERN to "*Foo" - ) - val config = TestConfig(configValues) - assertThat(VariableNaming(config).compileAndLint(excludeClassPatternVariableRegexCode)).isEmpty() - } - - @Test - fun shouldFailWithInvalidRegexVariableNaming() { - val config = TestConfig(mapOf(VariableNaming.EXCLUDE_CLASS_PATTERN to "*Foo")) - assertThatExceptionOfType(PatternSyntaxException::class.java).isThrownBy { - VariableNaming(config).compileAndLint(excludeClassPatternVariableRegexCode) - } - } - - @Test - fun shouldExcludeClassesFromFunctionNaming() { - val code = """ - class Bar { - fun MYFun() {} - } - - object Foo { - fun MYFun() {} - } - """.trimIndent() - val config = TestConfig(mapOf(FunctionNaming.EXCLUDE_CLASS_PATTERN to "Foo|Bar")) - assertThat(FunctionNaming(config).compileAndLint(code)).isEmpty() - } - - @Test - fun shouldNotFailWithInvalidRegexWhenDisabledFunctionNaming() { - val configRules = mapOf( - "active" to "false", - FunctionNaming.EXCLUDE_CLASS_PATTERN to "*Foo" - ) - val config = TestConfig(configRules) - assertThat(FunctionNaming(config).compileAndLint(excludeClassPatternFunctionRegexCode)).isEmpty() - } - - @Test - fun shouldFailWithInvalidRegexFunctionNaming() { - val config = TestConfig(mapOf(FunctionNaming.EXCLUDE_CLASS_PATTERN to "*Foo")) - assertThatExceptionOfType(PatternSyntaxException::class.java).isThrownBy { - FunctionNaming(config).compileAndLint(excludeClassPatternFunctionRegexCode) - } - } -} diff --git a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/NamingConventionLengthSpec.kt b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/NamingConventionLengthSpec.kt deleted file mode 100644 index 964081edad8..00000000000 --- a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/NamingConventionLengthSpec.kt +++ /dev/null @@ -1,110 +0,0 @@ -package io.gitlab.arturbosch.detekt.rules.naming - -import io.gitlab.arturbosch.detekt.test.TestConfig -import io.gitlab.arturbosch.detekt.test.compileAndLint -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Nested -import org.junit.jupiter.api.Test - -class NamingConventionLengthSpec { - - @Test - fun `should not report underscore variable names`() { - val subject = NamingRules() - val code = """ - fun getResult(): Pair = TODO() - fun function() { - val (_, status) = getResult() - } - """.trimIndent() - subject.compileAndLint(code) - assertThat(subject.findings).isEmpty() - } - - @Test - fun `should not report a variable with single letter name`() { - val subject = NamingRules() - val code = "private val a = 3" - subject.compileAndLint(code) - assertThat(subject.findings).isEmpty() - } - - @Nested - inner class `VariableMinLength rule with a custom minimum length` { - - val variableMinLength = - VariableMinLength(TestConfig(mapOf(VariableMinLength.MINIMUM_VARIABLE_NAME_LENGTH to "2"))) - - @Test - fun `reports a very short variable name`() { - val code = "private val a = 3" - assertThat(variableMinLength.compileAndLint(code)).hasSize(1) - } - - @Test - fun `does not report a variable with only a single underscore`() { - val code = """ - class C { - val prop: (Int) -> Unit = { _ -> Unit } - } - """.trimIndent() - assertThat(variableMinLength.compileAndLint(code)).isEmpty() - } - } - - @Test - fun `should not report a variable with 64 letters`() { - val subject = NamingRules() - val code = "private val varThatIsExactly64LettersLongWhichYouMightNotWantToBelieveInLolz = 3" - subject.compileAndLint(code) - assertThat(subject.findings).isEmpty() - } - - @Test - fun `should report a variable name that is too long`() { - val subject = NamingRules() - val code = "private val thisVariableIsDefinitelyWayTooLongLongerThanEverythingAndShouldBeMuchShorter = 3" - subject.compileAndLint(code) - assertThat(subject.findings).hasSize(1) - } - - @Test - fun `should not report a variable name that is okay`() { - val subject = NamingRules() - val code = "private val thisOneIsCool = 3" - subject.compileAndLint(code) - assertThat(subject.findings).isEmpty() - } - - @Test - fun `should report a function name that is too short`() { - val subject = NamingRules() - val code = "fun a() = 3" - subject.compileAndLint(code) - assertThat(subject.findings).hasSize(1) - } - - @Test - fun `should report a function name that is too long`() { - val subject = NamingRules() - val code = "fun thisFunctionIsDefinitelyWayTooLongAndShouldBeMuchShorter() = 3" - subject.compileAndLint(code) - assertThat(subject.findings).hasSize(1) - } - - @Test - fun `should not report a function name that is okay`() { - val subject = NamingRules() - val code = "fun three() = 3" - subject.compileAndLint(code) - assertThat(subject.findings).isEmpty() - } - - @Test - fun `should report a function name that begins with a backtick, capitals, and spaces`() { - val subject = NamingRules() - val code = "fun `Hi bye`() = 3" - subject.compileAndLint(code) - assertThat(subject.findings).hasSize(1) - } -} diff --git a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/NamingRulesSpec.kt b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/NamingRulesSpec.kt deleted file mode 100644 index 21e7841a19f..00000000000 --- a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/NamingRulesSpec.kt +++ /dev/null @@ -1,19 +0,0 @@ -package io.gitlab.arturbosch.detekt.rules.naming - -import io.gitlab.arturbosch.detekt.test.assertThat -import io.gitlab.arturbosch.detekt.test.compileAndLint -import org.junit.jupiter.api.Test - -class NamingRulesSpec { - - @Test - fun `should not detect any`() { - val code = """ - data class D(val i: Int, val j: Int) - fun doStuff() { - val (_, HOLY_GRAIL) = D(5, 4) - } - """.trimIndent() - assertThat(NamingRules().compileAndLint(code)).isEmpty() - } -} diff --git a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/PackageNamingSpec.kt b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/PackageNamingSpec.kt index 85fa407b16c..ab00ac81b8a 100644 --- a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/PackageNamingSpec.kt +++ b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/PackageNamingSpec.kt @@ -1,11 +1,18 @@ package io.gitlab.arturbosch.detekt.rules.naming +import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.compileAndLint import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test class PackageNamingSpec { + @Test + fun `should use custom name for package`() { + val rule = PackageNaming(TestConfig(PackageNaming.PACKAGE_PATTERN to "^(package_1)$")) + assertThat(rule.compileAndLint("package package_1")).isEmpty() + } + @Test fun `should ignore the issue by alias suppression`() { assertThat( diff --git a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/TopLevelPropertyNamingSpec.kt b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/TopLevelPropertyNamingSpec.kt index 522ebca29b2..3d238104ba6 100644 --- a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/TopLevelPropertyNamingSpec.kt +++ b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/TopLevelPropertyNamingSpec.kt @@ -1,5 +1,7 @@ package io.gitlab.arturbosch.detekt.rules.naming +import io.gitlab.arturbosch.detekt.test.TestConfig +import io.gitlab.arturbosch.detekt.test.compileAndLint import io.gitlab.arturbosch.detekt.test.lint import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Nested @@ -9,6 +11,21 @@ class TopLevelPropertyNamingSpec { val subject = TopLevelPropertyNaming() + @Test + fun `should use custom name top level propeties`() { + assertThat( + TopLevelPropertyNaming(TestConfig(mapOf(TopLevelPropertyNaming.CONSTANT_PATTERN to "^lowerCaseConst$"))).compileAndLint( + """ + class Foo{ + companion object { + const val lowerCaseConst = "" + } + } + """.trimIndent() + ) + ).isEmpty() + } + @Nested inner class `constants on top level` { diff --git a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/VariableMaxLengthSpec.kt b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/VariableMaxLengthSpec.kt new file mode 100644 index 00000000000..07c93bc655d --- /dev/null +++ b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/VariableMaxLengthSpec.kt @@ -0,0 +1,31 @@ +package io.gitlab.arturbosch.detekt.rules.naming + +import io.gitlab.arturbosch.detekt.test.assertThat +import io.gitlab.arturbosch.detekt.test.compileAndLint +import org.junit.jupiter.api.Test + +class VariableMaxLengthSpec { + + @Test + fun `should not report underscore variable names`() { + val code = """ + fun getResult(): Pair = TODO() + fun function() { + val (_, status) = getResult() + } + """.trimIndent() + assertThat(VariableMaxLength().compileAndLint(code)).isEmpty() + } + + @Test + fun `should not report a variable with 64 letters`() { + val code = "private val varThatIsExactly64LettersLongWhichYouMightNotWantToBelieveInLolz = 3" + assertThat(VariableMaxLength().compileAndLint(code)).isEmpty() + } + + @Test + fun `should report a variable name that is too long`() { + val code = "private val thisVariableIsDefinitelyWayTooLongLongerThanEverythingAndShouldBeMuchShorter = 3" + assertThat(VariableMaxLength().compileAndLint(code)).hasSize(1) + } +} diff --git a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/VariableMinLengthSpec.kt b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/VariableMinLengthSpec.kt new file mode 100644 index 00000000000..e99cda86cd7 --- /dev/null +++ b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/VariableMinLengthSpec.kt @@ -0,0 +1,56 @@ +package io.gitlab.arturbosch.detekt.rules.naming + +import io.gitlab.arturbosch.detekt.test.TestConfig +import io.gitlab.arturbosch.detekt.test.compileAndLint +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test + +class VariableMinLengthSpec { + + @Nested + inner class `VariableMinLength rule with a custom minimum length` { + + val variableMinLength = + VariableMinLength(TestConfig(mapOf(VariableMinLength.MINIMUM_VARIABLE_NAME_LENGTH to "2"))) + + @Test + fun `reports a very short variable name`() { + val code = "private val a = 3" + assertThat(variableMinLength.compileAndLint(code)).hasSize(1) + } + + @Test + fun `does not report a variable with only a single underscore`() { + val code = """ + class C { + val prop: (Int) -> Unit = { _ -> Unit } + } + """.trimIndent() + assertThat(variableMinLength.compileAndLint(code)).isEmpty() + } + } + + @Test + fun `should not report a variable name that is okay`() { + val code = "private val thisOneIsCool = 3" + assertThat(VariableMinLength().compileAndLint(code)).isEmpty() + } + + @Test + fun `should not report a variable with single letter name`() { + val code = "private val a = 3" + assertThat(VariableMinLength().compileAndLint(code)).isEmpty() + } + + @Test + fun `should not report underscore variable names`() { + val code = """ + fun getResult(): Pair = TODO() + fun function() { + val (_, status) = getResult() + } + """.trimIndent() + assertThat(VariableMinLength().compileAndLint(code)).isEmpty() + } +} diff --git a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/VariableNamingSpec.kt b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/VariableNamingSpec.kt index 07e037c207f..ff825b8909a 100644 --- a/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/VariableNamingSpec.kt +++ b/detekt-rules-naming/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/naming/VariableNamingSpec.kt @@ -4,10 +4,60 @@ import io.gitlab.arturbosch.detekt.api.SourceLocation import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.assertThat import io.gitlab.arturbosch.detekt.test.compileAndLint +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test +import java.util.regex.PatternSyntaxException class VariableNamingSpec { + @Nested + inner class `Exclude class pattern cases` { + + private val excludeClassPatternVariableRegexCode = """ + class Bar { + val MYVar = 3 + } + + object Foo { + val MYVar = 3 + } + """.trimIndent() + + @Test + fun shouldNotFailWithInvalidRegexWhenDisabledVariableNaming() { + val configValues = mapOf( + "active" to "false", + VariableNaming.EXCLUDE_CLASS_PATTERN to "*Foo" + ) + val config = TestConfig(configValues) + assertThat(VariableNaming(config).compileAndLint(excludeClassPatternVariableRegexCode)).isEmpty() + } + + @Test + fun shouldFailWithInvalidRegexVariableNaming() { + val config = TestConfig(mapOf(VariableNaming.EXCLUDE_CLASS_PATTERN to "*Foo")) + assertThatExceptionOfType(PatternSyntaxException::class.java).isThrownBy { + VariableNaming(config).compileAndLint(excludeClassPatternVariableRegexCode) + } + } + } + + @Test + fun shouldExcludeClassesFromVariableNaming() { + val code = """ + class Bar { + val MYVar = 3 + } + + object Foo { + val MYVar = 3 + } + """.trimIndent() + val config = TestConfig(mapOf(VariableNaming.EXCLUDE_CLASS_PATTERN to "Foo|Bar")) + assertThat(VariableNaming(config).compileAndLint(code)).isEmpty() + } + @Test fun `should detect all positive cases`() { val code = """ @@ -53,6 +103,19 @@ class VariableNamingSpec { assertThat(VariableNaming().compileAndLint(code)).isEmpty() } + @Test + fun `should not flag lambda fun arguments`() { + val code = """ + fun foo() { + listOf>().flatMap { (left, right) -> listOf(left, right) } + } + fun bar() { + listOf>().flatMap { (right, _) -> listOf(right) } + } + """.trimIndent() + assertThat(VariableNaming().compileAndLint(code)).isEmpty() + } + @Test fun `doesn't ignore overridden member properties if ignoreOverridden is false`() { val code = """ @@ -73,6 +136,17 @@ class VariableNamingSpec { SourceLocation(5, 18) ) } + + @Test + fun `should not detect any`() { + val code = """ + data class D(val i: Int, val j: Int) + fun doStuff() { + val (_, HOLY_GRAIL) = D(5, 4) + } + """.trimIndent() + assertThat(VariableNaming().compileAndLint(code)).isEmpty() + } } private const val IGNORE_OVERRIDDEN = "ignoreOverridden"