mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-05-06 05:10:22 +07:00
IDEA-193288 Inspection to detect Xyz.class.isInstance(foo) and Xyz.class.cast(foo)
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>Reports redundant calls of <b>java.lang.Class</b> methods. E.g. <b>Xyz.class.isInstance(object)</b> could be replaced with <b>object instanceof Xyz</b>.</p>
|
||||
<!-- tooltip end -->
|
||||
<small>New in 2018.2</small>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Replace with '(Integer)'" "true"
|
||||
class X {
|
||||
void test(Object obj) {
|
||||
if(Integer.class.isInstance(obj)) {
|
||||
System.out.println((Integer) obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Replace with 'instanceof Integer'" "true"
|
||||
class X {
|
||||
void test(Object obj) {
|
||||
if(obj instanceof Integer) {
|
||||
System.out.println("Integer");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Replace with '(Integer)'" "true"
|
||||
class X {
|
||||
void test(Object obj) {
|
||||
if(Integer.class.isInstance(obj)) {
|
||||
System.out.println(Integer.class.c<caret>ast(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Replace with 'instanceof Integer'" "true"
|
||||
class X {
|
||||
void test(Object obj) {
|
||||
if(Integer.class.isIns<caret>tance(obj)) {
|
||||
System.out.println("Integer");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Replace with 'instanceof Integer'" "false"
|
||||
class X {
|
||||
void test(String obj) {
|
||||
if(Integer.class.isIns<caret>tance(obj)) {
|
||||
System.out.println("Integer");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.java.codeInsight.daemon.quickFix;
|
||||
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.siyeh.ig.redundancy.RedundantClassCallInspection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
public class RedundantClassCallInspectionTest extends LightQuickFixParameterizedTestCase {
|
||||
@NotNull
|
||||
@Override
|
||||
protected LocalInspectionTool[] configureLocalInspectionTools() {
|
||||
return new LocalInspectionTool[]{
|
||||
new RedundantClassCallInspection()
|
||||
};
|
||||
}
|
||||
|
||||
public void test() { doAllTests(); }
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/codeInsight/daemonCodeAnalyzer/quickFix/redundantClassCall";
|
||||
}
|
||||
}
|
||||
@@ -2287,4 +2287,7 @@ inspection.serializable.can.have.default.serial.uid='Serializable' can have defa
|
||||
inspection.serializable.can.have.default.serial.uid.message='serialVersionUID' differs from default generated
|
||||
inspection.serializable.can.have.default.serial.uid.fix.name=Change 'serialVersionUID' to generated by signature
|
||||
junit5.nested.test.display.name=JUnit 5 malformed @Nested class
|
||||
implicit.default.charset.usage.fix.family.name=Specify UTF-8 charset
|
||||
implicit.default.charset.usage.fix.family.name=Specify UTF-8 charset
|
||||
|
||||
inspection.redundant.class.call.display.name=Redundant 'isInstance' or 'cast' call
|
||||
|
||||
|
||||
@@ -2020,6 +2020,9 @@
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="RedundantStringOperation" bundle="com.siyeh.InspectionGadgetsBundle"
|
||||
key="inspection.redundant.string.operation.display.name" groupBundle="messages.InspectionsBundle" groupKey="group.names.verbose.or.redundant.code.constructs"
|
||||
enabledByDefault="true" level="WARNING" cleanupTool="true" implementationClass="com.siyeh.ig.redundancy.RedundantStringOperationInspection"/>
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="RedundantClassCall" bundle="com.siyeh.InspectionGadgetsBundle"
|
||||
key="inspection.redundant.class.call.display.name" groupBundle="messages.InspectionsBundle" groupKey="group.names.verbose.or.redundant.code.constructs"
|
||||
enabledByDefault="true" level="WARNING" cleanupTool="true" implementationClass="com.siyeh.ig.redundancy.RedundantClassCallInspection"/>
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="RedundantCollectionOperation" bundle="com.siyeh.InspectionGadgetsBundle"
|
||||
key="inspection.redundant.collection.operation.display.name" groupBundle="messages.InspectionsBundle" groupKey="group.names.verbose.or.redundant.code.constructs"
|
||||
enabledByDefault="true" level="WARNING" cleanupTool="true" implementationClass="com.siyeh.ig.redundancy.RedundantCollectionOperationInspection"/>
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.siyeh.ig.redundancy;
|
||||
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.callMatcher.CallMatcher;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
public class RedundantClassCallInspection extends AbstractBaseJavaLocalInspectionTool implements CleanupLocalInspectionTool {
|
||||
private static final CallMatcher IS_INSTANCE =
|
||||
CallMatcher.exactInstanceCall(CommonClassNames.JAVA_LANG_CLASS, "isInstance").parameterCount(1);
|
||||
private static final CallMatcher CAST =
|
||||
CallMatcher.exactInstanceCall(CommonClassNames.JAVA_LANG_CLASS, "cast").parameterCount(1);
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
return new JavaElementVisitor() {
|
||||
@Override
|
||||
public void visitMethodCallExpression(PsiMethodCallExpression call) {
|
||||
PsiElement nameElement = call.getMethodExpression().getReferenceNameElement();
|
||||
if (nameElement == null) return;
|
||||
boolean isInstance = IS_INSTANCE.test(call);
|
||||
boolean cast = CAST.test(call);
|
||||
if (isInstance || cast) {
|
||||
PsiExpression qualifier = PsiUtil.skipParenthesizedExprDown(call.getMethodExpression().getQualifierExpression());
|
||||
if (qualifier instanceof PsiClassObjectAccessExpression) {
|
||||
PsiTypeElement typeElement = ((PsiClassObjectAccessExpression)qualifier).getOperand();
|
||||
PsiType classType = typeElement.getType();
|
||||
PsiExpression argument = call.getArgumentList().getExpressions()[0];
|
||||
PsiType argumentType = argument.getType();
|
||||
if (argumentType == null || !argumentType.isConvertibleFrom(classType)) {
|
||||
// will be a compilation error after replacement; skip this
|
||||
return;
|
||||
}
|
||||
LocalQuickFix fix = isInstance ? new ReplaceWithInstanceOfFix(typeElement) : new ReplaceWithCastFix(typeElement);
|
||||
holder.registerProblem(nameElement, InspectionGadgetsBundle.message("redundant.call.problem.descriptor"), fix);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static abstract class ReplaceRedundantClassCallFix implements LocalQuickFix {
|
||||
final String myReplacement;
|
||||
|
||||
ReplaceRedundantClassCallFix(String replacement) {
|
||||
myReplacement = replacement;
|
||||
}
|
||||
|
||||
@Nls(capitalization = Nls.Capitalization.Sentence)
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return InspectionGadgetsBundle.message("replace.with", myReplacement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(descriptor.getStartElement(), PsiMethodCallExpression.class);
|
||||
if (call == null) return;
|
||||
PsiExpression arg = ArrayUtil.getFirstElement(call.getArgumentList().getExpressions());
|
||||
if (arg == null) return;
|
||||
PsiClassObjectAccessExpression qualifier =
|
||||
tryCast(PsiUtil.skipParenthesizedExprDown(call.getMethodExpression().getQualifierExpression()),
|
||||
PsiClassObjectAccessExpression.class);
|
||||
if (qualifier == null) return;
|
||||
CommentTracker ct = new CommentTracker();
|
||||
ct.replaceAndRestoreComments(call, createReplacement(ct.text(arg), ct.text(qualifier.getOperand())));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
abstract String createReplacement(String argText, String classText);
|
||||
}
|
||||
|
||||
private static class ReplaceWithInstanceOfFix extends ReplaceRedundantClassCallFix {
|
||||
public ReplaceWithInstanceOfFix(@NotNull PsiTypeElement typeElement) {
|
||||
super("instanceof "+typeElement.getType().getPresentableText());
|
||||
}
|
||||
|
||||
@Nls(capitalization = Nls.Capitalization.Sentence)
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return "Replace with 'instanceof'";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
String createReplacement(String argText, String classText) {
|
||||
return argText + " instanceof " + classText;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ReplaceWithCastFix extends ReplaceRedundantClassCallFix {
|
||||
public ReplaceWithCastFix(@NotNull PsiTypeElement typeElement) {
|
||||
super("("+typeElement.getType().getPresentableText()+")");
|
||||
}
|
||||
|
||||
@Nls(capitalization = Nls.Capitalization.Sentence)
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return "Replace with cast";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
String createReplacement(String argText, String classText) {
|
||||
return "("+classText+")"+argText;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user