KTIJ-717 [Java side inspection]: "implementation of Kotlin sealed"

This commit is contained in:
Andrei Klunnyi
2020-12-14 18:32:23 +01:00
parent 65cf941b9b
commit 3e8016ed25
9 changed files with 175 additions and 0 deletions
@@ -0,0 +1,5 @@
<html>
<body>
This inspection reports attempt to inherit Kotlin sealed interface/class in Java code.
</body>
</html>
@@ -1343,6 +1343,7 @@ double.negation.fix.text=Remove redundant negations
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.replace.array.of.with.literal.display.name='arrayOf' call can be repl
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
+8
View File
@@ -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"
@@ -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)
}
}
}
@@ -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() {};
}
}
@@ -0,0 +1,5 @@
sealed interface KotlinSealedInterface
sealed class KotlinSealedClass
interface KotlinInterface: KotlinSealedInterface
class KotlinClass: KotlinSealedClass()
@@ -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>
@@ -0,0 +1,3 @@
{
"inspectionClass": "org.jetbrains.kotlin.idea.inspections.KotlinSealedInheritorsInJavaInspection"
}
@@ -49,6 +49,11 @@ public class MultiFileInspectionTestGenerated extends AbstractMultiFileInspectio
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");