diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt index 6b6c287953f..49a7128504b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt @@ -40,6 +40,7 @@ import com.intellij.psi.search.PsiSearchHelper import com.intellij.psi.search.PsiSearchHelper.SearchCostResult.* import com.intellij.psi.search.SearchScope import com.intellij.psi.search.searches.DefinitionsScopedSearch +import com.intellij.psi.search.searches.MethodReferencesSearch import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.refactoring.safeDelete.SafeDeleteHandler import org.jetbrains.kotlin.asJava.LightClassUtil @@ -243,7 +244,8 @@ class UnusedSymbolInspection : AbstractKotlinInspection() { } private fun hasReferences(declaration: KtNamedDeclaration, useScope: SearchScope): Boolean { - return !ReferencesSearch.search(declaration, useScope).forEach(fun (ref: PsiReference): Boolean { + + fun checkReference(ref: PsiReference): Boolean { if (declaration.isAncestor(ref.element)) return true // usages inside element's declaration are not counted if (ref.element.parent is KtValueArgumentName) return true // usage of parameter in form of named argument is not counted @@ -273,7 +275,16 @@ class UnusedSymbolInspection : AbstractKotlinInspection() { } return false - }) + } + + if (declaration is KtCallableDeclaration) { + val lightMethods = declaration.toLightMethods() + for (method in lightMethods) { + if (!MethodReferencesSearch.search(method).forEach(::checkReference)) return true + } + } + + return !ReferencesSearch.search(declaration, useScope).forEach(::checkReference) } private fun hasOverrides(declaration: KtNamedDeclaration, useScope: SearchScope): Boolean { @@ -347,7 +358,7 @@ class UnusedSymbolInspection : AbstractKotlinInspection() { } class SafeDeleteFix(declaration: KtDeclaration) : LocalQuickFix { - private val name = + private val name: String = if (declaration is KtConstructor<*>) "Safe delete constructor" else QuickFixBundle.message("safe.delete.text", declaration.name) diff --git a/idea/testData/inspections/unusedSymbol/function/delegatedSecondaryConstructor.kt b/idea/testData/inspections/unusedSymbol/function/delegatedSecondaryConstructor.kt new file mode 100644 index 00000000000..c1fd8b12aa8 --- /dev/null +++ b/idea/testData/inspections/unusedSymbol/function/delegatedSecondaryConstructor.kt @@ -0,0 +1,20 @@ +class CtorUsedByOtherCtor { + constructor() + + constructor(p: String): this() +} + +open class CtorUsedParent { + constructor() +} + +class CtorUsedChild : CtorUsedParent { + constructor() : super() +} + +@test.anno.EntryPoint +fun use(): Int { + val first = CtorUsedByOtherCtor("") + val second = CtorUsedChild(true) + return first.hashCode() + second.hashCode() +} \ No newline at end of file diff --git a/idea/testData/inspections/unusedSymbol/function/usedInJava.java b/idea/testData/inspections/unusedSymbol/function/usedInJava.java index fb1247d707a..c5cafd814aa 100644 --- a/idea/testData/inspections/unusedSymbol/function/usedInJava.java +++ b/idea/testData/inspections/unusedSymbol/function/usedInJava.java @@ -2,4 +2,9 @@ class RandomJavaClass { void g() { foo.UsedInJavaKt.usedInJava(); } + + public void context() { + foo.Ctor c = new foo.Ctor(0); + c.justCompare(); + } } \ No newline at end of file diff --git a/idea/testData/inspections/unusedSymbol/function/usedInJava.kt b/idea/testData/inspections/unusedSymbol/function/usedInJava.kt index 6b80f3da461..954c4c623a4 100644 --- a/idea/testData/inspections/unusedSymbol/function/usedInJava.kt +++ b/idea/testData/inspections/unusedSymbol/function/usedInJava.kt @@ -1,4 +1,9 @@ package foo fun usedInJava() { -} \ No newline at end of file +} + +class Ctor { + constructor(p: Int) + fun justCompare() {} +} diff --git a/idea/testData/quickfix/removeUnused/secondaryConstructorFromJava.before.Dependency.java b/idea/testData/quickfix/removeUnused/secondaryConstructorFromJava.before.Dependency.java new file mode 100644 index 00000000000..1c5c4b754c8 --- /dev/null +++ b/idea/testData/quickfix/removeUnused/secondaryConstructorFromJava.before.Dependency.java @@ -0,0 +1,6 @@ +class RandomJavaClass { + public void context() { + Ctor c = new Ctor(0); + c.justCompare(); + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/removeUnused/secondaryConstructorFromJava.before.Main.kt b/idea/testData/quickfix/removeUnused/secondaryConstructorFromJava.before.Main.kt new file mode 100644 index 00000000000..ec8a96e15a4 --- /dev/null +++ b/idea/testData/quickfix/removeUnused/secondaryConstructorFromJava.before.Main.kt @@ -0,0 +1,11 @@ +// "Safe delete constructor" "false" +// ACTION: Convert to primary constructor +// ACTION: Make internal +// ACTION: Make private +// ACTION: Make protected + +class Ctor { + constructor(p: Int) + + fun justCompare() {} +} diff --git a/idea/testData/quickfix/removeUnused/secondaryConstructorFromJavaDelegate.before.Dependency.java b/idea/testData/quickfix/removeUnused/secondaryConstructorFromJavaDelegate.before.Dependency.java new file mode 100644 index 00000000000..19f9d955e20 --- /dev/null +++ b/idea/testData/quickfix/removeUnused/secondaryConstructorFromJavaDelegate.before.Dependency.java @@ -0,0 +1,5 @@ +class RandomJavaClass extends Ctor { + RandomJavaClass() { + super(42); + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/removeUnused/secondaryConstructorFromJavaDelegate.before.Main.kt b/idea/testData/quickfix/removeUnused/secondaryConstructorFromJavaDelegate.before.Main.kt new file mode 100644 index 00000000000..79c4b24f3d0 --- /dev/null +++ b/idea/testData/quickfix/removeUnused/secondaryConstructorFromJavaDelegate.before.Main.kt @@ -0,0 +1,9 @@ +// "Safe delete constructor" "false" +// ACTION: Convert to primary constructor +// ACTION: Make internal +// ACTION: Make private +// ACTION: Make protected + +open class Ctor { + constructor(p: Int) +} diff --git a/idea/testData/quickfix/removeUnused/unusedDelegatedConstructor.kt b/idea/testData/quickfix/removeUnused/unusedDelegatedConstructor.kt new file mode 100644 index 00000000000..31901e73c5a --- /dev/null +++ b/idea/testData/quickfix/removeUnused/unusedDelegatedConstructor.kt @@ -0,0 +1,11 @@ +// "Safe delete constructor" "false" +// ACTION: Convert to primary constructor +// ACTION: Make internal +// ACTION: Make private +// ACTION: Make protected + +class CtorUsedByOtherCtor { + constructor() + + constructor(p: String): this() +} diff --git a/idea/testData/quickfix/removeUnused/unusedDelegatedConstructorSuper.kt b/idea/testData/quickfix/removeUnused/unusedDelegatedConstructorSuper.kt new file mode 100644 index 00000000000..44e011efa22 --- /dev/null +++ b/idea/testData/quickfix/removeUnused/unusedDelegatedConstructorSuper.kt @@ -0,0 +1,13 @@ +// "Safe delete constructor" "false" +// ACTION: Convert to primary constructor +// ACTION: Make internal +// ACTION: Make private +// ACTION: Make protected + +open class CtorUsedParent { + constructor() +} + +class CtorUsedChild : CtorUsedParent { + constructor() : super() +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java index 4e86d3df4e2..61ade1516c3 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -1831,6 +1831,18 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes doTestWithExtraFile(fileName); } + @TestMetadata("secondaryConstructorFromJava.before.Main.kt") + public void testSecondaryConstructorFromJava() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/removeUnused/secondaryConstructorFromJava.before.Main.kt"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("secondaryConstructorFromJavaDelegate.before.Main.kt") + public void testSecondaryConstructorFromJavaDelegate() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/removeUnused/secondaryConstructorFromJavaDelegate.before.Main.kt"); + doTestWithExtraFile(fileName); + } + @TestMetadata("usedObjectAsAliasMulti.before.Main.kt") public void testUsedObjectAsAliasMulti() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/removeUnused/usedObjectAsAliasMulti.before.Main.kt"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 3b9e48e4fb9..e47ba46b216 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -7524,6 +7524,18 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("unusedDelegatedConstructor.kt") + public void testUnusedDelegatedConstructor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/removeUnused/unusedDelegatedConstructor.kt"); + doTest(fileName); + } + + @TestMetadata("unusedDelegatedConstructorSuper.kt") + public void testUnusedDelegatedConstructorSuper() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/removeUnused/unusedDelegatedConstructorSuper.kt"); + doTest(fileName); + } + @TestMetadata("unusedFunction.kt") public void testUnusedFunction() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/removeUnused/unusedFunction.kt");