mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-20 05:21:29 +07:00
[kotlin] Add ObjectInheritsException inspection
IJ-CR-168258 #KTIJ-30892 (cherry picked from commit e7e379821883d5f5b582488a71a3d0575f2dee5a) IJ-MR-169152 GitOrigin-RevId: c2b4c7d94df4fd42a190015275bdc1bf2091901a
This commit is contained in:
committed by
intellij-monorepo-bot
parent
7e92a25089
commit
5f6f79cb8f
@@ -2404,6 +2404,11 @@ inspection.deprecated.callable.add.replace.with.display.name=@Deprecated annotat
|
||||
inspection.replace.collection.count.with.size.display.name=Collection count can be converted to size
|
||||
inspection.simplify.assert.not.null.display.name='assert' call can be replaced with '!!' or '?:'
|
||||
inspection.object.literal.to.lambda.display.name=Object literal can be converted to lambda
|
||||
|
||||
inspection.object.exception.to.class.display.name=Exception should not be an object
|
||||
inspection.object.exception.to.class.warning=Exception should not be an object
|
||||
inspection.object.exception.to.class.quick.fix.name=Change exception object to class
|
||||
|
||||
remove.redundant.elvis.return.null.text=Remove redundant '?: return null'
|
||||
inspection.redundant.elvis.return.null.descriptor=Redundant '?: return null'
|
||||
inspection.redundant.elvis.return.null.display.name=Redundant '?: return null'
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<html>
|
||||
<body>
|
||||
Reports object inherited from <code>Exception</code> because exceptions are inherently stateful, containing information like the stacktrace.
|
||||
<p><b>Example:</b></p>
|
||||
<pre><code>
|
||||
object MyEx: Exception()
|
||||
</code></pre>
|
||||
<p>After the quick-fix is applied:</p>
|
||||
<pre><code>
|
||||
class MyEx: Exception()
|
||||
</code></pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -173,6 +173,16 @@
|
||||
key="inspection.object.literal.to.lambda.display.name"
|
||||
bundle="messages.KotlinBundle"/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.codeInsight.inspections.shared.ObjectInheritsExceptionInspection"
|
||||
groupPath="Kotlin"
|
||||
groupBundle="messages.KotlinBundle" groupKey="group.names.probable.bugs"
|
||||
enabledByDefault="true"
|
||||
level="WARNING"
|
||||
language="kotlin"
|
||||
cleanupTool="true"
|
||||
key="inspection.object.exception.to.class.display.name"
|
||||
bundle="messages.KotlinBundle"/>
|
||||
|
||||
<localInspection
|
||||
implementationClass="org.jetbrains.kotlin.idea.codeInsight.inspections.shared.JavaIoSerializableObjectMustHaveReadResolveInspection"
|
||||
groupPath="Kotlin"
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
|
||||
package org.jetbrains.kotlin.idea.codeInsight.inspections.shared
|
||||
|
||||
import com.intellij.codeInsight.intention.preview.IntentionPreviewUtils
|
||||
import com.intellij.codeInspection.CleanupLocalInspectionTool
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.codeInspection.util.IntentionFamilyName
|
||||
import com.intellij.modcommand.ModPsiUpdater
|
||||
import com.intellij.modcommand.PsiUpdateModCommandQuickFix
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import com.intellij.psi.impl.source.tree.LeafPsiElement
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.analysis.api.analyze
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KaNamedClassSymbol
|
||||
import org.jetbrains.kotlin.builtins.StandardNames.BUILT_INS_PACKAGE_FQ_NAME
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractKotlinInspection
|
||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchParameters
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtObjectDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.psi.KtUserType
|
||||
import org.jetbrains.kotlin.psi.KtVisitorVoid
|
||||
|
||||
internal class ObjectInheritsExceptionInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor =
|
||||
object : KtVisitorVoid() {
|
||||
override fun visitObjectDeclaration(declaration: KtObjectDeclaration) {
|
||||
val isException = analyze(declaration) {
|
||||
val symbol = declaration.symbol as? KaNamedClassSymbol ?: return
|
||||
symbol.superTypes.any {
|
||||
it.isClassType(exceptionClassId) ||
|
||||
it.isSubtypeOf(exceptionClassId)
|
||||
}
|
||||
}
|
||||
|
||||
if (!isException) return
|
||||
|
||||
holder.registerProblem(
|
||||
declaration.getObjectKeyword() ?: return,
|
||||
KotlinBundle.message("inspection.object.exception.to.class.warning"),
|
||||
ChangeObjectToClassQuickFix()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class ChangeObjectToClassQuickFix : PsiUpdateModCommandQuickFix() {
|
||||
override fun getFamilyName(): @IntentionFamilyName String = KotlinBundle.message("inspection.object.exception.to.class.quick.fix.name")
|
||||
|
||||
override fun applyFix(project: Project, element: PsiElement, updater: ModPsiUpdater) {
|
||||
val objectDeclaration =
|
||||
(element as? LeafPsiElement)?.let { it.parent as? KtObjectDeclaration } ?: return
|
||||
|
||||
val psiFactory = KtPsiFactory(project)
|
||||
|
||||
val searchParameters = KotlinReferencesSearchParameters(
|
||||
objectDeclaration, objectDeclaration.useScope, ignoreAccessScope = false
|
||||
)
|
||||
|
||||
val query = ReferencesSearch.search(searchParameters)
|
||||
|
||||
val references = if (IntentionPreviewUtils.isIntentionPreviewActive()) {
|
||||
listOfNotNull(query.findFirst())
|
||||
} else {
|
||||
query.findAll()
|
||||
}
|
||||
|
||||
references
|
||||
.mapNotNull {
|
||||
if (it !is KtSimpleNameReference) return@mapNotNull null
|
||||
val expression = it.element
|
||||
|
||||
if (expression.parent is KtUserType ||
|
||||
PsiTreeUtil.getParentOfType(
|
||||
/* element = */ expression,
|
||||
/* aClass = */ KtImportDirective::class.java,
|
||||
/* strict = */ false,
|
||||
/* ...stopAt = */ KtBlockExpression::class.java
|
||||
)
|
||||
!= null
|
||||
) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
|
||||
updater.getWritable(expression)
|
||||
}.forEach {
|
||||
val referencedName = it.getReferencedName()
|
||||
it.replace(psiFactory.createExpression("$referencedName()"))
|
||||
}
|
||||
|
||||
objectDeclaration.getObjectKeyword()?.replace(psiFactory.createClassKeyword())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val exceptionClassId = ClassId(BUILT_INS_PACKAGE_FQ_NAME , Name.identifier("Exception"))
|
||||
@@ -20,6 +20,30 @@ import org.junit.runner.RunWith;
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("../testData/quickfix")
|
||||
public abstract class SharedK1QuickFixTestGenerated extends AbstractSharedK1QuickFixTest {
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("../testData/quickfix/objectInheritsException")
|
||||
public static class ObjectInheritsException extends AbstractSharedK1QuickFixTest {
|
||||
@java.lang.Override
|
||||
@org.jetbrains.annotations.NotNull
|
||||
public final KotlinPluginMode getPluginMode() {
|
||||
return KotlinPluginMode.K1;
|
||||
}
|
||||
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("../testData/quickfix/objectInheritsException/simple.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("withCallSites.kt")
|
||||
public void testWithCallSites() throws Exception {
|
||||
runTest("../testData/quickfix/objectInheritsException/withCallSites.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("../testData/quickfix/redundantSemicolon")
|
||||
public static class RedundantSemicolon extends AbstractSharedK1QuickFixTest {
|
||||
|
||||
@@ -22,6 +22,30 @@ public abstract class K2SharedQuickFixTestGenerated extends AbstractK2SharedQuic
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("../testData/quickfix")
|
||||
public abstract static class Quickfix extends AbstractK2SharedQuickFixTest {
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("../testData/quickfix/objectInheritsException")
|
||||
public static class ObjectInheritsException extends AbstractK2SharedQuickFixTest {
|
||||
@java.lang.Override
|
||||
@org.jetbrains.annotations.NotNull
|
||||
public final KotlinPluginMode getPluginMode() {
|
||||
return KotlinPluginMode.K2;
|
||||
}
|
||||
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("../testData/quickfix/objectInheritsException/simple.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("withCallSites.kt")
|
||||
public void testWithCallSites() throws Exception {
|
||||
runTest("../testData/quickfix/objectInheritsException/withCallSites.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("../testData/quickfix/redundantSemicolon")
|
||||
public static class RedundantSemicolon extends AbstractK2SharedQuickFixTest {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.codeInsight.inspections.shared.ObjectInheritsExceptionInspection
|
||||
@@ -0,0 +1,5 @@
|
||||
// "Change exception object to class" "true"
|
||||
// WITH_STDLIB
|
||||
<caret>object MyException : Exception()
|
||||
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.codeInsight.inspections.shared.ObjectInheritsExceptionInspection$ChangeObjectToClassQuickFix
|
||||
// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.codeInsight.inspections.shared.ObjectInheritsExceptionInspection$ChangeObjectToClassQuickFix
|
||||
@@ -0,0 +1,5 @@
|
||||
// "Change exception object to class" "true"
|
||||
// WITH_STDLIB
|
||||
<caret>class MyException : Exception()
|
||||
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.codeInsight.inspections.shared.ObjectInheritsExceptionInspection$ChangeObjectToClassQuickFix
|
||||
// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.codeInsight.inspections.shared.ObjectInheritsExceptionInspection$ChangeObjectToClassQuickFix
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Change exception object to class" "true"
|
||||
// WITH_STDLIB
|
||||
package some
|
||||
|
||||
import some.MyException
|
||||
|
||||
<caret>object MyException : Exception()
|
||||
|
||||
fun foo() {
|
||||
throw MyException
|
||||
}
|
||||
|
||||
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.codeInsight.inspections.shared.ObjectInheritsExceptionInspection$ChangeObjectToClassQuickFix
|
||||
// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.codeInsight.inspections.shared.ObjectInheritsExceptionInspection$ChangeObjectToClassQuickFix
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Change exception object to class" "true"
|
||||
// WITH_STDLIB
|
||||
package some
|
||||
|
||||
import some.MyException
|
||||
|
||||
<caret>class MyException : Exception()
|
||||
|
||||
fun foo() {
|
||||
throw MyException()
|
||||
}
|
||||
|
||||
// FUS_QUICKFIX_NAME: org.jetbrains.kotlin.idea.codeInsight.inspections.shared.ObjectInheritsExceptionInspection$ChangeObjectToClassQuickFix
|
||||
// FUS_K2_QUICKFIX_NAME: org.jetbrains.kotlin.idea.codeInsight.inspections.shared.ObjectInheritsExceptionInspection$ChangeObjectToClassQuickFix
|
||||
Reference in New Issue
Block a user