Quick fix for unused symbol inspection.

This commit is contained in:
Evgeny Gerashchenko
2015-01-21 12:38:30 +03:00
parent 6e621ab114
commit 378d00bbad
14 changed files with 136 additions and 12 deletions
@@ -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()) })
}
}
}
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.inspections.UnusedSymbolInspection
@@ -0,0 +1,3 @@
// "Safe delete 'UnusedClass'" "true"
class DummyClass
@@ -0,0 +1,4 @@
// "Safe delete 'unusedFun'" "true"
fun dummy() {
}
@@ -0,0 +1,3 @@
// "Safe delete 'property'" "true"
class UnusedProperty() {
}
@@ -0,0 +1,2 @@
// "Safe delete 'property'" "true"
class UnusedPropertyAsConstructorParameter()
@@ -0,0 +1,2 @@
// "Safe delete 'T'" "true"
class UnusedTypeParameter<P>(val p: P)
@@ -0,0 +1,4 @@
// "Safe delete 'UnusedClass'" "true"
class DummyClass
class <caret>UnusedClass
@@ -0,0 +1,7 @@
// "Safe delete 'unusedFun'" "true"
fun dummy() {
}
fun <caret>unusedFun() {
}
@@ -0,0 +1,4 @@
// "Safe delete 'property'" "true"
class UnusedProperty() {
val <caret>property: String = ":)"
}
@@ -0,0 +1,2 @@
// "Safe delete 'property'" "true"
class UnusedPropertyAsConstructorParameter(val <caret>property: String)
@@ -0,0 +1,2 @@
// "Safe delete 'T'" "true"
class UnusedTypeParameter<<caret>T, P>(val p: P)
@@ -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<String, Boolean> pair = parseActionHintImpl(getFile(), text);
@@ -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)