diff --git a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NamingConventionViolation.kt b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NamingConventionViolation.kt new file mode 100644 index 000000000..2b5540ad6 --- /dev/null +++ b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NamingConventionViolation.kt @@ -0,0 +1,38 @@ +package io.gitlab.arturbosch.detekt.rules + +import io.gitlab.arturbosch.detekt.api.CodeSmell +import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.Location +import io.gitlab.arturbosch.detekt.api.Rule +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtNamedDeclaration +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.KtVariableDeclaration + +/** + * @author Artur Bosch + */ +class NamingConventionViolation(config: Config = Config.EMPTY) : Rule("NamingConventionViolation", Severity.Style, config) { + + private val variablePattern = Regex("^(_)?[a-z$][a-zA-Z$0-9]*$") + private val methodPattern = Regex("^[a-z$][a-zA-Z$0-9]*$") + private val classPattern = Regex("^[A-Z$][a-zA-Z$]*$") + + override fun visitNamedDeclaration(declaration: KtNamedDeclaration) { + if (declaration.nameAsSafeName.isSpecial) return + declaration.nameIdentifier?.parent?.javaClass?.let { + val name = declaration.nameAsSafeName.asString() + if (declaration is KtVariableDeclaration && !name.matches(variablePattern)) { + addFindings(CodeSmell(id, Location.of(declaration))) + } + if (declaration is KtNamedFunction && !name.matches(methodPattern)) { + addFindings(CodeSmell(id, Location.of(declaration))) + } + if (declaration is KtClassOrObject && !name.matches(classPattern)) { + addFindings(CodeSmell(id, Location.of(declaration))) + } + } + super.visitNamedDeclaration(declaration) + } + +} \ No newline at end of file diff --git a/detekt-rules/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/KT.kt b/detekt-rules/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/KT.kt index a06eba6f0..75408721d 100644 --- a/detekt-rules/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/KT.kt +++ b/detekt-rules/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/KT.kt @@ -14,6 +14,7 @@ private val compiler = KtCompiler() enum class Case(val file: String) { CasesFolder("/cases"), Default("/cases/Default.kt"), + NamingConventions("/cases/NamingConventions.kt"), ComplexClass("/cases/ComplexClass.kt"), NestedClasses("/cases/NestedClasses.kt"), NestedLongMethods("/cases/NestedLongMethods.kt"); diff --git a/detekt-rules/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/NamingConventionViolationSpec.kt b/detekt-rules/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/NamingConventionViolationSpec.kt new file mode 100644 index 000000000..3609746ea --- /dev/null +++ b/detekt-rules/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/NamingConventionViolationSpec.kt @@ -0,0 +1,22 @@ +package io.gitlab.arturbosch.detekt.rules + +import com.natpryce.hamkrest.assertion.assertThat +import com.natpryce.hamkrest.equalTo +import com.natpryce.hamkrest.hasSize +import org.jetbrains.spek.api.SubjectSpek +import org.jetbrains.spek.api.dsl.it + +/** + * @author Artur Bosch + */ +class NamingConventionViolationSpec : SubjectSpek({ + subject { NamingConventionViolation() } + + it("should find all wrong namings") { + val root = load(Case.NamingConventions) + subject.visit(root) + subject.findings.map { it.compact() }.forEach { println(it) } + assertThat(subject.findings, hasSize(equalTo(9))) + } + +}) \ No newline at end of file diff --git a/detekt-rules/src/test/resources/cases/NamingConventions.kt b/detekt-rules/src/test/resources/cases/NamingConventions.kt new file mode 100644 index 000000000..fc8147ef3 --- /dev/null +++ b/detekt-rules/src/test/resources/cases/NamingConventions.kt @@ -0,0 +1,37 @@ +package cases + +// both valid +val variable = 5 +val _variable = 5 +// invalid start with _ is optional, but then lowercase! +val V_riable = 5 +val _Variable = 5 + +//valid +fun fileMethod() { +} +//invalid +fun FileMethod() { +} +fun _fileMethod() { +} + +@Suppress("unused") +class NamingConventions { + + //invalid + val C_lassVariable = 5 + //valid + val _classVariable = 5 + val classVariable = 5 + fun classMethod(){ + } + //invalid + fun _classmethod(){ + } + fun Classmethod(){ + } +} +//invalid +class _NamingConventions{} +class namingConventions{} \ No newline at end of file