KT-11576 Unused symbol warning: add quickfix for annotated declarations
#KT-11576 Fixed
This commit is contained in:
@@ -16,9 +16,11 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.inspections
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil
|
||||
import com.intellij.codeInsight.FileModificationService
|
||||
import com.intellij.codeInsight.daemon.QuickFixBundle
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil
|
||||
import com.intellij.codeInsight.intention.QuickFixFactory
|
||||
import com.intellij.codeInspection.*
|
||||
import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection
|
||||
import com.intellij.codeInspection.ex.EntryPointsManager
|
||||
@@ -26,6 +28,7 @@ import com.intellij.codeInspection.ex.EntryPointsManagerBase
|
||||
import com.intellij.codeInspection.ex.EntryPointsManagerImpl
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import com.intellij.psi.PsiReference
|
||||
@@ -40,9 +43,11 @@ import org.jetbrains.kotlin.asJava.LightClassUtil
|
||||
import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory
|
||||
import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindClassUsagesHandler
|
||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.*
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
@@ -50,11 +55,13 @@ import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
||||
import java.awt.GridBagConstraints
|
||||
import java.awt.GridBagLayout
|
||||
import java.awt.Insets
|
||||
import java.util.*
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JPanel
|
||||
|
||||
@@ -120,19 +127,6 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
|
||||
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
||||
return object : KtVisitorVoid() {
|
||||
private fun createQuickFix(declaration: KtNamedDeclaration): LocalQuickFix {
|
||||
return object : LocalQuickFix {
|
||||
override fun getName() = QuickFixBundle.message("safe.delete.text", declaration.name)
|
||||
|
||||
override fun getFamilyName() = "Safe delete"
|
||||
|
||||
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||
if (!FileModificationService.getInstance().prepareFileForWrite(declaration.containingFile)) return
|
||||
SafeDeleteHandler.invoke(project, arrayOf(declaration), false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitNamedDeclaration(declaration: KtNamedDeclaration) {
|
||||
val messageKey = when (declaration) {
|
||||
is KtClass -> "unused.class"
|
||||
@@ -179,7 +173,7 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
|
||||
KotlinBundle.message(messageKey, declaration.name),
|
||||
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
|
||||
true,
|
||||
createQuickFix(declaration)
|
||||
*createQuickFixes(declaration).toTypedArray()
|
||||
)
|
||||
|
||||
holder.registerProblem(problemDescriptor)
|
||||
@@ -284,4 +278,40 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
|
||||
)
|
||||
return panel
|
||||
}
|
||||
|
||||
private fun createQuickFixes(declaration: KtNamedDeclaration): List<LocalQuickFix> {
|
||||
val list = ArrayList<LocalQuickFix>()
|
||||
|
||||
list.add(SafeDeleteFix(declaration))
|
||||
|
||||
for (annotationEntry in declaration.annotationEntries) {
|
||||
val typeElement = annotationEntry.typeReference?.typeElement as? KtUserType ?: continue
|
||||
val bindingContext = annotationEntry.analyze(BodyResolveMode.PARTIAL)
|
||||
val target = typeElement.referenceExpression?.mainReference?.resolveToDescriptors(bindingContext)?.singleOrNull() ?: continue
|
||||
val fqName = target.importableFqName?.asString() ?: continue
|
||||
|
||||
// checks taken from com.intellij.codeInspection.util.SpecialAnnotationsUtilBase.createAddToSpecialAnnotationFixes
|
||||
if (fqName.startsWith("kotlin.")
|
||||
|| fqName.startsWith("java.")
|
||||
|| fqName.startsWith("javax.")
|
||||
|| fqName.startsWith("org.jetbrains.") && AnnotationUtil.isJetbrainsAnnotation(StringUtil.getShortName(fqName)))
|
||||
continue
|
||||
|
||||
val intentionAction = QuickFixFactory.getInstance().createAddToDependencyInjectionAnnotationsFix(declaration.project, fqName, "declarations")
|
||||
list.add(IntentionWrapper(intentionAction, declaration.containingFile))
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
}
|
||||
|
||||
class SafeDeleteFix(val declaration: KtDeclaration) : LocalQuickFix {
|
||||
override fun getName() = QuickFixBundle.message("safe.delete.text", declaration.name)
|
||||
|
||||
override fun getFamilyName() = "Safe delete"
|
||||
|
||||
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||
if (!FileModificationService.getInstance().prepareFileForWrite(declaration.containingFile)) return
|
||||
SafeDeleteHandler.invoke(project, arrayOf(declaration), false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.inspections.UnusedSymbolInspection
|
||||
@@ -0,0 +1,5 @@
|
||||
// "Suppress for declarations annotated by 'kotlin.Deprecated'" "false"
|
||||
// ACTION: Safe delete 'foo'
|
||||
// ACTION: Create test
|
||||
@Deprecated("")
|
||||
fun foo<caret>(){}
|
||||
@@ -0,0 +1,5 @@
|
||||
// "Suppress for declarations annotated by 'java.lang.SuppressWarnings'" "false"
|
||||
// ACTION: Safe delete 'Unused'
|
||||
// ACTION: Create test
|
||||
@java.lang.SuppressWarnings("")
|
||||
class <caret>Unused
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Suppress for declarations annotated by 'org.jetbrains.annotations.NonNls'" "false"
|
||||
// ACTION: Safe delete 'foo'
|
||||
// ACTION: Create test
|
||||
import org.jetbrains.annotations.NonNls
|
||||
|
||||
@NonNls
|
||||
fun <caret>foo() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// "Suppress for declarations annotated by 'xxx.XXX'" "true"
|
||||
package xxx
|
||||
|
||||
annotation class XXX
|
||||
|
||||
@XXX
|
||||
class <caret>UnusedClass
|
||||
@@ -0,0 +1,7 @@
|
||||
// "Suppress for declarations annotated by 'xxx.XXX'" "true"
|
||||
package xxx
|
||||
|
||||
annotation class XXX
|
||||
|
||||
@XXX
|
||||
class <caret>UnusedClass
|
||||
@@ -7715,6 +7715,39 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/unusedSuppressAnnotation")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class UnusedSuppressAnnotation extends AbstractQuickFixTest {
|
||||
public void testAllFilesPresentInUnusedSuppressAnnotation() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/unusedSuppressAnnotation"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("notForDeprecated.kt")
|
||||
public void testNotForDeprecated() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/unusedSuppressAnnotation/notForDeprecated.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("notForJava.kt")
|
||||
public void testNotForJava() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/unusedSuppressAnnotation/notForJava.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("notForJetBrains.kt")
|
||||
public void testNotForJetBrains() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/unusedSuppressAnnotation/notForJetBrains.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/unusedSuppressAnnotation/simple.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/variables")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user