aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStanley Shyiko <stanley.shyiko@gmail.com>2018-05-01 09:42:10 -0700
committerGitHub <noreply@github.com>2018-05-01 09:42:10 -0700
commit79f5a7991a5f367e2a55b8caecfa63464b68ae7c (patch)
treebed13d936e1b7d6894b87d535c3c7d5c14a0ca24
parentcb91be53736d57fdfc6efd81aa6c6bff2d6fc92f (diff)
parent1e3823cf77c4be8ab165814a13d2a986c6024da2 (diff)
downloadktlint-79f5a7991a5f367e2a55b8caecfa63464b68ae7c.tar.gz
Merge pull request #194 from JelloRanger/jelloranger/add-single-class-matches-filename-rule
Add rule to check that a single top level class name matches the file name
-rw-r--r--ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/ClassNameMatchesFileNameRule.kt39
-rw-r--r--ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoMultipleSpacesRule.kt5
-rw-r--r--ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoUnusedImportsRule.kt5
-rw-r--r--ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/ParameterListWrappingRule.kt5
-rw-r--r--ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/StandardRuleSetProvider.kt1
-rw-r--r--ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/package.kt5
-rw-r--r--ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/ClassNameMatchesFileNameRuleTest.kt81
-rw-r--r--ktlint-test/src/main/kotlin/com/github/shyiko/ktlint/test/DumpAST.kt (renamed from ktlint-test/src/main/kotlin/com/github/shyiko/ktlint/test/package.kt)0
8 files changed, 126 insertions, 15 deletions
diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/ClassNameMatchesFileNameRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/ClassNameMatchesFileNameRule.kt
new file mode 100644
index 00000000..9622c2bf
--- /dev/null
+++ b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/ClassNameMatchesFileNameRule.kt
@@ -0,0 +1,39 @@
+package com.github.shyiko.ktlint.ruleset.standard
+
+import com.github.shyiko.ktlint.core.KtLint
+import com.github.shyiko.ktlint.core.Rule
+import org.jetbrains.kotlin.com.intellij.lang.ASTNode
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
+import java.nio.file.Paths
+
+/**
+ * If there is only one top level class in a given file, then its name should match the file's name
+ */
+class ClassNameMatchesFileNameRule : Rule("class-name-matches-file-name"), Rule.Modifier.RestrictToRoot {
+
+ override fun visit(
+ node: ASTNode,
+ autoCorrect: Boolean,
+ emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit
+ ) {
+ val filePath = node.getUserData(KtLint.FILE_PATH_USER_DATA_KEY)
+
+ // Ignore all non ".kt" files (including ".kts")
+ if (filePath?.endsWith(".kt") != true) {
+ return
+ }
+
+ val topLevelClassNames = node.getChildren(null)
+ .filter { it.elementType == KtStubElementTypes.CLASS }
+ .mapNotNull { it.findChildByType(KtTokens.IDENTIFIER)?.text }
+
+ val name = Paths.get(filePath).fileName.toString().substringBefore(".")
+ if (topLevelClassNames.size == 1 && name != topLevelClassNames.first()) {
+ val className = topLevelClassNames.first()
+ emit(0,
+ "Class $className should be declared in a file named $className.kt",
+ false)
+ }
+ }
+}
diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoMultipleSpacesRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoMultipleSpacesRule.kt
index 84287e4c..15aa1a2a 100644
--- a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoMultipleSpacesRule.kt
+++ b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoMultipleSpacesRule.kt
@@ -78,9 +78,4 @@ class NoMultipleSpacesRule : Rule("no-multi-spaces") {
}
}
}
-
- private fun ASTNode.visit(cb: (node: ASTNode) -> Unit) {
- cb(this)
- this.getChildren(null).forEach { it.visit(cb) }
- }
}
diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoUnusedImportsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoUnusedImportsRule.kt
index 7973044d..3b0e1b8f 100644
--- a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoUnusedImportsRule.kt
+++ b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoUnusedImportsRule.kt
@@ -84,9 +84,4 @@ class NoUnusedImportsRule : Rule("no-unused-imports") {
}
private fun String.isComponentN() = componentNRegex.matches(this)
-
- private fun ASTNode.visit(cb: (node: ASTNode) -> Unit) {
- cb(this)
- this.getChildren(null).forEach { it.visit(cb) }
- }
}
diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/ParameterListWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/ParameterListWrappingRule.kt
index 3117727b..aa4e2843 100644
--- a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/ParameterListWrappingRule.kt
+++ b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/ParameterListWrappingRule.kt
@@ -121,11 +121,6 @@ class ParameterListWrappingRule : Rule("parameter-list-wrapping") {
return offsetToTheLeft + 1
}
- private fun ASTNode.visit(cb: (node: ASTNode) -> Unit) {
- cb(this)
- this.getChildren(null).forEach { it.visit(cb) }
- }
-
private fun errorMessage(node: ASTNode) =
when (node.elementType) {
KtStubElementTypes.VALUE_PARAMETER ->
diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/StandardRuleSetProvider.kt b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/StandardRuleSetProvider.kt
index 17a2ddd8..57cf907a 100644
--- a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/StandardRuleSetProvider.kt
+++ b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/StandardRuleSetProvider.kt
@@ -7,6 +7,7 @@ class StandardRuleSetProvider : RuleSetProvider {
override fun get(): RuleSet = RuleSet("standard",
ChainWrappingRule(),
+ ClassNameMatchesFileNameRule(),
FinalNewlineRule(),
// disabled until it's clear how to reconcile difference in Intellij & Android Studio import layout
// ImportOrderingRule(),
diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/package.kt b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/package.kt
index 087a1934..1d64c651 100644
--- a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/package.kt
+++ b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/package.kt
@@ -1,5 +1,6 @@
package com.github.shyiko.ktlint.ruleset.standard
+import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.com.intellij.psi.PsiElement
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.com.intellij.psi.util.PsiTreeUtil
@@ -11,6 +12,10 @@ internal fun PsiElement.isPartOf(clazz: KClass<out PsiElement>) = getNonStrictPa
internal fun PsiElement.isPartOfString() = isPartOf(KtStringTemplateEntry::class)
internal fun PsiElement.prevLeaf(): LeafPsiElement? = PsiTreeUtil.prevLeaf(this) as LeafPsiElement?
internal fun PsiElement.nextLeaf(): LeafPsiElement? = PsiTreeUtil.nextLeaf(this) as LeafPsiElement?
+internal fun ASTNode.visit(cb: (node: ASTNode) -> Unit) {
+ cb(this)
+ this.getChildren(null).forEach { it.visit(cb) }
+}
internal fun <T> List<T>.head() = this.subList(0, this.size - 1)
internal fun <T> List<T>.tail() = this.subList(1, this.size)
diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/ClassNameMatchesFileNameRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/ClassNameMatchesFileNameRuleTest.kt
new file mode 100644
index 00000000..e57759b8
--- /dev/null
+++ b/ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/ClassNameMatchesFileNameRuleTest.kt
@@ -0,0 +1,81 @@
+package com.github.shyiko.ktlint.ruleset.standard
+
+import com.github.shyiko.ktlint.core.LintError
+import com.github.shyiko.ktlint.test.lint
+import org.assertj.core.api.Assertions.assertThat
+import org.testng.annotations.Test
+
+class ClassNameMatchesFileNameRuleTest {
+
+ @Test
+ fun testMatchingSingleClassName() {
+ assertThat(ClassNameMatchesFileNameRule().lint(
+ """
+ class A
+ """.trimIndent(),
+ fileName("/some/path/A.kt")
+ )).isEmpty()
+ }
+
+ @Test
+ fun testNonMatchingSingleClassName() {
+ assertThat(ClassNameMatchesFileNameRule().lint(
+ """
+ class B
+ """.trimIndent(),
+ fileName("A.kt")
+ )).isEqualTo(listOf(
+ LintError(1, 1, "class-name-matches-file-name", "Class B should be declared in a file named B.kt")
+ ))
+ }
+
+ @Test
+ fun testMultipleTopLevelClasses() {
+ assertThat(ClassNameMatchesFileNameRule().lint(
+ """
+ class B
+ class C
+ """.trimIndent(),
+ fileName("A.kt")
+ )).isEmpty()
+ }
+
+ @Test
+ fun testMultipleNonTopLevelClasses() {
+ assertThat(ClassNameMatchesFileNameRule().lint(
+ """
+ class B {
+ class C
+ class D
+ }
+ """.trimIndent(),
+ fileName("A.kt")
+ )).isEqualTo(listOf(
+ LintError(1, 1, "class-name-matches-file-name", "Class B should be declared in a file named B.kt")
+ ))
+ }
+
+ @Test
+ fun testCaseSensitiveMatching() {
+ assertThat(ClassNameMatchesFileNameRule().lint(
+ """
+ interface Woohoo
+ """.trimIndent(),
+ fileName("woohoo.kt")
+ )).isEqualTo(listOf(
+ LintError(1, 1, "class-name-matches-file-name", "Class Woohoo should be declared in a file named Woohoo.kt")
+ ))
+ }
+
+ @Test
+ fun testIgnoreKotlinScriptFiles() {
+ assertThat(ClassNameMatchesFileNameRule().lint(
+ """
+ class B
+ """.trimIndent(),
+ fileName("A.kts")
+ )).isEmpty()
+ }
+
+ private fun fileName(fileName: String) = mapOf("file_path" to fileName)
+}
diff --git a/ktlint-test/src/main/kotlin/com/github/shyiko/ktlint/test/package.kt b/ktlint-test/src/main/kotlin/com/github/shyiko/ktlint/test/DumpAST.kt
index a22223ee..a22223ee 100644
--- a/ktlint-test/src/main/kotlin/com/github/shyiko/ktlint/test/package.kt
+++ b/ktlint-test/src/main/kotlin/com/github/shyiko/ktlint/test/DumpAST.kt