diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt
index 8dad5c64acd..8e1b16fa291 100644
--- a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt
@@ -52,6 +52,12 @@ import org.jetbrains.kotlin.idea.search.usagesSearch.dataClassComponentFunctionN
import org.jetbrains.kotlin.psi.JetTypeParameter
import org.jetbrains.kotlin.idea.search.usagesSearch.DefaultSearchHelper
import com.intellij.util.Processor
+import com.intellij.codeInspection.LocalQuickFix
+import com.intellij.openapi.project.Project
+import com.intellij.codeInspection.ProblemDescriptor
+import com.intellij.codeInsight.daemon.QuickFixBundle
+import com.intellij.codeInsight.FileModificationService
+import com.intellij.refactoring.safeDelete.SafeDeleteHandler
public class UnusedSymbolInspection : AbstractKotlinInspection() {
private val javaInspection = UnusedDeclarationInspection()
@@ -60,6 +66,19 @@ public class UnusedSymbolInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : JetVisitorVoid() {
+ private fun createQuickFix(declaration: JetNamedDeclaration): LocalQuickFix {
+ return object : LocalQuickFix {
+ override fun getName() = QuickFixBundle.message("safe.delete.text", declaration.getName())
+
+ override fun getFamilyName() = "whatever"
+
+ override fun applyFix(project: Project, descriptor: ProblemDescriptor?) {
+ if (!FileModificationService.getInstance().prepareFileForWrite(declaration.getContainingFile())) return
+ SafeDeleteHandler.invoke(project, array(declaration), false)
+ }
+ }
+ }
+
override fun visitClass(klass: JetClass) {
if (klass.getName() == null) return
@@ -72,8 +91,9 @@ public class UnusedSymbolInspection : AbstractKotlinInspection() {
holder.registerProblem(
klass.getNameIdentifier(),
JetBundle.message("unused.class", klass.getName()),
- ProblemHighlightType.LIKE_UNUSED_SYMBOL
- ) // TODO add quick fix to delete it
+ ProblemHighlightType.LIKE_UNUSED_SYMBOL,
+ createQuickFix(klass)
+ )
}
override fun visitNamedFunction(function: JetNamedFunction) {
@@ -87,8 +107,9 @@ public class UnusedSymbolInspection : AbstractKotlinInspection() {
holder.registerProblem(
function.getNameIdentifier(),
JetBundle.message("unused.function", function.getName()),
- ProblemHighlightType.LIKE_UNUSED_SYMBOL
- ) // TODO add quick fix to delete it
+ ProblemHighlightType.LIKE_UNUSED_SYMBOL,
+ createQuickFix(function)
+ )
}
override fun visitProperty(property: JetProperty) {
@@ -102,8 +123,9 @@ public class UnusedSymbolInspection : AbstractKotlinInspection() {
holder.registerProblem(
property.getNameIdentifier(),
JetBundle.message("unused.property", property.getName()),
- ProblemHighlightType.LIKE_UNUSED_SYMBOL
- ) // TODO add quick fix to delete it
+ ProblemHighlightType.LIKE_UNUSED_SYMBOL,
+ createQuickFix(property)
+ )
}
override fun visitParameter(parameter: JetParameter) {
@@ -120,8 +142,9 @@ public class UnusedSymbolInspection : AbstractKotlinInspection() {
holder.registerProblem(
parameter.getNameIdentifier(),
JetBundle.message("unused.property", parameter.getName()),
- ProblemHighlightType.LIKE_UNUSED_SYMBOL
- ) // TODO add quick fix to delete it
+ ProblemHighlightType.LIKE_UNUSED_SYMBOL,
+ createQuickFix(parameter)
+ )
}
override fun visitTypeParameter(parameter: JetTypeParameter) {
@@ -132,8 +155,9 @@ public class UnusedSymbolInspection : AbstractKotlinInspection() {
holder.registerProblem(
parameter.getNameIdentifier(),
JetBundle.message("unused.type.parameter", parameter.getName()),
- ProblemHighlightType.LIKE_UNUSED_SYMBOL
- ) // TODO add quick fix to delete it
+ ProblemHighlightType.LIKE_UNUSED_SYMBOL,
+ createQuickFix(parameter)
+ )
}
}
}
@@ -200,4 +224,4 @@ public class UnusedSymbolInspection : AbstractKotlinInspection() {
return !query.forEach(Processor { declaration.isAncestor(it.getElement()) })
}
-}
\ No newline at end of file
+}
diff --git a/idea/testData/quickfix/removeUnused/.inspection b/idea/testData/quickfix/removeUnused/.inspection
new file mode 100644
index 00000000000..92d81ff196a
--- /dev/null
+++ b/idea/testData/quickfix/removeUnused/.inspection
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.inspections.UnusedSymbolInspection
\ No newline at end of file
diff --git a/idea/testData/quickfix/removeUnused/afterUnusedClass.kt b/idea/testData/quickfix/removeUnused/afterUnusedClass.kt
new file mode 100644
index 00000000000..88b655b4048
--- /dev/null
+++ b/idea/testData/quickfix/removeUnused/afterUnusedClass.kt
@@ -0,0 +1,3 @@
+// "Safe delete 'UnusedClass'" "true"
+class DummyClass
+
diff --git a/idea/testData/quickfix/removeUnused/afterUnusedFunction.kt b/idea/testData/quickfix/removeUnused/afterUnusedFunction.kt
new file mode 100644
index 00000000000..972b055f57c
--- /dev/null
+++ b/idea/testData/quickfix/removeUnused/afterUnusedFunction.kt
@@ -0,0 +1,4 @@
+// "Safe delete 'unusedFun'" "true"
+fun dummy() {
+}
+
diff --git a/idea/testData/quickfix/removeUnused/afterUnusedProperty.kt b/idea/testData/quickfix/removeUnused/afterUnusedProperty.kt
new file mode 100644
index 00000000000..8489c295e4f
--- /dev/null
+++ b/idea/testData/quickfix/removeUnused/afterUnusedProperty.kt
@@ -0,0 +1,3 @@
+// "Safe delete 'property'" "true"
+class UnusedProperty() {
+}
diff --git a/idea/testData/quickfix/removeUnused/afterUnusedPropertyAsConstructorParameter.kt b/idea/testData/quickfix/removeUnused/afterUnusedPropertyAsConstructorParameter.kt
new file mode 100644
index 00000000000..642ef76bdd0
--- /dev/null
+++ b/idea/testData/quickfix/removeUnused/afterUnusedPropertyAsConstructorParameter.kt
@@ -0,0 +1,2 @@
+// "Safe delete 'property'" "true"
+class UnusedPropertyAsConstructorParameter()
diff --git a/idea/testData/quickfix/removeUnused/afterUnusedTypeParameter.kt b/idea/testData/quickfix/removeUnused/afterUnusedTypeParameter.kt
new file mode 100644
index 00000000000..75b2e2cf04b
--- /dev/null
+++ b/idea/testData/quickfix/removeUnused/afterUnusedTypeParameter.kt
@@ -0,0 +1,2 @@
+// "Safe delete 'T'" "true"
+class UnusedTypeParameter
(val p: P)
diff --git a/idea/testData/quickfix/removeUnused/beforeUnusedClass.kt b/idea/testData/quickfix/removeUnused/beforeUnusedClass.kt
new file mode 100644
index 00000000000..ae78f7de9ed
--- /dev/null
+++ b/idea/testData/quickfix/removeUnused/beforeUnusedClass.kt
@@ -0,0 +1,4 @@
+// "Safe delete 'UnusedClass'" "true"
+class DummyClass
+
+class UnusedClass
diff --git a/idea/testData/quickfix/removeUnused/beforeUnusedFunction.kt b/idea/testData/quickfix/removeUnused/beforeUnusedFunction.kt
new file mode 100644
index 00000000000..b35f9b9b61c
--- /dev/null
+++ b/idea/testData/quickfix/removeUnused/beforeUnusedFunction.kt
@@ -0,0 +1,7 @@
+// "Safe delete 'unusedFun'" "true"
+fun dummy() {
+}
+
+fun unusedFun() {
+
+}
diff --git a/idea/testData/quickfix/removeUnused/beforeUnusedProperty.kt b/idea/testData/quickfix/removeUnused/beforeUnusedProperty.kt
new file mode 100644
index 00000000000..beb08549d00
--- /dev/null
+++ b/idea/testData/quickfix/removeUnused/beforeUnusedProperty.kt
@@ -0,0 +1,4 @@
+// "Safe delete 'property'" "true"
+class UnusedProperty() {
+ val property: String = ":)"
+}
diff --git a/idea/testData/quickfix/removeUnused/beforeUnusedPropertyAsConstructorParameter.kt b/idea/testData/quickfix/removeUnused/beforeUnusedPropertyAsConstructorParameter.kt
new file mode 100644
index 00000000000..887caff8028
--- /dev/null
+++ b/idea/testData/quickfix/removeUnused/beforeUnusedPropertyAsConstructorParameter.kt
@@ -0,0 +1,2 @@
+// "Safe delete 'property'" "true"
+class UnusedPropertyAsConstructorParameter(val property: String)
diff --git a/idea/testData/quickfix/removeUnused/beforeUnusedTypeParameter.kt b/idea/testData/quickfix/removeUnused/beforeUnusedTypeParameter.kt
new file mode 100644
index 00000000000..27a969798fa
--- /dev/null
+++ b/idea/testData/quickfix/removeUnused/beforeUnusedTypeParameter.kt
@@ -0,0 +1,2 @@
+// "Safe delete 'T'" "true"
+class UnusedTypeParameter<T, P>(val p: P)
diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/AbstractQuickFixTest.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/AbstractQuickFixTest.java
index 3d7faa943bf..a5482d59775 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/AbstractQuickFixTest.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/AbstractQuickFixTest.java
@@ -26,9 +26,11 @@ import com.intellij.openapi.projectRoots.JavaSdk;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.io.FileUtil;
import com.intellij.psi.PsiElement;
import org.apache.commons.lang.SystemUtils;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.idea.DirectiveBasedActionUtils;
import org.jetbrains.kotlin.idea.KotlinLightQuickFixTestCase;
import org.jetbrains.kotlin.idea.PluginTestCaseBase;
@@ -39,6 +41,7 @@ import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.Assert;
import java.io.File;
+import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -50,6 +53,19 @@ public abstract class AbstractQuickFixTest extends KotlinLightQuickFixTestCase {
((StartupManagerImpl) StartupManager.getInstance(getProject())).runPostStartupActivities();
}
+ @Nullable
+ private static File findInspectionFile(@NotNull File startDir) {
+ File currentDir = startDir;
+ while (currentDir != null) {
+ File inspectionFile = new File(currentDir, ".inspection");
+ if (inspectionFile.exists()) {
+ return inspectionFile;
+ }
+ currentDir = currentDir.getParentFile();
+ }
+ return null;
+ }
+
protected void doTest(@NotNull String beforeFileName) throws Exception {
boolean isWithRuntime = beforeFileName.endsWith("Runtime.kt");
@@ -58,6 +74,8 @@ public abstract class AbstractQuickFixTest extends KotlinLightQuickFixTestCase {
ConfigLibraryUtil.configureKotlinRuntime(getModule(), getFullJavaJDK());
}
+ enableInspections(beforeFileName);
+
doSingleTest(getTestName(false) + ".kt");
checkForUnexpectedActions();
checkForUnexpectedErrors();
@@ -69,6 +87,15 @@ public abstract class AbstractQuickFixTest extends KotlinLightQuickFixTestCase {
}
}
+ private void enableInspections(String beforeFileName) throws IOException, ClassNotFoundException {
+ File inspectionFile = findInspectionFile(new File(beforeFileName).getParentFile());
+ if (inspectionFile != null) {
+ String className = FileUtil.loadFile(inspectionFile).trim();
+ Class> inspectionClass = Class.forName(className);
+ enableInspectionTools(inspectionClass);
+ }
+ }
+
private void checkForUnexpectedActions() throws ClassNotFoundException {
String text = getEditor().getDocument().getText();
Pair pair = parseActionHintImpl(getFile(), text);
diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java
index 910da86bd69..a024a93e502 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java
@@ -30,7 +30,7 @@ import java.util.regex.Pattern;
@SuppressWarnings("all")
@TestMetadata("idea/testData/quickfix")
@TestDataPath("$PROJECT_ROOT")
-@InnerTestClasses({QuickFixTestGenerated.Abstract.class, QuickFixTestGenerated.AddStarProjections.class, QuickFixTestGenerated.AutoImports.class, QuickFixTestGenerated.ChangeSignature.class, QuickFixTestGenerated.CheckArguments.class, QuickFixTestGenerated.ConflictingImports.class, QuickFixTestGenerated.CreateFromUsage.class, QuickFixTestGenerated.Expressions.class, QuickFixTestGenerated.Migration.class, QuickFixTestGenerated.Modifiers.class, QuickFixTestGenerated.Nullables.class, QuickFixTestGenerated.Override.class, QuickFixTestGenerated.PlatformClasses.class, QuickFixTestGenerated.Supercalls.class, QuickFixTestGenerated.SupertypeInitialization.class, QuickFixTestGenerated.Suppress.class, QuickFixTestGenerated.TypeAddition.class, QuickFixTestGenerated.TypeImports.class, QuickFixTestGenerated.TypeMismatch.class, QuickFixTestGenerated.TypeProjection.class, QuickFixTestGenerated.Variables.class, QuickFixTestGenerated.When.class})
+@InnerTestClasses({QuickFixTestGenerated.Abstract.class, QuickFixTestGenerated.AddStarProjections.class, QuickFixTestGenerated.AutoImports.class, QuickFixTestGenerated.ChangeSignature.class, QuickFixTestGenerated.CheckArguments.class, QuickFixTestGenerated.ConflictingImports.class, QuickFixTestGenerated.CreateFromUsage.class, QuickFixTestGenerated.Expressions.class, QuickFixTestGenerated.Migration.class, QuickFixTestGenerated.Modifiers.class, QuickFixTestGenerated.Nullables.class, QuickFixTestGenerated.Override.class, QuickFixTestGenerated.PlatformClasses.class, QuickFixTestGenerated.RemoveUnused.class, QuickFixTestGenerated.Supercalls.class, QuickFixTestGenerated.SupertypeInitialization.class, QuickFixTestGenerated.Suppress.class, QuickFixTestGenerated.TypeAddition.class, QuickFixTestGenerated.TypeImports.class, QuickFixTestGenerated.TypeMismatch.class, QuickFixTestGenerated.TypeProjection.class, QuickFixTestGenerated.Variables.class, QuickFixTestGenerated.When.class})
@RunWith(JUnit3RunnerWithInners.class)
public class QuickFixTestGenerated extends AbstractQuickFixTest {
public void testAllFilesPresentInQuickfix() throws Exception {
@@ -3423,6 +3423,45 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
}
}
+ @TestMetadata("idea/testData/quickfix/removeUnused")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class RemoveUnused extends AbstractQuickFixTest {
+ public void testAllFilesPresentInRemoveUnused() throws Exception {
+ JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/removeUnused"), Pattern.compile("^before(\\w+)\\.kt$"), true);
+ }
+
+ @TestMetadata("beforeUnusedClass.kt")
+ public void testUnusedClass() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/removeUnused/beforeUnusedClass.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("beforeUnusedFunction.kt")
+ public void testUnusedFunction() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/removeUnused/beforeUnusedFunction.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("beforeUnusedProperty.kt")
+ public void testUnusedProperty() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/removeUnused/beforeUnusedProperty.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("beforeUnusedPropertyAsConstructorParameter.kt")
+ public void testUnusedPropertyAsConstructorParameter() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/removeUnused/beforeUnusedPropertyAsConstructorParameter.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("beforeUnusedTypeParameter.kt")
+ public void testUnusedTypeParameter() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/removeUnused/beforeUnusedTypeParameter.kt");
+ doTest(fileName);
+ }
+ }
+
@TestMetadata("idea/testData/quickfix/supercalls")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)