-
-
Notifications
You must be signed in to change notification settings - Fork 755
/
MatchingDeclarationName.kt
94 lines (86 loc) · 3.53 KB
/
MatchingDeclarationName.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package io.gitlab.arturbosch.detekt.rules.naming
import io.github.detekt.psi.fileNameWithoutSuffix
import io.gitlab.arturbosch.detekt.api.CodeSmell
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.api.Debt
import io.gitlab.arturbosch.detekt.api.Entity
import io.gitlab.arturbosch.detekt.api.Issue
import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.Severity
import io.gitlab.arturbosch.detekt.api.config
import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault
import io.gitlab.arturbosch.detekt.api.internal.Configuration
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtTypeAlias
import org.jetbrains.kotlin.psi.psiUtil.isPrivate
/**
* "If a Kotlin file contains a single non-private class (potentially with related top-level declarations),
* its name should be the same as the name of the class, with the .kt extension appended.
* If a file contains multiple classes, or only top-level declarations,
* choose a name describing what the file contains, and name the file accordingly.
* Use camel humps with an uppercase first letter (e.g. ProcessDeclarations.kt).
*
* The name of the file should describe what the code in the file does.
* Therefore, you should avoid using meaningless words such as "Util" in file names." - Official Kotlin Style Guide
*
* More information at: https://kotlinlang.org/docs/coding-conventions.html
*
* <noncompliant>
*
* class Foo // FooUtils.kt
*
* fun Bar.toFoo(): Foo = ...
* fun Foo.toBar(): Bar = ...
*
* </noncompliant>
*
* <compliant>
*
* class Foo { // Foo.kt
* fun stuff() = 42
* }
*
* fun Bar.toFoo(): Foo = ...
*
* </compliant>
*/
@ActiveByDefault(since = "1.0.0")
class MatchingDeclarationName(config: Config = Config.empty) : Rule(config) {
override val issue: Issue = Issue(
javaClass.simpleName,
Severity.Style,
"If a source file contains only a single non-private top-level class or object, " +
"the file name should reflect the case-sensitive name plus the .kt extension.",
Debt.FIVE_MINS
)
@Configuration("name should only be checked if the file starts with a class or object")
private val mustBeFirst: Boolean by config(true)
override fun visitKtFile(file: KtFile) {
val declarations = file.declarations
.asSequence()
.filterIsInstance<KtClassOrObject>()
.filterNot { it.isPrivate() }
.toList()
fun matchesFirstClassOrObjectCondition(): Boolean =
!mustBeFirst || mustBeFirst && declarations.first() === file.declarations.first()
fun hasNoMatchingTypeAlias(filename: String): Boolean =
file.declarations.filterIsInstance<KtTypeAlias>().all { it.name != filename }
if (declarations.size == 1 && matchesFirstClassOrObjectCondition()) {
val declaration = declarations.first()
val declarationName = declaration.name
val filename = file.fileNameWithoutSuffix()
if (declarationName != filename && hasNoMatchingTypeAlias(filename)) {
val entity = Entity.atName(declaration).copy(ktElement = file)
report(
CodeSmell(
issue,
entity,
"The file name '$filename' " +
"does not match the name of the single top-level declaration '$declarationName'."
)
)
}
}
}
}