KTIJ-717 [Java side inspection]: "implementation of Kotlin sealed"
This commit is contained in:
@@ -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
|
redundant.double.negation=Redundant double negation
|
||||||
equals.between.objects.of.inconvertible.types='equals()' between objects of inconvertible types
|
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
|
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
|
junit.static.methods=JUnit static methods
|
||||||
redundant.override.fix.text=Remove redundant overriding method
|
redundant.override.fix.text=Remove redundant overriding method
|
||||||
redundant.overriding.method=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.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.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.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.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.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
|
inspection.data.class.private.constructor.display.name=Private data class constructor is exposed via the 'copy' method
|
||||||
|
|||||||
@@ -1504,6 +1504,14 @@
|
|||||||
language="JAVA"
|
language="JAVA"
|
||||||
key="inspection.kotlin.internal.in.java.display.name" bundle="messages.KotlinBundle"/>
|
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"
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.MoveSuspiciousCallableReferenceIntoParenthesesInspection"
|
||||||
groupPath="Kotlin"
|
groupPath="Kotlin"
|
||||||
groupName="Probable bugs"
|
groupName="Probable bugs"
|
||||||
|
|||||||
+57
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+18
@@ -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() {};
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+5
@@ -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><default></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><default></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><default></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><default></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><default></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>
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"inspectionClass": "org.jetbrains.kotlin.idea.inspections.KotlinSealedInheritorsInJavaInspection"
|
||||||
|
}
|
||||||
+5
@@ -49,6 +49,11 @@ public class MultiFileInspectionTestGenerated extends AbstractMultiFileInspectio
|
|||||||
runTest("idea/testData/multiFileInspections/kotlinInternalInJavaTest/kotlinInternalInJavaTest.test");
|
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")
|
@TestMetadata("mainInTwoModules/mainInTwoModules.test")
|
||||||
public void testMainInTwoModules_MainInTwoModules() throws Exception {
|
public void testMainInTwoModules_MainInTwoModules() throws Exception {
|
||||||
runTest("idea/testData/multiFileInspections/mainInTwoModules/mainInTwoModules.test");
|
runTest("idea/testData/multiFileInspections/mainInTwoModules/mainInTwoModules.test");
|
||||||
|
|||||||
Reference in New Issue
Block a user