From bff7ff0c5d827f3596d9ba032e252c019b1cef19 Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Mon, 9 Nov 2015 19:17:28 +0300 Subject: [PATCH] Improve heuristic to decide whether we will generate DefaultImpls for interface This is not accurate, there are some cases when we will generate DefaultImpls class that are not covered: - Inherited default implementations - Annotated members In these cases IDE will report an error when in fact code compiles What is critical though is to be able to decide whether to build DefaultImpls light classes solely based on psi without triggering stubs calculation --- .../kotlin/asJava/lightClassUtils.kt | 13 +++++++- .../InterfaceDefaultImpls.java | 13 ++++++++ .../InterfaceDefaultImpls.kt | 30 +++++++++++++++++++ ...nstKotlinBinariesCheckerTestGenerated.java | 6 ++++ ...ainstKotlinSourceCheckerTestGenerated.java | 6 ++++ 5 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/InterfaceDefaultImpls.java create mode 100644 idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/InterfaceDefaultImpls.kt diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt index cf7af579cfd..d3c9bc46662 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt @@ -104,7 +104,18 @@ public val PsiElement.namedUnwrappedElement: PsiNamedElement? val KtClassOrObject.hasInterfaceDefaultImpls: Boolean - get() = this is KtClass && isInterface() + get() = this is KtClass && isInterface() && hasNonAbstractMembers(this) + +private fun hasNonAbstractMembers(ktInterface: KtClass): Boolean { + return ktInterface.declarations.any { + isNonAbstractMember(it) + } +} + +private fun isNonAbstractMember(member: KtDeclaration?): Boolean { + return (member is KtNamedFunction && member.hasBody()) || + (member is KtProperty && (member.hasDelegateExpressionOrInitializer() || member.getter?.hasBody() ?: false || member.setter?.hasBody() ?: false)) +} private val DEFAULT_IMPLS_CLASS_NAME = Name.identifier(JvmAbi.DEFAULT_IMPLS_CLASS_NAME) fun FqName.defaultImplsChild() = child(DEFAULT_IMPLS_CLASS_NAME) diff --git a/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/InterfaceDefaultImpls.java b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/InterfaceDefaultImpls.java new file mode 100644 index 00000000000..12fd906cb33 --- /dev/null +++ b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/InterfaceDefaultImpls.java @@ -0,0 +1,13 @@ +import lib.*; + +class C { + public static void test(NonAbstractFun i, NonAbstractFunWithExpressionBody i2, NonAbstractProperty i3, NonAbstractPropertyWithBody i4) { + AllAbstract.DefaultImpls; + + NonAbstractFun.DefaultImpls.f(i); + NonAbstractFunWithExpressionBody.DefaultImpls.f(i2); + + NonAbstractProperty.DefaultImpls.getC(i3); + NonAbstractPropertyWithBody.DefaultImpls.getC(i4); + } +} diff --git a/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/InterfaceDefaultImpls.kt b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/InterfaceDefaultImpls.kt new file mode 100644 index 00000000000..d585dd46e9e --- /dev/null +++ b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/InterfaceDefaultImpls.kt @@ -0,0 +1,30 @@ +package lib + +interface AllAbstract { + val c: Int + fun f(t: T): T + + val g: Int + + var g2: String +} + +interface NonAbstractFun { + fun f() { + + } +} + +interface NonAbstractFunWithExpressionBody { + fun f() = 3 +} + +interface NonAbstractProperty { + val c: Int get() = 3 +} + +interface NonAbstractPropertyWithBody { + val c: Int get() { + return 3 + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/checkers/JavaAgainstKotlinBinariesCheckerTestGenerated.java b/idea/tests/org/jetbrains/kotlin/checkers/JavaAgainstKotlinBinariesCheckerTestGenerated.java index 9addd2dab6f..b5386f4f619 100644 --- a/idea/tests/org/jetbrains/kotlin/checkers/JavaAgainstKotlinBinariesCheckerTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/checkers/JavaAgainstKotlinBinariesCheckerTestGenerated.java @@ -77,6 +77,12 @@ public class JavaAgainstKotlinBinariesCheckerTestGenerated extends AbstractJavaA doTest(fileName); } + @TestMetadata("InterfaceDefaultImpls.kt") + public void testInterfaceDefaultImpls() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/InterfaceDefaultImpls.kt"); + doTest(fileName); + } + @TestMetadata("JvmOverloadsFunctions.kt") public void testJvmOverloadsFunctions() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/JvmOverloadsFunctions.kt"); diff --git a/idea/tests/org/jetbrains/kotlin/checkers/JavaAgainstKotlinSourceCheckerTestGenerated.java b/idea/tests/org/jetbrains/kotlin/checkers/JavaAgainstKotlinSourceCheckerTestGenerated.java index 6181befdd12..579bb1c38d1 100644 --- a/idea/tests/org/jetbrains/kotlin/checkers/JavaAgainstKotlinSourceCheckerTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/checkers/JavaAgainstKotlinSourceCheckerTestGenerated.java @@ -79,6 +79,12 @@ public class JavaAgainstKotlinSourceCheckerTestGenerated extends AbstractJavaAga doTest(fileName); } + @TestMetadata("InterfaceDefaultImpls.kt") + public void testInterfaceDefaultImpls() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/InterfaceDefaultImpls.kt"); + doTest(fileName); + } + @TestMetadata("JvmOverloadsFunctions.kt") public void testJvmOverloadsFunctions() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/JvmOverloadsFunctions.kt");