diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties index 03a924bd175..6d54b77a516 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties @@ -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 diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt index 0fbbbbc6590..4bbc03b537c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt @@ -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 = when (declaration) { is JetClass -> ClassUsagesSearchHelper(constructorUsages = true, nonConstructorUsages = true, skipImports = true) + is JetNamedFunction -> FunctionUsagesSearchHelper(skipImports = true) else -> return false } diff --git a/idea/testData/inspections/unusedSymbol/class/localUnused.kt b/idea/testData/inspections/unusedSymbol/class/localUnused.kt index 2b0a47d01ca..44953a01a74 100644 --- a/idea/testData/inspections/unusedSymbol/class/localUnused.kt +++ b/idea/testData/inspections/unusedSymbol/class/localUnused.kt @@ -1,4 +1,4 @@ -fun f() { +fun main(args: Array) { class Local { } } \ No newline at end of file diff --git a/idea/testData/inspections/unusedSymbol/class/localUsed.kt b/idea/testData/inspections/unusedSymbol/class/localUsed.kt index b56df51cf92..8e8017f02c8 100644 --- a/idea/testData/inspections/unusedSymbol/class/localUsed.kt +++ b/idea/testData/inspections/unusedSymbol/class/localUsed.kt @@ -1,4 +1,4 @@ -fun f() { +fun main(args: Array) { class Local { } diff --git a/idea/testData/inspections/unusedSymbol/class/used.kt b/idea/testData/inspections/unusedSymbol/class/used.kt index f6cbea3ef5f..aca3ec5fd9a 100644 --- a/idea/testData/inspections/unusedSymbol/class/used.kt +++ b/idea/testData/inspections/unusedSymbol/class/used.kt @@ -1,4 +1,5 @@ class A -fun f(): A { +fun main(args: Array) { + val x: A? = null } \ No newline at end of file diff --git a/idea/testData/inspections/unusedSymbol/class/usedOnlyInside.kt b/idea/testData/inspections/unusedSymbol/class/usedOnlyInside.kt index ef575ae9d85..78de9e90a7f 100644 --- a/idea/testData/inspections/unusedSymbol/class/usedOnlyInside.kt +++ b/idea/testData/inspections/unusedSymbol/class/usedOnlyInside.kt @@ -1,4 +1,3 @@ class A { - fun f(other: A) { - } + val x: A? = null } \ No newline at end of file diff --git a/idea/testData/inspections/unusedSymbol/function/classMemberInheritedUsed.kt b/idea/testData/inspections/unusedSymbol/function/classMemberInheritedUsed.kt new file mode 100644 index 00000000000..126b582b1a3 --- /dev/null +++ b/idea/testData/inspections/unusedSymbol/function/classMemberInheritedUsed.kt @@ -0,0 +1,10 @@ +open class Klass { + fun used() { + } +} + +class Subklass: Klass() + +fun main(args: Array) { + Subklass().used() +} \ No newline at end of file diff --git a/idea/testData/inspections/unusedSymbol/function/classMemberOverriddenUsed.kt b/idea/testData/inspections/unusedSymbol/function/classMemberOverriddenUsed.kt new file mode 100644 index 00000000000..25dd07153c3 --- /dev/null +++ b/idea/testData/inspections/unusedSymbol/function/classMemberOverriddenUsed.kt @@ -0,0 +1,13 @@ +open class Klass { + open fun used() { + } +} + +class Subklass: Klass() { + override fun used() { + } +} + +fun main(args: Array) { + Subklass().used() +} \ No newline at end of file diff --git a/idea/testData/inspections/unusedSymbol/function/classMemberOverride.kt b/idea/testData/inspections/unusedSymbol/function/classMemberOverride.kt new file mode 100644 index 00000000000..3b3639683b7 --- /dev/null +++ b/idea/testData/inspections/unusedSymbol/function/classMemberOverride.kt @@ -0,0 +1,9 @@ +class Klass: Iterable { + override fun iterator(): Iterator { + throw UnsupportedOperationException() + } +} + +fun main(args: Array) { + Klass() +} diff --git a/idea/testData/inspections/unusedSymbol/function/classMemberUnused.kt b/idea/testData/inspections/unusedSymbol/function/classMemberUnused.kt new file mode 100644 index 00000000000..e711c7d8f5a --- /dev/null +++ b/idea/testData/inspections/unusedSymbol/function/classMemberUnused.kt @@ -0,0 +1,8 @@ +class Klass { + fun unused() { + } +} + +fun main(args: Array) { + Klass() +} \ No newline at end of file diff --git a/idea/testData/inspections/unusedSymbol/function/classMemberUsed.kt b/idea/testData/inspections/unusedSymbol/function/classMemberUsed.kt new file mode 100644 index 00000000000..4649f141d51 --- /dev/null +++ b/idea/testData/inspections/unusedSymbol/function/classMemberUsed.kt @@ -0,0 +1,8 @@ +class Klass { + fun used() { + } +} + +fun main(args: Array) { + Klass().used() +} \ No newline at end of file diff --git a/idea/testData/inspections/unusedSymbol/function/inspectionData/expected.xml b/idea/testData/inspections/unusedSymbol/function/inspectionData/expected.xml new file mode 100644 index 00000000000..94b24f0cec5 --- /dev/null +++ b/idea/testData/inspections/unusedSymbol/function/inspectionData/expected.xml @@ -0,0 +1,54 @@ + + + topLevelUnused.kt + 1 + light_idea_test_case + + Unused Symbol + Function 'unused' is never used + + + + topLevelOnlyInImport.kt + 5 + light_idea_test_case + + Unused Symbol + Function 'onlyInImport' is never used + + + + local.kt + 2 + light_idea_test_case + + Unused Symbol + Function 'local' is never used + + + local.kt + 1 + light_idea_test_case + + Unused Symbol + Function 'outer' is never used + + + + usedOnlyRecursively.kt + 1 + light_idea_test_case + + Unused Symbol + Function 'usedOnlyRecursively' is never used + + + + classMemberUnused.kt + 2 + light_idea_test_case + + Unused Symbol + Function 'unused' is never used + + diff --git a/idea/testData/inspections/unusedSymbol/function/inspectionData/inspections.test b/idea/testData/inspections/unusedSymbol/function/inspectionData/inspections.test new file mode 100644 index 00000000000..02aea1c357e --- /dev/null +++ b/idea/testData/inspections/unusedSymbol/function/inspectionData/inspections.test @@ -0,0 +1 @@ +// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.UnusedSymbolInspection diff --git a/idea/testData/inspections/unusedSymbol/function/local.kt b/idea/testData/inspections/unusedSymbol/function/local.kt new file mode 100644 index 00000000000..007aa90f7ec --- /dev/null +++ b/idea/testData/inspections/unusedSymbol/function/local.kt @@ -0,0 +1,5 @@ +fun outer() { + fun local() { + + } +} diff --git a/idea/testData/inspections/unusedSymbol/function/topLevelMain.kt b/idea/testData/inspections/unusedSymbol/function/topLevelMain.kt new file mode 100644 index 00000000000..adb5b33c573 --- /dev/null +++ b/idea/testData/inspections/unusedSymbol/function/topLevelMain.kt @@ -0,0 +1,3 @@ +fun main(args: Array) { + +} \ No newline at end of file diff --git a/idea/testData/inspections/unusedSymbol/function/topLevelOnlyInImport.kt b/idea/testData/inspections/unusedSymbol/function/topLevelOnlyInImport.kt new file mode 100644 index 00000000000..4171bd3641f --- /dev/null +++ b/idea/testData/inspections/unusedSymbol/function/topLevelOnlyInImport.kt @@ -0,0 +1,7 @@ +package foo + +import foo.onlyInImport + +fun onlyInImport() { + +} diff --git a/idea/testData/inspections/unusedSymbol/function/topLevelUnused.kt b/idea/testData/inspections/unusedSymbol/function/topLevelUnused.kt new file mode 100644 index 00000000000..d7c611df34c --- /dev/null +++ b/idea/testData/inspections/unusedSymbol/function/topLevelUnused.kt @@ -0,0 +1,3 @@ +fun unused() { + +} diff --git a/idea/testData/inspections/unusedSymbol/function/topLevelUsed.kt b/idea/testData/inspections/unusedSymbol/function/topLevelUsed.kt new file mode 100644 index 00000000000..98b39cffee6 --- /dev/null +++ b/idea/testData/inspections/unusedSymbol/function/topLevelUsed.kt @@ -0,0 +1,7 @@ +fun used() { + +} + +fun main(args: Array) { + used() +} \ No newline at end of file diff --git a/idea/testData/inspections/unusedSymbol/function/usedOnlyRecursively.kt b/idea/testData/inspections/unusedSymbol/function/usedOnlyRecursively.kt new file mode 100644 index 00000000000..87243a9dc18 --- /dev/null +++ b/idea/testData/inspections/unusedSymbol/function/usedOnlyRecursively.kt @@ -0,0 +1,3 @@ +fun usedOnlyRecursively() { + usedOnlyRecursively() +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/JetInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/JetInspectionTestGenerated.java index 3e1cbeba355..dbafe700f5f 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/JetInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/JetInspectionTestGenerated.java @@ -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); + } } }