Finding unused functions.

This commit is contained in:
Evgeny Gerashchenko
2014-12-18 20:35:54 +03:00
parent 38044c1000
commit f652d314d8
20 changed files with 167 additions and 10 deletions
@@ -118,6 +118,7 @@ goto.super.property.chooser.title=Choose super property
goto.super.class.chooser.title=Choose super class or interface
unused.class=Class ''{0}'' is never used
unused.function=Function ''{0}'' is never used
livetemplate.description.main=main() function
livetemplate.description.soutp=Prints function parameter names and values to System.out
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearch
import org.jetbrains.kotlin.idea.JetBundle
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindClassUsagesHandler
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.psi.JetNamedDeclaration
@@ -49,7 +50,7 @@ public class UnusedSymbolInspection : AbstractKotlinInspection() {
override fun visitClass(klass: JetClass) {
if (klass.getName() == null) return
if (classIsEntryPoint(klass)) return
if (isEntryPoint(klass)) return
if (hasNonTrivialUsages(klass)) return
if (classHasTextUsages(klass)) return
@@ -59,13 +60,30 @@ public class UnusedSymbolInspection : AbstractKotlinInspection() {
ProblemHighlightType.LIKE_UNUSED_SYMBOL
) // TODO add quick fix to delete it
}
override fun visitNamedFunction(function: JetNamedFunction) {
if (function.getName() == null) return
if (function.hasModifier(JetTokens.OVERRIDE_KEYWORD)) return
if (isEntryPoint(function)) return
if (hasNonTrivialUsages(function)) return
holder.registerProblem(
function.getNameIdentifier(),
JetBundle.message("unused.function", function.getName()),
ProblemHighlightType.LIKE_UNUSED_SYMBOL
) // TODO add quick fix to delete it
}
}
}
private fun classIsEntryPoint(klass: JetClass): Boolean {
val lightClass = klass.toLightClass()
if (lightClass != null && javaInspection.isEntryPoint(lightClass)) return true
return false
private fun isEntryPoint(declaration: JetNamedDeclaration): Boolean {
val lightElement: PsiElement? = when (declaration) {
is JetClass -> declaration.toLightClass()
is JetNamedFunction -> LightClassUtil.getLightClassMethod(declaration)
else -> null
}
return lightElement != null && javaInspection.isEntryPoint(lightElement)
}
private fun classHasTextUsages(klass: JetClass): Boolean {
@@ -88,6 +106,7 @@ public class UnusedSymbolInspection : AbstractKotlinInspection() {
private fun hasNonTrivialUsages(declaration: JetNamedDeclaration): Boolean {
val searchHelper: UsagesSearchHelper<out JetNamedDeclaration> = when (declaration) {
is JetClass -> ClassUsagesSearchHelper(constructorUsages = true, nonConstructorUsages = true, skipImports = true)
is JetNamedFunction -> FunctionUsagesSearchHelper(skipImports = true)
else -> return false
}
@@ -1,4 +1,4 @@
fun f() {
fun main(args: Array<String>) {
class Local {
}
}
@@ -1,4 +1,4 @@
fun f() {
fun main(args: Array<String>) {
class Local {
}
@@ -1,4 +1,5 @@
class A
fun f(): A {
fun main(args: Array<String>) {
val x: A? = null
}
@@ -1,4 +1,3 @@
class A {
fun f(other: A) {
}
val x: A? = null
}
@@ -0,0 +1,10 @@
open class Klass {
fun used() {
}
}
class Subklass: Klass()
fun main(args: Array<String>) {
Subklass().used()
}
@@ -0,0 +1,13 @@
open class Klass {
open fun used() {
}
}
class Subklass: Klass() {
override fun used() {
}
}
fun main(args: Array<String>) {
Subklass().used()
}
@@ -0,0 +1,9 @@
class Klass: Iterable<String> {
override fun iterator(): Iterator<String> {
throw UnsupportedOperationException()
}
}
fun main(args: Array<String>) {
Klass()
}
@@ -0,0 +1,8 @@
class Klass {
fun unused() {
}
}
fun main(args: Array<String>) {
Klass()
}
@@ -0,0 +1,8 @@
class Klass {
fun used() {
}
}
fun main(args: Array<String>) {
Klass().used()
}
@@ -0,0 +1,54 @@
<problems>
<problem>
<file>topLevelUnused.kt</file>
<line>1</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/topLevelUnused.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Unused Symbol</problem_class>
<description>Function 'unused' is never used</description>
</problem>
<problem>
<file>topLevelOnlyInImport.kt</file>
<line>5</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/topLevelOnlyInImport.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Unused Symbol</problem_class>
<description>Function 'onlyInImport' is never used</description>
</problem>
<problem>
<file>local.kt</file>
<line>2</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/local.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Unused Symbol</problem_class>
<description>Function 'local' is never used</description>
</problem>
<problem>
<file>local.kt</file>
<line>1</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/local.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Unused Symbol</problem_class>
<description>Function 'outer' is never used</description>
</problem>
<problem>
<file>usedOnlyRecursively.kt</file>
<line>1</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/usedOnlyRecursively.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Unused Symbol</problem_class>
<description>Function 'usedOnlyRecursively' is never used</description>
</problem>
<problem>
<file>classMemberUnused.kt</file>
<line>2</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/classMemberUnused.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Unused Symbol</problem_class>
<description>Function 'unused' is never used</description>
</problem>
</problems>
@@ -0,0 +1 @@
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.UnusedSymbolInspection
@@ -0,0 +1,5 @@
fun outer() {
fun local() {
}
}
@@ -0,0 +1,3 @@
fun main(args: Array<String>) {
}
@@ -0,0 +1,7 @@
package foo
import foo.onlyInImport
fun onlyInImport() {
}
@@ -0,0 +1,3 @@
fun unused() {
}
@@ -0,0 +1,7 @@
fun used() {
}
fun main(args: Array<String>) {
used()
}
@@ -0,0 +1,3 @@
fun usedOnlyRecursively() {
usedOnlyRecursively()
}
@@ -83,5 +83,11 @@ public class JetInspectionTestGenerated extends AbstractJetInspectionTest {
String fileName = JetTestUtils.navigationMetadata("idea/testData/inspections/unusedSymbol/class/inspectionData/inspections.test");
doTest(fileName);
}
@TestMetadata("unusedSymbol/function/inspectionData/inspections.test")
public void testUnusedSymbol_function_inspectionData_Inspections_test() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/inspections/unusedSymbol/function/inspectionData/inspections.test");
doTest(fileName);
}
}
}