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
|
package com.github.shyiko.ktlint.ruleset.standard
import com.github.shyiko.ktlint.core.Rule
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.kdoc.lexer.KDocTokens
import org.jetbrains.kotlin.kdoc.psi.impl.KDocLink
import org.jetbrains.kotlin.psi.KtImportDirective
import org.jetbrains.kotlin.psi.KtPackageDirective
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
class NoUnusedImportsRule : Rule("no-unused-imports") {
private val operatorSet = setOf(
// unary
"unaryPlus", "unaryMinus", "not",
// inc/dec
"inc", "dec",
// arithmetic
"plus", "minus", "times", "div", "rem", "mod", "rangeTo",
// in
"contains",
// indexed access
"get", "set",
// invoke
"invoke",
// augmented assignments
"plusAssign", "minusAssign", "timesAssign", "divAssign", "modAssign",
// (in)equality
"equals",
// comparison
"compareTo",
// iteration (https://github.com/shyiko/ktlint/issues/40)
"iterator",
// by (https://github.com/shyiko/ktlint/issues/54)
"getValue", "setValue",
// destructuring assignment
"component1", "component2", "component3", "component4", "component5"
)
private val ref = mutableSetOf("*")
private var packageName = ""
override fun visit(node: ASTNode, autoCorrect: Boolean,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit) {
if (node.elementType == KtStubElementTypes.FILE) {
node.visit { vnode ->
val psi = vnode.psi
val type = vnode.elementType
if (type == KDocTokens.MARKDOWN_LINK && psi is KDocLink) {
val linkText = psi.getLinkText().replace("`", "")
ref.add(linkText.split('.').first())
} else
if ((type == KtNodeTypes.REFERENCE_EXPRESSION || type == KtNodeTypes.OPERATION_REFERENCE) &&
!psi.isPartOf(KtImportDirective::class)) {
ref.add(vnode.text.trim('`'))
}
}
} else
if (node.elementType == KtStubElementTypes.PACKAGE_DIRECTIVE) {
val packageDirective = node.psi as KtPackageDirective
packageName = packageDirective.qualifiedName
} else
if (node.elementType == KtStubElementTypes.IMPORT_DIRECTIVE) {
val importDirective = node.psi as KtImportDirective
val name = importDirective.importPath?.importedName?.asString()
val importPath = importDirective.importPath?.pathStr!!
if (importDirective.aliasName == null &&
importPath.startsWith(packageName) &&
importPath.substring(packageName.length + 1).indexOf('.') == -1) {
emit(importDirective.startOffset, "Unnecessary import", true)
if (autoCorrect) {
importDirective.delete()
}
} else
if (name != null && !ref.contains(name) && !operatorSet.contains(name)) {
emit(importDirective.startOffset, "Unused import", true)
if (autoCorrect) {
importDirective.delete()
}
}
}
}
private fun ASTNode.visit(cb: (node: ASTNode) -> Unit) {
cb(this)
this.getChildren(null).forEach { it.visit(cb) }
}
}
|