KTIJ-717 [Java side inspection]: "implementation of Kotlin sealed"
diff --git a/idea/resources-en/inspectionDescriptions/KotlinSealedInheritorsInJava.html b/idea/resources-en/inspectionDescriptions/KotlinSealedInheritorsInJava.html
new file mode 100644
index 0000000..a81e027
--- /dev/null
+++ b/idea/resources-en/inspectionDescriptions/KotlinSealedInheritorsInJava.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+This inspection reports attempt to inherit Kotlin sealed interface/class in Java code.
+</body>
+</html>
\ No newline at end of file
diff --git a/idea/resources-en/messages/KotlinBundle.properties b/idea/resources-en/messages/KotlinBundle.properties
index 8503a39..a5b5796 100644
--- a/idea/resources-en/messages/KotlinBundle.properties
+++ b/idea/resources-en/messages/KotlinBundle.properties
@@ -1343,6 +1343,7 @@
 redundant.double.negation=Redundant double negation
 equals.between.objects.of.inconvertible.types='equals()' between objects of inconvertible types
 usage.of.kotlin.internal.declaration.from.different.module=Usage of Kotlin internal declaration from different module
+inheritance.of.kotlin.sealed=Java {0,choice,0#interface|1#class} cannot be a part of Kotlin sealed hierarchy
 junit.static.methods=JUnit static methods
 redundant.override.fix.text=Remove redundant overriding method
 redundant.overriding.method=Redundant overriding method
@@ -2064,6 +2065,7 @@
 inspection.copy.without.named.arguments.display.name='copy' method of data class is called without named arguments
 inspection.move.suspicious.callable.reference.into.parentheses.display.name=Suspicious callable reference used as lambda result
 inspection.kotlin.internal.in.java.display.name=Usage of Kotlin internal declarations from Java
+inspection.kotlin.sealed.in.java.display.name=Inheritance of Kotlin sealed interface/class from Java
 inspection.unused.lambda.expression.body.display.name=Unused return value of a function with lambda expression body
 inspection.destructuring.wrong.name.display.name=Variable in destructuring declaration uses name of a wrong data class property
 inspection.data.class.private.constructor.display.name=Private data class constructor is exposed via the 'copy' method
diff --git a/idea/resources/META-INF/inspections.xml b/idea/resources/META-INF/inspections.xml
index eb699fc..4717ddc 100644
--- a/idea/resources/META-INF/inspections.xml
+++ b/idea/resources/META-INF/inspections.xml
@@ -1504,6 +1504,14 @@
                      language="JAVA"
                      key="inspection.kotlin.internal.in.java.display.name" bundle="messages.KotlinBundle"/>
 
+    <localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.KotlinSealedInheritorsInJavaInspection"
+                     groupPath="Kotlin"
+                     groupName="Java interop issues"
+                     enabledByDefault="true"
+                     level="ERROR"
+                     language="JAVA"
+                     key="inspection.kotlin.sealed.in.java.display.name" bundle="messages.KotlinBundle"/>
+
     <localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.MoveSuspiciousCallableReferenceIntoParenthesesInspection"
                      groupPath="Kotlin"
                      groupName="Probable bugs"
diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinSealedInheritorsInJavaInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinSealedInheritorsInJavaInspection.kt
new file mode 100644
index 0000000..572f5b0
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinSealedInheritorsInJavaInspection.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.idea.inspections
+
+import com.intellij.codeInspection.LocalInspectionTool
+import com.intellij.codeInspection.ProblemHighlightType
+import com.intellij.codeInspection.ProblemsHolder
+import com.intellij.psi.*
+import com.intellij.psi.impl.source.PsiClassReferenceType
+import org.jetbrains.kotlin.asJava.classes.KtUltraLightClass
+import org.jetbrains.kotlin.descriptors.isSealed
+import org.jetbrains.kotlin.idea.KotlinBundle
+
+
+class KotlinSealedInheritorsInJavaInspection : LocalInspectionTool() {
+    companion object {
+        private fun PsiClass.listSealedParentReferences(): List<PsiReference> {
+            if (this is PsiAnonymousClass && baseClassType.isKotlinSealed())
+                return listOf(baseClassReference)
+
+            val sealedBaseClasses = extendsList?.listSealedMembers()
+            val sealedBaseInterfaces = implementsList?.listSealedMembers()
+
+            return sealedBaseClasses.orEmpty() + sealedBaseInterfaces.orEmpty()
+        }
+
+        private fun PsiReferenceList.listSealedMembers(): List<PsiReference>? = referencedTypes
+            ?.filter { it.isKotlinSealed() }
+            ?.mapNotNull { it as? PsiClassReferenceType }
+            ?.map { it.reference }
+
+        private fun PsiClassType.isKotlinSealed(): Boolean = resolve()?.isKotlinSealed() == true
+
+        private fun PsiClass.isKotlinSealed(): Boolean = this is KtUltraLightClass && getDescriptor()?.isSealed() == true
+
+        private val PsiClass.abstractionTypeName: String
+            get() = if (isInterface) "interface" else "class"
+    }
+
+    override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
+        return object : JavaElementVisitor() {
+            override fun visitClass(aClass: PsiClass?) {
+                aClass?.listSealedParentReferences()?.forEach {
+                    holder.registerProblem(
+                        it, KotlinBundle.message("inheritance.of.kotlin.sealed", 0.takeIf { aClass.isInterface } ?: 1),
+                        ProblemHighlightType.GENERIC_ERROR_OR_WARNING
+                    )
+                }
+            }
+
+            override fun visitAnonymousClass(aClass: PsiAnonymousClass?) = visitClass(aClass)
+        }
+    }
+}
\ No newline at end of file
diff --git a/idea/testData/multiFileInspections/kotlinSealedInJavaTest/before/JavaTriesToExtendKotlinSealed.java b/idea/testData/multiFileInspections/kotlinSealedInJavaTest/before/JavaTriesToExtendKotlinSealed.java
new file mode 100644
index 0000000..2ab7d25
--- /dev/null
+++ b/idea/testData/multiFileInspections/kotlinSealedInJavaTest/before/JavaTriesToExtendKotlinSealed.java
@@ -0,0 +1,18 @@
+public class JavaTriesToExtendKotlinSealed {
+
+    private static class TryToImplement implements KotlinSealedInterface {}
+    private static interface TryToExtend extends KotlinSealedInterface {}
+    private static class TryToExtendClass extends KotlinSealedClass {}
+
+    class OkToImplement implements KotlinInterface {}
+    interface OkToExtend extends KotlinInterface {}
+    class OkToExtendClass extends KotlinClass{}
+
+    public static void main(String[] args) {
+        KotlinSealedInterface sealedInterface = new KotlinSealedInterface() {}; // anonymouns class implements interface
+        KotlinSealedClass sealedClass = new KotlinSealedClass() {};
+
+        new KotlinInterface() {};
+        new KotlinClass() {};
+    }
+}
\ No newline at end of file
diff --git a/idea/testData/multiFileInspections/kotlinSealedInJavaTest/before/KotlinSealedDeclarations.kt b/idea/testData/multiFileInspections/kotlinSealedInJavaTest/before/KotlinSealedDeclarations.kt
new file mode 100644
index 0000000..7e9097c
--- /dev/null
+++ b/idea/testData/multiFileInspections/kotlinSealedInJavaTest/before/KotlinSealedDeclarations.kt
@@ -0,0 +1,5 @@
+sealed interface KotlinSealedInterface
+sealed class KotlinSealedClass
+
+interface KotlinInterface: KotlinSealedInterface
+class KotlinClass: KotlinSealedClass()
\ No newline at end of file
diff --git a/idea/testData/multiFileInspections/kotlinSealedInJavaTest/expected.xml b/idea/testData/multiFileInspections/kotlinSealedInJavaTest/expected.xml
new file mode 100644
index 0000000..585da37
--- /dev/null
+++ b/idea/testData/multiFileInspections/kotlinSealedInJavaTest/expected.xml
@@ -0,0 +1,72 @@
+<problems>
+  <problem>
+    <file>JavaTriesToExtendKotlinSealed.java</file>
+    <line>3</line>
+    <module>testKotlinSealedInJavaTest_KotlinSealedInJavaTest</module>
+    <package>&lt;default&gt;</package>
+    <entry_point TYPE="class" FQNAME="JavaTriesToExtendKotlinSealed.TryToImplement"/>
+    <problem_class id="KotlinSealedInheritorsInJava" severity="ERROR" attribute_key="ERRORS_ATTRIBUTES">Inheritance of Kotlin sealed
+      interface/class from Java
+    </problem_class>
+    <description>Java class cannot be a part of Kotlin sealed hierarchy</description>
+    <highlighted_element>KotlinSealedInterface</highlighted_element>
+    <offset>4</offset>
+    <length>71</length>
+  </problem>
+
+  <problem>
+    <file>JavaTriesToExtendKotlinSealed.java</file>
+    <line>4</line>
+    <module>testKotlinSealedInJavaTest_KotlinSealedInJavaTest</module>
+    <package>&lt;default&gt;</package>
+    <entry_point TYPE="class" FQNAME="JavaTriesToExtendKotlinSealed.TryToExtend"/>
+    <problem_class id="KotlinSealedInheritorsInJava" severity="ERROR" attribute_key="ERRORS_ATTRIBUTES">Inheritance of Kotlin sealed
+      interface/class from Java
+    </problem_class>
+    <description>Java interface cannot be a part of Kotlin sealed hierarchy</description>
+    <highlighted_element>KotlinSealedInterface</highlighted_element>
+    <offset>4</offset>
+    <length>69</length>
+  </problem>
+
+  <problem>
+    <file>JavaTriesToExtendKotlinSealed.java</file>
+    <line>5</line>
+    <module>testKotlinSealedInJavaTest_KotlinSealedInJavaTest</module>
+    <package>&lt;default&gt;</package>
+    <entry_point TYPE="class" FQNAME="JavaTriesToExtendKotlinSealed.TryToExtendClass"/>
+    <problem_class id="KotlinSealedInheritorsInJava" severity="ERROR" attribute_key="ERRORS_ATTRIBUTES">Inheritance of Kotlin sealed
+      interface/class from Java
+    </problem_class>
+    <description>Java class cannot be a part of Kotlin sealed hierarchy</description>
+    <highlighted_element>KotlinSealedClass</highlighted_element>
+    <offset>4</offset>
+    <length>66</length>
+  </problem>
+
+  <problem>
+    <file>JavaTriesToExtendKotlinSealed.java</file>
+    <line>12</line>
+    <module>testKotlinSealedInJavaTest_KotlinSealedInJavaTest</module>
+    <package>&lt;default&gt;</package>
+    <entry_point TYPE="class" FQNAME="JavaTriesToExtendKotlinSealed$1" />
+    <problem_class id="KotlinSealedInheritorsInJava" severity="ERROR" attribute_key="ERRORS_ATTRIBUTES">Inheritance of Kotlin sealed interface/class from Java</problem_class>
+    <description>Java class cannot be a part of Kotlin sealed hierarchy</description>
+    <highlighted_element>KotlinSealedInterface</highlighted_element>
+    <offset>52</offset>
+    <length>26</length>
+  </problem>
+
+  <problem>
+    <file>JavaTriesToExtendKotlinSealed.java</file>
+    <line>13</line>
+    <module>testKotlinSealedInJavaTest_KotlinSealedInJavaTest</module>
+    <package>&lt;default&gt;</package>
+    <entry_point TYPE="class" FQNAME="JavaTriesToExtendKotlinSealed$2" />
+    <problem_class id="KotlinSealedInheritorsInJava" severity="ERROR" attribute_key="ERRORS_ATTRIBUTES">Inheritance of Kotlin sealed interface/class from Java</problem_class>
+    <description>Java class cannot be a part of Kotlin sealed hierarchy</description>
+    <highlighted_element>KotlinSealedClass()</highlighted_element>
+    <offset>44</offset>
+    <length>22</length>
+  </problem>
+</problems>
\ No newline at end of file
diff --git a/idea/testData/multiFileInspections/kotlinSealedInJavaTest/kotlinSealedInJavaTest.test b/idea/testData/multiFileInspections/kotlinSealedInJavaTest/kotlinSealedInJavaTest.test
new file mode 100644
index 0000000..ef7ba98
--- /dev/null
+++ b/idea/testData/multiFileInspections/kotlinSealedInJavaTest/kotlinSealedInJavaTest.test
@@ -0,0 +1,3 @@
+{
+    "inspectionClass": "org.jetbrains.kotlin.idea.inspections.KotlinSealedInheritorsInJavaInspection"
+}
\ No newline at end of file
diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/MultiFileInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/MultiFileInspectionTestGenerated.java
index 4cef517..55c8dc1 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/MultiFileInspectionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/MultiFileInspectionTestGenerated.java
@@ -48,6 +48,11 @@
         runTest("idea/testData/multiFileInspections/kotlinInternalInJavaTest/kotlinInternalInJavaTest.test");
     }
 
+    @TestMetadata("kotlinSealedInJavaTest/kotlinSealedInJavaTest.test")
+    public void testKotlinSealedInJavaTest_KotlinSealedInJavaTest() throws Exception {
+        runTest("idea/testData/multiFileInspections/kotlinSealedInJavaTest/kotlinSealedInJavaTest.test");
+    }
+
     @TestMetadata("mainInTwoModules/mainInTwoModules.test")
     public void testMainInTwoModules_MainInTwoModules() throws Exception {
         runTest("idea/testData/multiFileInspections/mainInTwoModules/mainInTwoModules.test");