diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt
index 9a2dff7ff55..37b3cbab23f 100644
--- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt
+++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt
@@ -379,6 +379,11 @@ class KtPsiFactory @JvmOverloads constructor(private val project: Project, val m
return whenEntry
}
+ fun createWhenCondition(conditionText: String): KtWhenCondition {
+ val whenEntry = createWhenEntry("$conditionText -> {}")
+ return whenEntry.conditions[0]
+ }
+
fun createBlockStringTemplateEntry(expression: KtExpression): KtStringTemplateEntryWithExpression {
// We don't want reformatting here as it can potentially change something in raw strings
val stringTemplateExpression = createExpressionByPattern("\"$\${$0}\"", expression, reformat = false) as KtStringTemplateExpression
diff --git a/idea/resources/inspectionDescriptions/RedundantObjectTypeCheck.html b/idea/resources/inspectionDescriptions/RedundantObjectTypeCheck.html
new file mode 100644
index 00000000000..cededb0d4d5
--- /dev/null
+++ b/idea/resources/inspectionDescriptions/RedundantObjectTypeCheck.html
@@ -0,0 +1,5 @@
+
+
+This inspection reports redundant type checks for object.
+
+
\ No newline at end of file
diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index 3f179d9123f..bf9a50f0b32 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -2617,6 +2617,15 @@
language="JAVA"
/>
+
+
diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantObjectTypeCheckInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantObjectTypeCheckInspection.kt
new file mode 100644
index 00000000000..4b78eeca7ca
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantObjectTypeCheckInspection.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlin.idea.inspections
+
+import com.intellij.codeInspection.LocalQuickFix
+import com.intellij.codeInspection.ProblemDescriptor
+import com.intellij.codeInspection.ProblemsHolder
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.util.TextRange
+import com.intellij.psi.PsiElementVisitor
+import org.jetbrains.kotlin.descriptors.ClassDescriptor
+import org.jetbrains.kotlin.idea.caches.resolve.analyze
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
+import org.jetbrains.kotlin.resolve.BindingContext
+import org.jetbrains.kotlin.resolve.DescriptorUtils
+
+class RedundantObjectTypeCheckInspection : AbstractKotlinInspection() {
+
+ override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
+ return object : KtVisitorVoid() {
+
+ override fun visitIsExpression(expression: KtIsExpression) {
+ super.visitIsExpression(expression)
+ val typeReference = expression.typeReference ?: return
+ if (!typeReference.isObject()) return
+ registerProblem(expression.operationReference, ReplaceWithEqualityFix(expression.isNegated))
+ }
+
+ override fun visitWhenConditionIsPattern(condition: KtWhenConditionIsPattern) {
+ super.visitWhenConditionIsPattern(condition)
+ val typeReference = condition.typeReference ?: return
+ if (!typeReference.isObject()) return
+ if (condition.isNegated) return
+ registerProblem(condition, RemoveIsOperator())
+ }
+
+ private fun registerProblem(element: KtElement, quickFix: LocalQuickFix) {
+ holder.registerProblem(element,
+ TextRange(0, 2),
+ "Redundant type checks for object",
+ quickFix)
+ }
+ }
+ }
+}
+
+private fun KtTypeReference.isObject(): Boolean {
+ val descriptor = this.analyze()[BindingContext.TYPE, this]?.constructor?.declarationDescriptor as? ClassDescriptor
+ return DescriptorUtils.isObject(descriptor)
+}
+
+private class ReplaceWithEqualityFix(isNegated: Boolean) : LocalQuickFix {
+ private val isOperator = if (isNegated) "!is" else "is"
+
+ private val equality = if (isNegated) "!=" else "=="
+
+ override fun getName() = "Replace '$isOperator' with '$equality'"
+
+ override fun getFamilyName() = name
+
+ override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
+ val element = descriptor.psiElement.getParentOfType(strict = false) ?: return
+ val typeReference = element.typeReference ?: return
+ val factory = KtPsiFactory(project)
+ val newElement = factory.createExpressionByPattern("$0 $1 $2", element.leftHandSide, equality, typeReference.text)
+ element.replace(newElement)
+ }
+}
+
+private class RemoveIsOperator : LocalQuickFix {
+ override fun getName() = "Remove 'is' operator"
+
+ override fun getFamilyName() = name
+
+ override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
+ val element = descriptor.psiElement.getParentOfType(strict = false) ?: return
+ val typeReference = element.typeReference ?: return
+ val factory = KtPsiFactory(project)
+ val newElement = factory.createWhenCondition(typeReference.text)
+ element.replace(newElement)
+ }
+}
+
diff --git a/idea/testData/inspectionsLocal/redundantObjectTypeCheck/.inspection b/idea/testData/inspectionsLocal/redundantObjectTypeCheck/.inspection
new file mode 100644
index 00000000000..74ecb7f3637
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantObjectTypeCheck/.inspection
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.inspections.RedundantObjectTypeCheckInspection
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isClass.kt b/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isClass.kt
new file mode 100644
index 00000000000..3c9301cc6c9
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isClass.kt
@@ -0,0 +1,7 @@
+// PROBLEM: none
+class C
+
+fun foo(arg: Any) {
+ if (arg is C) {
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isClassWhenEntry.kt b/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isClassWhenEntry.kt
new file mode 100644
index 00000000000..9eeb00081e3
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isClassWhenEntry.kt
@@ -0,0 +1,9 @@
+// PROBLEM: none
+class C
+
+fun foo(arg: Any) {
+ when (arg) {
+ is C -> {
+ }
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isNotObject.kt b/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isNotObject.kt
new file mode 100644
index 00000000000..cf7842ba8e7
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isNotObject.kt
@@ -0,0 +1,6 @@
+object O
+
+fun foo(arg: Any) {
+ if (arg !is O) {
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isNotObject.kt.after b/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isNotObject.kt.after
new file mode 100644
index 00000000000..f8f7b58faff
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isNotObject.kt.after
@@ -0,0 +1,6 @@
+object O
+
+fun foo(arg: Any) {
+ if (arg != O) {
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isNotObjectWhenEntry.kt b/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isNotObjectWhenEntry.kt
new file mode 100644
index 00000000000..b25a2b7687b
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isNotObjectWhenEntry.kt
@@ -0,0 +1,9 @@
+// PROBLEM: none
+object O
+
+fun foo(arg: Any) {
+ when (arg) {
+ !is O -> {
+ }
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isObject.kt b/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isObject.kt
new file mode 100644
index 00000000000..0173d4676c6
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isObject.kt
@@ -0,0 +1,6 @@
+object O
+
+fun foo(arg: Any) {
+ if (arg is O) {
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isObject.kt.after b/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isObject.kt.after
new file mode 100644
index 00000000000..7b5c390ee16
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isObject.kt.after
@@ -0,0 +1,6 @@
+object O
+
+fun foo(arg: Any) {
+ if (arg == O) {
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isObjectWhenEntry.kt b/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isObjectWhenEntry.kt
new file mode 100644
index 00000000000..8f76847811b
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isObjectWhenEntry.kt
@@ -0,0 +1,8 @@
+object O
+
+fun foo(arg: Any) {
+ when (arg) {
+ is O -> {
+ }
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isObjectWhenEntry.kt.after b/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isObjectWhenEntry.kt.after
new file mode 100644
index 00000000000..405e54d79c7
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantObjectTypeCheck/isObjectWhenEntry.kt.after
@@ -0,0 +1,8 @@
+object O
+
+fun foo(arg: Any) {
+ when (arg) {
+ O -> {
+ }
+ }
+}
\ No newline at end of file
diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
index 7a05520aacd..5ce7befa2e5 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
@@ -2646,6 +2646,51 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
}
}
+ @TestMetadata("idea/testData/inspectionsLocal/redundantObjectTypeCheck")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class RedundantObjectTypeCheck extends AbstractLocalInspectionTest {
+ public void testAllFilesPresentInRedundantObjectTypeCheck() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/redundantObjectTypeCheck"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
+ }
+
+ @TestMetadata("isClass.kt")
+ public void testIsClass() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantObjectTypeCheck/isClass.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("isClassWhenEntry.kt")
+ public void testIsClassWhenEntry() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantObjectTypeCheck/isClassWhenEntry.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("isNotObject.kt")
+ public void testIsNotObject() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantObjectTypeCheck/isNotObject.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("isNotObjectWhenEntry.kt")
+ public void testIsNotObjectWhenEntry() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantObjectTypeCheck/isNotObjectWhenEntry.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("isObject.kt")
+ public void testIsObject() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantObjectTypeCheck/isObject.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("isObjectWhenEntry.kt")
+ public void testIsObjectWhenEntry() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantObjectTypeCheck/isObjectWhenEntry.kt");
+ doTest(fileName);
+ }
+ }
+
@TestMetadata("idea/testData/inspectionsLocal/redundantOverride")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)