-
-
Notifications
You must be signed in to change notification settings - Fork 758
/
RedundantExplicitType.kt
97 lines (91 loc) · 3.64 KB
/
RedundantExplicitType.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
95
96
97
package io.gitlab.arturbosch.detekt.rules.style
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.internal.RequiresTypeResolution
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtConstantExpression
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtStringTemplateExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.types.AbbreviatedType
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isBoolean
import org.jetbrains.kotlin.types.typeUtil.isChar
import org.jetbrains.kotlin.types.typeUtil.isDouble
import org.jetbrains.kotlin.types.typeUtil.isFloat
import org.jetbrains.kotlin.types.typeUtil.isInt
import org.jetbrains.kotlin.types.typeUtil.isLong
/*
* Based on code from Kotlin compiler:
* https://github.com/JetBrains/kotlin/blob/v1.3.50/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantExplicitTypeInspection.kt
*/
/**
* Local properties do not need their type to be explicitly provided when the inferred type matches the explicit type.
*
* <noncompliant>
* fun function() {
* val x: String = "string"
* }
* </noncompliant>
*
* <compliant>
* fun function() {
* val x = "string"
* }
* </compliant>
*/
@RequiresTypeResolution
class RedundantExplicitType(config: Config) : Rule(config) {
override val issue = Issue(
"RedundantExplicitType",
Severity.Style,
"Type does not need to be stated explicitly and can be removed.",
Debt.FIVE_MINS
)
@Suppress("ReturnCount", "ComplexMethod")
override fun visitProperty(property: KtProperty) {
if (bindingContext == BindingContext.EMPTY) return
if (!property.isLocal) return
val typeReference = property.typeReference ?: return
val type =
(bindingContext[BindingContext.VARIABLE, property])?.type ?: return
if (type is AbbreviatedType) return
when (val initializer = property.initializer) {
is KtConstantExpression -> if (!initializer.typeIsSameAs(type)) return
is KtStringTemplateExpression -> if (!KotlinBuiltIns.isString(type)) return
is KtNameReferenceExpression -> if (typeReference.text != initializer.getReferencedName()) return
is KtCallExpression -> if (typeReference.text != initializer.calleeExpression?.text) return
else -> return
}
report(CodeSmell(issue, Entity.atName(property), issue.description))
super.visitProperty(property)
}
private fun KtConstantExpression.typeIsSameAs(type: KotlinType) =
when (node.elementType) {
KtNodeTypes.BOOLEAN_CONSTANT -> type.isBoolean()
KtNodeTypes.CHARACTER_CONSTANT -> type.isChar()
KtNodeTypes.INTEGER_CONSTANT -> {
if (text.endsWith("L")) {
type.isLong()
} else {
type.isInt()
}
}
KtNodeTypes.FLOAT_CONSTANT -> {
if (text.endsWith("f") || text.endsWith("F")) {
type.isFloat()
} else {
type.isDouble()
}
}
else -> false
}
}