diff --git a/java/java-impl/src/inspectionDescriptions/RedundantClassCall.html b/java/java-impl/src/inspectionDescriptions/RedundantClassCall.html
new file mode 100644
index 000000000000..39dca030ec6d
--- /dev/null
+++ b/java/java-impl/src/inspectionDescriptions/RedundantClassCall.html
@@ -0,0 +1,7 @@
+
+
+Reports redundant calls of java.lang.Class methods. E.g. Xyz.class.isInstance(object) could be replaced with object instanceof Xyz.
+
+New in 2018.2
+
+
\ No newline at end of file
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantClassCall/afterCast.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantClassCall/afterCast.java
new file mode 100644
index 000000000000..3655ec0fa0c4
--- /dev/null
+++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantClassCall/afterCast.java
@@ -0,0 +1,8 @@
+// "Replace with '(Integer)'" "true"
+class X {
+ void test(Object obj) {
+ if(Integer.class.isInstance(obj)) {
+ System.out.println((Integer) obj);
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantClassCall/afterInstanceOf.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantClassCall/afterInstanceOf.java
new file mode 100644
index 000000000000..a5b81339642d
--- /dev/null
+++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantClassCall/afterInstanceOf.java
@@ -0,0 +1,8 @@
+// "Replace with 'instanceof Integer'" "true"
+class X {
+ void test(Object obj) {
+ if(obj instanceof Integer) {
+ System.out.println("Integer");
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantClassCall/beforeCast.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantClassCall/beforeCast.java
new file mode 100644
index 000000000000..e1434c9990c8
--- /dev/null
+++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantClassCall/beforeCast.java
@@ -0,0 +1,8 @@
+// "Replace with '(Integer)'" "true"
+class X {
+ void test(Object obj) {
+ if(Integer.class.isInstance(obj)) {
+ System.out.println(Integer.class.cast(obj));
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantClassCall/beforeInstanceOf.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantClassCall/beforeInstanceOf.java
new file mode 100644
index 000000000000..69644adbd4ab
--- /dev/null
+++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantClassCall/beforeInstanceOf.java
@@ -0,0 +1,8 @@
+// "Replace with 'instanceof Integer'" "true"
+class X {
+ void test(Object obj) {
+ if(Integer.class.isInstance(obj)) {
+ System.out.println("Integer");
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantClassCall/beforeInstanceOfWrongType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantClassCall/beforeInstanceOfWrongType.java
new file mode 100644
index 000000000000..48ecb5c452f4
--- /dev/null
+++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantClassCall/beforeInstanceOfWrongType.java
@@ -0,0 +1,8 @@
+// "Replace with 'instanceof Integer'" "false"
+class X {
+ void test(String obj) {
+ if(Integer.class.isInstance(obj)) {
+ System.out.println("Integer");
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/RedundantClassCallInspectionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/RedundantClassCallInspectionTest.java
new file mode 100644
index 000000000000..f504ccdd4565
--- /dev/null
+++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/RedundantClassCallInspectionTest.java
@@ -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";
+ }
+}
\ No newline at end of file
diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties
index e5012fa36b8c..7081b68a58c0 100644
--- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties
+++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties
@@ -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
\ No newline at end of file
+implicit.default.charset.usage.fix.family.name=Specify UTF-8 charset
+
+inspection.redundant.class.call.display.name=Redundant 'isInstance' or 'cast' call
+
diff --git a/plugins/InspectionGadgets/src/META-INF/InspectionGadgets.xml b/plugins/InspectionGadgets/src/META-INF/InspectionGadgets.xml
index d35b3bfba2bf..0accc3033d10 100644
--- a/plugins/InspectionGadgets/src/META-INF/InspectionGadgets.xml
+++ b/plugins/InspectionGadgets/src/META-INF/InspectionGadgets.xml
@@ -2020,6 +2020,9 @@
+
diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/redundancy/RedundantClassCallInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/redundancy/RedundantClassCallInspection.java
new file mode 100644
index 000000000000..895ad5d6fa95
--- /dev/null
+++ b/plugins/InspectionGadgets/src/com/siyeh/ig/redundancy/RedundantClassCallInspection.java
@@ -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;
+ }
+ }
+}