diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java index f9fd8200c2d..c5415ab5758 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java @@ -83,6 +83,7 @@ import static org.jetbrains.kotlin.resolve.DescriptorUtils.*; import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.JAVA_STRING_TYPE; import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE; import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.NO_ORIGIN; +import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind.CLASS_MEMBER_DELEGATION_TO_DEFAULT_IMPL; import static org.jetbrains.kotlin.types.Variance.INVARIANT; import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isLocalFunction; import static org.jetbrains.org.objectweb.asm.Opcodes.*; @@ -1282,14 +1283,15 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { CodegenUtilKt.reportTarget6InheritanceErrorIfNeeded(descriptor, myClass.getPsiOrParent(), restrictedInheritance, state); } - private void generateDelegationToDefaultImpl(@NotNull FunctionDescriptor traitFun, @NotNull FunctionDescriptor inheritedFun) { + private void generateDelegationToDefaultImpl(@NotNull FunctionDescriptor interfaceFun, @NotNull FunctionDescriptor inheritedFun) { + functionCodegen.generateMethod( - JvmDeclarationOriginKt.DelegationToDefaultImpls(descriptorToDeclaration(traitFun), traitFun), + new JvmDeclarationOrigin(CLASS_MEMBER_DELEGATION_TO_DEFAULT_IMPL, descriptorToDeclaration(interfaceFun), interfaceFun), inheritedFun, new FunctionGenerationStrategy.CodegenBased(state) { @Override public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) { - DeclarationDescriptor containingDeclaration = traitFun.getContainingDeclaration(); + DeclarationDescriptor containingDeclaration = interfaceFun.getContainingDeclaration(); if (!DescriptorUtils.isInterface(containingDeclaration)) return; DeclarationDescriptor declarationInheritedFun = inheritedFun.getContainingDeclaration(); @@ -1301,12 +1303,12 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { ClassDescriptor containingTrait = (ClassDescriptor) containingDeclaration; Type traitImplType = typeMapper.mapDefaultImpls(containingTrait); - Method traitMethod = typeMapper.mapAsmMethod(traitFun.getOriginal(), OwnerKind.DEFAULT_IMPLS); + Method traitMethod = typeMapper.mapAsmMethod(interfaceFun.getOriginal(), OwnerKind.DEFAULT_IMPLS); Type[] argTypes = signature.getAsmMethod().getArgumentTypes(); Type[] originalArgTypes = traitMethod.getArgumentTypes(); assert originalArgTypes.length == argTypes.length + 1 : - "Invalid trait implementation signature: " + signature + " vs " + traitMethod + " for " + traitFun; + "Invalid trait implementation signature: " + signature + " vs " + traitMethod + " for " + interfaceFun; InstructionAdapter iv = codegen.v; iv.load(0, OBJECT_TYPE); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/InterfaceImplBodyCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/InterfaceImplBodyCodegen.kt index a211fbcb342..72bd18a722c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/InterfaceImplBodyCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/InterfaceImplBodyCodegen.kt @@ -29,8 +29,8 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtPureClassOrObject import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.jvm.diagnostics.DelegationToDefaultImpls import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin +import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.org.objectweb.asm.MethodVisitor @@ -113,7 +113,10 @@ class InterfaceImplBodyCodegen( if (delegateTo is JavaMethodDescriptor) return functionCodegen.generateMethod( - DelegationToDefaultImpls(DescriptorToSourceUtils.descriptorToDeclaration(descriptor), descriptor), + JvmDeclarationOrigin( + JvmDeclarationOriginKind.DEFAULT_IMPL_DELEGATION_TO_SUPERINTERFACE_DEFAULT_IMPL, + DescriptorToSourceUtils.descriptorToDeclaration(descriptor), descriptor + ), descriptor, object : FunctionGenerationStrategy.CodegenBased(state) { override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/SignatureCollectingClassBuilderFactory.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/SignatureCollectingClassBuilderFactory.kt index ed1e231a41c..a4e54e9eddf 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/SignatureCollectingClassBuilderFactory.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/SignatureCollectingClassBuilderFactory.kt @@ -27,7 +27,7 @@ import org.jetbrains.org.objectweb.asm.FieldVisitor import org.jetbrains.org.objectweb.asm.MethodVisitor abstract class SignatureCollectingClassBuilderFactory( - delegate: ClassBuilderFactory + delegate: ClassBuilderFactory, val shouldGenerate: (JvmDeclarationOrigin) -> Boolean ) : DelegatingClassBuilderFactory(delegate) { protected abstract fun handleClashingSignatures(data: ConflictingJvmDeclarationsData) @@ -57,11 +57,17 @@ abstract class SignatureCollectingClassBuilderFactory( override fun newField(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor { signatures.putValue(RawSignature(name, desc, MemberKind.FIELD), origin) + if (!shouldGenerate(origin)) { + return AbstractClassBuilder.EMPTY_FIELD_VISITOR + } return super.newField(origin, access, name, desc, signature, value) } override fun newMethod(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, exceptions: Array?): MethodVisitor { signatures.putValue(RawSignature(name, desc, MemberKind.METHOD), origin) + if (!shouldGenerate(origin)) { + return AbstractClassBuilder.EMPTY_METHOD_VISITOR + } return super.newMethod(origin, access, name, desc, signature, exceptions) } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/BuilderFactoryForDuplicateSignatureDiagnostics.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/BuilderFactoryForDuplicateSignatureDiagnostics.kt index dae5c87c61e..12180b19ac3 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/BuilderFactoryForDuplicateSignatureDiagnostics.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/BuilderFactoryForDuplicateSignatureDiagnostics.kt @@ -37,7 +37,7 @@ import org.jetbrains.kotlin.utils.addIfNotNull import java.util.* private val EXTERNAL_SOURCES_KINDS = arrayOf( - JvmDeclarationOriginKind.DELEGATION_TO_DEFAULT_IMPLS, + JvmDeclarationOriginKind.CLASS_MEMBER_DELEGATION_TO_DEFAULT_IMPL, JvmDeclarationOriginKind.DELEGATION, JvmDeclarationOriginKind.BRIDGE ) @@ -58,8 +58,9 @@ class BuilderFactoryForDuplicateSignatureDiagnostics( bindingContext: BindingContext, private val diagnostics: DiagnosticSink, fileClassesProvider: JvmFileClassesProvider, - moduleName: String -) : SignatureCollectingClassBuilderFactory(builderFactory) { + moduleName: String, + shouldGenerate: (JvmDeclarationOrigin) -> Boolean +) : SignatureCollectingClassBuilderFactory(builderFactory, shouldGenerate) { // Avoid errors when some classes are not loaded for some reason private val typeMapper = KotlinTypeMapper( diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt index a1192ba6820..94369b0bf1f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt @@ -44,6 +44,8 @@ import org.jetbrains.kotlin.psi.KtScript import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics import org.jetbrains.kotlin.resolve.jvm.JvmClassName +import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin +import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind.CLASS_MEMBER_DELEGATION_TO_DEFAULT_IMPL import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration import java.io.File @@ -177,8 +179,10 @@ class GenerationState @JvmOverloads constructor( { OptimizationClassBuilderFactory(it, configuration.get(JVMConfigurationKeys.DISABLE_OPTIMIZATION, false)) }, ::CoroutineTransformerClassBuilderFactory, { BuilderFactoryForDuplicateSignatureDiagnostics( - it, this.bindingContext, diagnostics, fileClassesProvider, this.moduleName - ).apply { duplicateSignatureFactory = this } }, + it, this.bindingContext, diagnostics, + fileClassesProvider, this.moduleName, + shouldGenerate = { !shouldOnlyCollectSignatures(it) } + ).apply { duplicateSignatureFactory = this } }, { BuilderFactoryForDuplicateClassNameDiagnostics(it, diagnostics) }, { configuration.get(JVMConfigurationKeys.DECLARATIONS_JSON_PATH) ?.let { destination -> SignatureDumpingBuilderFactory(it, File(destination)) } ?: it } @@ -209,6 +213,9 @@ class GenerationState @JvmOverloads constructor( fun destroy() { interceptedBuilderFactory.close() } + + private fun shouldOnlyCollectSignatures(origin: JvmDeclarationOrigin) + = classBuilderMode == ClassBuilderMode.LIGHT_CLASSES && origin.originKind == CLASS_MEMBER_DELEGATION_TO_DEFAULT_IMPL } private class LazyJvmDiagnostics(compute: () -> Diagnostics): Diagnostics { diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/JvmDeclarationOrigin.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/JvmDeclarationOrigin.kt index 4951c574a52..0407f0d8832 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/JvmDeclarationOrigin.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/JvmDeclarationOrigin.kt @@ -32,7 +32,8 @@ enum class JvmDeclarationOriginKind { OTHER, PACKAGE_PART, INTERFACE_DEFAULT_IMPL, - DELEGATION_TO_DEFAULT_IMPLS, + CLASS_MEMBER_DELEGATION_TO_DEFAULT_IMPL, + DEFAULT_IMPL_DELEGATION_TO_SUPERINTERFACE_DEFAULT_IMPL, DELEGATION, SAM_DELEGATION, BRIDGE, @@ -84,8 +85,6 @@ fun MultifileClassPart(file: KtFile, descriptor: PackageFragmentDescriptor): Jvm JvmDeclarationOrigin(MULTIFILE_CLASS_PART, file, descriptor) fun DefaultImpls(element: PsiElement?, descriptor: ClassDescriptor): JvmDeclarationOrigin = JvmDeclarationOrigin(INTERFACE_DEFAULT_IMPL, element, descriptor) -fun DelegationToDefaultImpls(element: PsiElement?, descriptor: FunctionDescriptor): JvmDeclarationOrigin = - JvmDeclarationOrigin(DELEGATION_TO_DEFAULT_IMPLS, element, descriptor) fun Delegation(element: PsiElement?, descriptor: FunctionDescriptor): JvmDeclarationOrigin = JvmDeclarationOrigin(DELEGATION, element, descriptor) diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightElements.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightElements.kt index 42de4ee5f19..b2eb7e82a6b 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightElements.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightElements.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.asJava.elements import com.intellij.psi.* import com.intellij.psi.impl.PsiVariableEx import org.jetbrains.kotlin.asJava.builder.LightMemberOrigin +import org.jetbrains.kotlin.asJava.classes.KtLightClass import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind @@ -33,6 +34,8 @@ interface KtLightDeclaration : KtLigh interface KtLightMember : PsiMember, KtLightDeclaration, PsiNameIdentifierOwner, PsiDocCommentOwner { val lightMemberOrigin: LightMemberOrigin? + + override fun getContainingClass(): KtLightClass } interface KtLightField : PsiField, KtLightMember, PsiVariableEx @@ -40,5 +43,5 @@ interface KtLightField : PsiField, KtLightMember, PsiVariableEx interface KtLightMethod : PsiAnnotationMethod, KtLightMember { val isDelegated: Boolean get() = lightMemberOrigin?.originKind == JvmDeclarationOriginKind.DELEGATION - || lightMemberOrigin?.originKind == JvmDeclarationOriginKind.DELEGATION_TO_DEFAULT_IMPLS + || lightMemberOrigin?.originKind == JvmDeclarationOriginKind.CLASS_MEMBER_DELEGATION_TO_DEFAULT_IMPL } \ No newline at end of file diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightMemberImpl.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightMemberImpl.kt index c30d421521b..90df98878a3 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightMemberImpl.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightMemberImpl.kt @@ -33,6 +33,7 @@ import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtNamedDeclaration +import org.jetbrains.kotlin.psi.psiUtil.hasBody abstract class KtLightMemberImpl( computeRealDelegate: () -> D, @@ -104,18 +105,24 @@ class KtLightModifierList( private val clsDelegate by lazyPub { owner.clsDelegate.modifierList!! } private val _annotations by lazyPub { computeAnnotations(this, clsDelegate) } - override fun hasModifierProperty(name: String): Boolean { - if (dummyDelegate != null) { - if (name in visibilityModifiers) { - if (owner.kotlinOrigin?.hasModifier(KtTokens.OVERRIDE_KEYWORD) ?: false) { - return clsDelegate.hasModifierProperty(name) - } + override fun hasModifierProperty(name: String) = when { + name == PsiModifier.ABSTRACT && isImplementationInInterface() -> false + name == PsiModifier.DEFAULT && isImplementationInInterface() -> true + dummyDelegate != null -> { + when { + name in visibilityModifiers && isOverride() -> + clsDelegate.hasModifierProperty(name) + else -> dummyDelegate.hasModifierProperty(name) } - return dummyDelegate.hasModifierProperty(name) } - return clsDelegate.hasModifierProperty(name) + else -> clsDelegate.hasModifierProperty(name) } + private fun isOverride() = owner.kotlinOrigin?.hasModifier(KtTokens.OVERRIDE_KEYWORD) ?: false + + private fun isImplementationInInterface() + = owner.containingClass.isInterface && owner is KtLightMethod && owner.kotlinOrigin?.hasBody() ?: false + override fun hasExplicitModifier(name: String) = hasModifierProperty(name) override fun setModifierProperty(name: String, value: Boolean) = clsDelegate.setModifierProperty(name, value) diff --git a/compiler/testData/asJava/lightClasses/DataClassWithCustomImplementedMembers.java b/compiler/testData/asJava/lightClasses/DataClassWithCustomImplementedMembers.java new file mode 100644 index 00000000000..1988b04c7eb --- /dev/null +++ b/compiler/testData/asJava/lightClasses/DataClassWithCustomImplementedMembers.java @@ -0,0 +1,70 @@ +public final class Wrapper { + public Wrapper() { /* compiled code */ } + + public static final class Equals { + @org.jetbrains.annotations.NotNull + private final p.G code; + + public boolean equals(@org.jetbrains.annotations.Nullable java.lang.Object other) { /* compiled code */ } + + @org.jetbrains.annotations.NotNull + public final p.G getCode() { /* compiled code */ } + + public Equals(@org.jetbrains.annotations.NotNull p.G code) { /* compiled code */ } + + @org.jetbrains.annotations.NotNull + public final p.G component1() { /* compiled code */ } + + @org.jetbrains.annotations.NotNull + public final p.Wrapper.Equals copy(@org.jetbrains.annotations.NotNull p.G code) { /* compiled code */ } + + public java.lang.String toString() { /* compiled code */ } + + public int hashCode() { /* compiled code */ } + } + + public static final class HashCode { + @org.jetbrains.annotations.NotNull + private final p.G code; + + public int hashCode() { /* compiled code */ } + + @org.jetbrains.annotations.NotNull + public final p.G getCode() { /* compiled code */ } + + public HashCode(@org.jetbrains.annotations.NotNull p.G code) { /* compiled code */ } + + @org.jetbrains.annotations.NotNull + public final p.G component1() { /* compiled code */ } + + @org.jetbrains.annotations.NotNull + public final p.Wrapper.HashCode copy(@org.jetbrains.annotations.NotNull p.G code) { /* compiled code */ } + + public java.lang.String toString() { /* compiled code */ } + + public boolean equals(java.lang.Object p) { /* compiled code */ } + } + + public static final class ToString { + @org.jetbrains.annotations.NotNull + private final p.G code; + + @org.jetbrains.annotations.NotNull + public java.lang.String toString() { /* compiled code */ } + + @org.jetbrains.annotations.NotNull + public final p.G getCode() { /* compiled code */ } + + public ToString(@org.jetbrains.annotations.NotNull p.G code) { /* compiled code */ } + + @org.jetbrains.annotations.NotNull + public final p.G component1() { /* compiled code */ } + + @org.jetbrains.annotations.NotNull + public final p.Wrapper.ToString copy(@org.jetbrains.annotations.NotNull p.G code) { /* compiled code */ } + + public int hashCode() { /* compiled code */ } + + public boolean equals(java.lang.Object p) { /* compiled code */ } + } +} \ No newline at end of file diff --git a/compiler/testData/asJava/lightClasses/ExtendingInterfaceWithDefaultImpls.java b/compiler/testData/asJava/lightClasses/ExtendingInterfaceWithDefaultImpls.java new file mode 100644 index 00000000000..43f385c121a --- /dev/null +++ b/compiler/testData/asJava/lightClasses/ExtendingInterfaceWithDefaultImpls.java @@ -0,0 +1,12 @@ +public interface B extends p.A { + @org.jetbrains.annotations.NotNull + java.lang.String b(); + + final class DefaultImpls { + @org.jetbrains.annotations.NotNull + public static java.lang.String b(p.B $this) { /* compiled code */ } + + @org.jetbrains.annotations.NotNull + public static java.lang.String a(p.B $this) { /* compiled code */ } + } +} diff --git a/compiler/testData/asJava/lightClasses/ExtendingInterfaceWithDefaultImpls.kt b/compiler/testData/asJava/lightClasses/ExtendingInterfaceWithDefaultImpls.kt new file mode 100644 index 00000000000..c97b33adca5 --- /dev/null +++ b/compiler/testData/asJava/lightClasses/ExtendingInterfaceWithDefaultImpls.kt @@ -0,0 +1,10 @@ +// p.B +package p + +interface A { + fun a() = "a" +} + +interface B: A { + fun b() = "b" +} \ No newline at end of file diff --git a/compiler/testData/asJava/lightClasses/InheritingInterfaceDefaultImpls.compiled.java b/compiler/testData/asJava/lightClasses/InheritingInterfaceDefaultImpls.compiled.java new file mode 100644 index 00000000000..59a42a2bf07 --- /dev/null +++ b/compiler/testData/asJava/lightClasses/InheritingInterfaceDefaultImpls.compiled.java @@ -0,0 +1,13 @@ +public final class Inheritor implements p.I, p.I2 { + public final void f() { /* compiled code */ } + + public void g() { /* compiled code */ } + + public Inheritor() { /* compiled code */ } + + @org.jetbrains.annotations.NotNull + public java.lang.String foo() { /* compiled code */ } + + @org.jetbrains.annotations.NotNull + public java.lang.String bar() { /* compiled code */ } +} \ No newline at end of file diff --git a/compiler/testData/asJava/lightClasses/InheritingInterfaceDefaultImpls.java b/compiler/testData/asJava/lightClasses/InheritingInterfaceDefaultImpls.java new file mode 100644 index 00000000000..4e30aafdcd9 --- /dev/null +++ b/compiler/testData/asJava/lightClasses/InheritingInterfaceDefaultImpls.java @@ -0,0 +1,7 @@ +public final class Inheritor implements p.I, p.I2 { + public final void f() { /* compiled code */ } + + public void g() { /* compiled code */ } + + public Inheritor() { /* compiled code */ } +} \ No newline at end of file diff --git a/compiler/testData/asJava/lightClasses/InheritingInterfaceDefaultImpls.kt b/compiler/testData/asJava/lightClasses/InheritingInterfaceDefaultImpls.kt new file mode 100644 index 00000000000..2b78bb02333 --- /dev/null +++ b/compiler/testData/asJava/lightClasses/InheritingInterfaceDefaultImpls.kt @@ -0,0 +1,26 @@ +// p.Inheritor +package p + +class Inheritor: I, I2 { + + fun f() { + + } + + override fun g() { + } +} + +interface I : I1 { + fun g() +} + +interface I1 { + fun foo() = "foo" +} + +interface I2 { + fun bar() = "bar" +} + +// LAZINESS:NoLaziness \ No newline at end of file diff --git a/compiler/tests-common/org/jetbrains/kotlin/asJava/LightClassTestCommon.kt b/compiler/tests-common/org/jetbrains/kotlin/asJava/LightClassTestCommon.kt index e2007729af4..dfd2e523f45 100644 --- a/compiler/tests-common/org/jetbrains/kotlin/asJava/LightClassTestCommon.kt +++ b/compiler/tests-common/org/jetbrains/kotlin/asJava/LightClassTestCommon.kt @@ -30,6 +30,7 @@ object LightClassTestCommon { @JvmOverloads fun testLightClass( + expectedFile: File, testDataFile: File, findLightClass: (String) -> PsiClass?, normalizeText: (String) -> String = { it } @@ -42,7 +43,7 @@ object LightClassTestCommon { val lightClass = findLightClass(fqName) val actual = actualText(fqName, lightClass, normalizeText) - KotlinTestUtils.assertEqualsToFile(KotlinTestUtils.replaceExtension(testDataFile, "java"), actual) + KotlinTestUtils.assertEqualsToFile(expectedFile, actual) } private fun actualText(fqName: String?, lightClass: PsiClass?, normalizeText: (String) -> String): String { diff --git a/compiler/tests/org/jetbrains/kotlin/asJava/AbstractCompilerLightClassTest.java b/compiler/tests/org/jetbrains/kotlin/asJava/AbstractCompilerLightClassTest.java index 59b8b6d1dbd..d89b6d4f1d1 100644 --- a/compiler/tests/org/jetbrains/kotlin/asJava/AbstractCompilerLightClassTest.java +++ b/compiler/tests/org/jetbrains/kotlin/asJava/AbstractCompilerLightClassTest.java @@ -52,7 +52,8 @@ public abstract class AbstractCompilerLightClassTest extends KotlinMultiFileTest @Override protected void doMultiFileTest(File file, Map modules, List files) throws IOException { - LightClassTestCommon.INSTANCE.testLightClass(file, s -> { + File expectedFile = KotlinTestUtils.replaceExtension(file, "java"); + LightClassTestCommon.INSTANCE.testLightClass(expectedFile, file, s -> { try { return createFinder(getEnvironment()).findClass(s, GlobalSearchScope.allScope(getEnvironment().getProject())); } diff --git a/compiler/tests/org/jetbrains/kotlin/asJava/CompilerLightClassTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/asJava/CompilerLightClassTestGenerated.java index 50a15907750..fb200b945e5 100644 --- a/compiler/tests/org/jetbrains/kotlin/asJava/CompilerLightClassTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/asJava/CompilerLightClassTestGenerated.java @@ -66,12 +66,24 @@ public class CompilerLightClassTestGenerated extends AbstractCompilerLightClassT doTest(fileName); } + @TestMetadata("ExtendingInterfaceWithDefaultImpls.kt") + public void testExtendingInterfaceWithDefaultImpls() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/ExtendingInterfaceWithDefaultImpls.kt"); + doTest(fileName); + } + @TestMetadata("HiddenDeprecated.kt") public void testHiddenDeprecated() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/HiddenDeprecated.kt"); doTest(fileName); } + @TestMetadata("InheritingInterfaceDefaultImpls.kt") + public void testInheritingInterfaceDefaultImpls() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/InheritingInterfaceDefaultImpls.kt"); + doTest(fileName); + } + @TestMetadata("JvmNameOnMember.kt") public void testJvmNameOnMember() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/JvmNameOnMember.kt"); diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/java/UnimplementedKotlinInterfaceMemberAnnotator.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/java/UnimplementedKotlinInterfaceMemberAnnotator.kt new file mode 100644 index 00000000000..24a15f3c7a7 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/java/UnimplementedKotlinInterfaceMemberAnnotator.kt @@ -0,0 +1,77 @@ +/* + * 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.java + +import com.intellij.codeInsight.ClassUtil.getAnyMethodToImplement +import com.intellij.codeInsight.daemon.JavaErrorMessages +import com.intellij.codeInsight.daemon.impl.analysis.HighlightNamesUtil.getClassDeclarationTextRange +import com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil +import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil +import com.intellij.codeInsight.intention.QuickFixFactory +import com.intellij.lang.annotation.Annotation +import com.intellij.lang.annotation.AnnotationHolder +import com.intellij.lang.annotation.Annotator +import com.intellij.psi.* +import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration +import org.jetbrains.kotlin.asJava.elements.KtLightMethod +import org.jetbrains.kotlin.idea.KotlinLanguage +import org.jetbrains.kotlin.utils.ifEmpty + +class UnimplementedKotlinInterfaceMemberAnnotator : Annotator { + override fun annotate(element: PsiElement, holder: AnnotationHolder) { + if (element !is PsiClass || element.language == KotlinLanguage.INSTANCE) return + + if (element.isInterface || element.hasModifierProperty(PsiModifier.ABSTRACT)) return + + if (getAnyMethodToImplement(element) != null) return // reported by java default annotator + + findUnimplementedMethod(element)?.let { + report(it, holder, element) + } + + } + + private fun findUnimplementedMethod(psiClass: PsiClass): KtLightMethod? { + val signaturesFromKotlinInterfaces = psiClass.visibleSignatures.filter { signature -> + signature.method.let { it is KtLightMethod && it.hasModifierProperty(PsiModifier.DEFAULT) } + }.ifEmpty { return null } + + val kotlinSuperClass = generateSequence(psiClass) { it.superClass }.firstOrNull { it is KtLightClassForSourceDeclaration } + ?: return signaturesFromKotlinInterfaces.first().method as? KtLightMethod + + val signaturesVisibleThroughKotlinSuperClass = kotlinSuperClass.visibleSignatures + return signaturesFromKotlinInterfaces.firstOrNull { it !in signaturesVisibleThroughKotlinSuperClass }?.method as? KtLightMethod + } + + private fun report(method: KtLightMethod, holder: AnnotationHolder, psiClass: PsiClass) { + val key = if (psiClass is PsiEnumConstantInitializer) "enum.constant.should.implement.method" else "class.must.be.abstract" + val message = JavaErrorMessages.message(key, HighlightUtil.formatClass(psiClass, false), JavaHighlightUtil.formatMethod(method), + HighlightUtil.formatClass(method.containingClass, false)) + val errorAnnotation = holder.createErrorAnnotation(getClassDeclarationTextRange(psiClass), message) + registerFixes(errorAnnotation, psiClass) + } + + private fun registerFixes(errorAnnotation: Annotation, psiClass: PsiClass) { + val quickFixFactory = QuickFixFactory.getInstance() + // this code is untested + // see com.intellij.codeInsight.daemon.impl.analysis.HighlightClassUtil.checkClassWithAbstractMethods + errorAnnotation.registerFix(quickFixFactory.createImplementMethodsFix(psiClass)) + if (psiClass !is PsiAnonymousClass && !(psiClass.modifierList?.hasExplicitModifier(PsiModifier.FINAL) ?: false)) { + errorAnnotation.registerFix(quickFixFactory.createModifierListFix(psiClass, PsiModifier.ABSTRACT, true, false)) + } + } +} \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 5a2e5562969..7c6c492fa53 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -516,6 +516,8 @@ + + diff --git a/idea/testData/codeInsight/lineMarker/overrideImplement/FakeOverrideToStringInTrait.kt b/idea/testData/codeInsight/lineMarker/overrideImplement/FakeOverrideToStringInTrait.kt index 38caac214df..00e46872938 100644 --- a/idea/testData/codeInsight/lineMarker/overrideImplement/FakeOverrideToStringInTrait.kt +++ b/idea/testData/codeInsight/lineMarker/overrideImplement/FakeOverrideToStringInTrait.kt @@ -1,5 +1,5 @@ interface A { - override fun toString() = "A" + override fun toString() = "A" } abstract class B : A diff --git a/idea/testData/codeInsight/lineMarker/overrideImplement/FakeOverridesForTraitFunWithImpl.kt b/idea/testData/codeInsight/lineMarker/overrideImplement/FakeOverridesForTraitFunWithImpl.kt index cb48b5d7041..a98a4db45cf 100644 --- a/idea/testData/codeInsight/lineMarker/overrideImplement/FakeOverridesForTraitFunWithImpl.kt +++ b/idea/testData/codeInsight/lineMarker/overrideImplement/FakeOverridesForTraitFunWithImpl.kt @@ -1,11 +1,9 @@ interface A { - fun foo(): String = "A" + fun foo(): String = "A" - // TODO: B shoudn't be mentioned - val some: String? get() = null + val some: String? get() = null - // TODO: B shoudn't be mentioned - var other: String? + var other: String? get() = null set(value) {} } diff --git a/idea/testData/codeInsight/lineMarker/overrideImplement/OverrideIconForOverloadMethodBug.kt b/idea/testData/codeInsight/lineMarker/overrideImplement/OverrideIconForOverloadMethodBug.kt index a5f80817c1e..5f33a73a3fb 100644 --- a/idea/testData/codeInsight/lineMarker/overrideImplement/OverrideIconForOverloadMethodBug.kt +++ b/idea/testData/codeInsight/lineMarker/overrideImplement/OverrideIconForOverloadMethodBug.kt @@ -4,8 +4,7 @@ interface SkipSupport { } public interface SkipSupportWithDefaults : SkipSupport { - // TODO: should be "Is overriden in SkipSupportImpl" - override fun skip(why: String) {} + override fun skip(why: String) {} override fun skip() { skip("not given") diff --git a/idea/testData/codeInsight/lineMarker/overrideImplement/OverridingTooltipOnDefaultTraitImpl.kt b/idea/testData/codeInsight/lineMarker/overrideImplement/OverridingTooltipOnDefaultTraitImpl.kt index e50b324e27e..2a50fe34143 100644 --- a/idea/testData/codeInsight/lineMarker/overrideImplement/OverridingTooltipOnDefaultTraitImpl.kt +++ b/idea/testData/codeInsight/lineMarker/overrideImplement/OverridingTooltipOnDefaultTraitImpl.kt @@ -3,7 +3,7 @@ interface SkipSupport { } public interface SkipSupportWithDefaults : SkipSupport { - override fun skip() {} + override fun skip() {} } public interface SkipSupportImpl1 : SkipSupportWithDefaults { diff --git a/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementationComplex.java b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementationComplex.java new file mode 100644 index 00000000000..6a7a3c103ec --- /dev/null +++ b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementationComplex.java @@ -0,0 +1,39 @@ +package test; + +public class ExtendClassWithDefaultImplementationComplext { + public static class Test1 implements A { + + } + + public static class Test2 extends AI implements A { + + } + + public static class Test3 extends AI implements B { + + } + + public static class Test4 extends BI implements B { + + } + + public static class Test5 extends BI implements C { + + } + + public static class Test6 extends BI implements D { + + } + + public static class Test7 extends BI implements S { + + } + + public static interface Test8 extends A { + + } + + public static abstract class Test9 implements A { + + } +} diff --git a/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementationComplex.kt b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementationComplex.kt new file mode 100644 index 00000000000..ba4dff26cae --- /dev/null +++ b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementationComplex.kt @@ -0,0 +1,26 @@ +package test + +interface A { + fun a() = Unit +} + +open class AI : A + +interface B : A { + fun b() = Unit +} + +open class BI: B + +interface C: B { + fun c() = Unit +} + +interface D { + fun d() = Unit +} + +interface S { + fun a() = Unit + fun b() = Unit +} \ No newline at end of file diff --git a/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementation_1_6.java b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementation_1_6.java new file mode 100644 index 00000000000..42d3d4b29d3 --- /dev/null +++ b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementation_1_6.java @@ -0,0 +1,14 @@ +package test; + +public class ExtendClassWithDefaultImplementation_1_6 { + public static class ExtendClass extends KotlinClass { + + } + + public static class ImplementInterface implements KotlinInterface { + @Override + public void f() { + + } + } +} diff --git a/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementation_1_6.kt b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementation_1_6.kt new file mode 100644 index 00000000000..c1fc147d957 --- /dev/null +++ b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementation_1_6.kt @@ -0,0 +1,20 @@ +package test + +interface KotlinInterface { + fun foo() { + + } + + fun bar() { + + } + + fun f() +} + + +abstract class KotlinClass : KotlinInterface { + override fun f() { + + } +} \ No newline at end of file diff --git a/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementation_1_6.txt b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementation_1_6.txt new file mode 100644 index 00000000000..9b64cffb2d1 --- /dev/null +++ b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementation_1_6.txt @@ -0,0 +1 @@ +// LANGUAGE_LEVEL 1.6 \ No newline at end of file diff --git a/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementation_1_8.java b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementation_1_8.java new file mode 100644 index 00000000000..1d048adb61f --- /dev/null +++ b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementation_1_8.java @@ -0,0 +1,14 @@ +package test; + +public class ExtendClassWithDefaultImplementation_1_8 { + public static class ExtendClass extends KotlinClass { + + } + + public static class ImplementInterface implements KotlinInterface { + @Override + public void f() { + + } + } +} diff --git a/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementation_1_8.kt b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementation_1_8.kt new file mode 100644 index 00000000000..c1fc147d957 --- /dev/null +++ b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementation_1_8.kt @@ -0,0 +1,20 @@ +package test + +interface KotlinInterface { + fun foo() { + + } + + fun bar() { + + } + + fun f() +} + + +abstract class KotlinClass : KotlinInterface { + override fun f() { + + } +} \ No newline at end of file diff --git a/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementation_1_8.txt b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementation_1_8.txt new file mode 100644 index 00000000000..16770180714 --- /dev/null +++ b/idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementation_1_8.txt @@ -0,0 +1 @@ +// LANGUAGE_LEVEL 1.8 \ 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 d32cd9361a8..ee2e1db92a5 100644 --- a/idea/tests/org/jetbrains/kotlin/checkers/JavaAgainstKotlinBinariesCheckerTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/checkers/JavaAgainstKotlinBinariesCheckerTestGenerated.java @@ -72,6 +72,24 @@ public class JavaAgainstKotlinBinariesCheckerTestGenerated extends AbstractJavaA doTest(fileName); } + @TestMetadata("ExtendClassWithDefaultImplementationComplex.kt") + public void testExtendClassWithDefaultImplementationComplex() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementationComplex.kt"); + doTest(fileName); + } + + @TestMetadata("ExtendClassWithDefaultImplementation_1_6.kt") + public void testExtendClassWithDefaultImplementation_1_6() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementation_1_6.kt"); + doTest(fileName); + } + + @TestMetadata("ExtendClassWithDefaultImplementation_1_8.kt") + public void testExtendClassWithDefaultImplementation_1_8() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementation_1_8.kt"); + doTest(fileName); + } + @TestMetadata("FunctionInNestedClassInDataFlowInspection.kt") public void testFunctionInNestedClassInDataFlowInspection() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/FunctionInNestedClassInDataFlowInspection.kt"); diff --git a/idea/tests/org/jetbrains/kotlin/checkers/JavaAgainstKotlinSourceCheckerTestGenerated.java b/idea/tests/org/jetbrains/kotlin/checkers/JavaAgainstKotlinSourceCheckerTestGenerated.java index bbb792dcc22..c4784fba6d3 100644 --- a/idea/tests/org/jetbrains/kotlin/checkers/JavaAgainstKotlinSourceCheckerTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/checkers/JavaAgainstKotlinSourceCheckerTestGenerated.java @@ -74,6 +74,24 @@ public class JavaAgainstKotlinSourceCheckerTestGenerated extends AbstractJavaAga doTest(fileName); } + @TestMetadata("ExtendClassWithDefaultImplementationComplex.kt") + public void testExtendClassWithDefaultImplementationComplex() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementationComplex.kt"); + doTest(fileName); + } + + @TestMetadata("ExtendClassWithDefaultImplementation_1_6.kt") + public void testExtendClassWithDefaultImplementation_1_6() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementation_1_6.kt"); + doTest(fileName); + } + + @TestMetadata("ExtendClassWithDefaultImplementation_1_8.kt") + public void testExtendClassWithDefaultImplementation_1_8() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/ExtendClassWithDefaultImplementation_1_8.kt"); + doTest(fileName); + } + @TestMetadata("FunctionInNestedClassInDataFlowInspection.kt") public void testFunctionInNestedClassInDataFlowInspection() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/FunctionInNestedClassInDataFlowInspection.kt"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/AbstractIdeLightClassTest.kt b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/AbstractIdeLightClassTest.kt index 3852cc3e9f6..312b1e6693a 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/AbstractIdeLightClassTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/AbstractIdeLightClassTest.kt @@ -56,7 +56,8 @@ abstract class AbstractIdeLightClassTest : KotlinLightCodeInsightFixtureTestCase myFixture.configureByFiles(*testFiles.toTypedArray()) val ktFile = myFixture.file as KtFile - testLightClass(testDataPath, { LightClassTestCommon.removeEmptyDefaultImpls(it) }) { fqName -> + val testData = File(testDataPath) + testLightClass(KotlinTestUtils.replaceExtension(testData, "java"), testData, { LightClassTestCommon.removeEmptyDefaultImpls(it) }, { fqName -> val tracker = LightClassLazinessChecker.Tracker(fqName) project.withServiceRegistered(tracker) { findClass(fqName, ktFile, project)?.apply { @@ -65,7 +66,7 @@ abstract class AbstractIdeLightClassTest : KotlinLightCodeInsightFixtureTestCase PsiElementChecker.checkPsiElementStructure(this) } } - } + }) } private fun lazinessModeByFileText(testDataPath: String): LightClassLazinessChecker.Mode { @@ -97,17 +98,22 @@ abstract class AbstractIdeCompiledLightClassTest : KotlinDaemonAnalyzerTestCase( private fun libName() = "libFor" + getTestName(false) fun doTest(testDataPath: String) { - testLightClass(testDataPath, { it }) { + val testDataFile = File(testDataPath) + val expectedFile = KotlinTestUtils.replaceExtension( + testDataFile, "compiled.java" + ).let { if (it.exists()) it else KotlinTestUtils.replaceExtension(testDataFile, "java") } + testLightClass(expectedFile, testDataFile, { it }, { findClass(it, null, project)?.apply { PsiElementChecker.checkPsiElementStructure(this) } - } + }) } } -private fun testLightClass(testDataPath: String, normalize: (String) -> String, findLightClass: (String) -> PsiClass?) { +private fun testLightClass(expected: File, testData: File, normalize: (String) -> String, findLightClass: (String) -> PsiClass?) { LightClassTestCommon.testLightClass( - File(testDataPath), + expected, + testData, findLightClass, normalizeText = { text -> //NOTE: ide and compiler differ in names generated for parameters with unspecified names @@ -116,6 +122,7 @@ private fun testLightClass(testDataPath: String, normalize: (String) -> String, .replace("java.lang.String s)", "java.lang.String p)") .replace("java.lang.String s1", "java.lang.String p1") .replace("java.lang.String s2", "java.lang.String p2") + .replace("java.lang.Object o)", "java.lang.Object p)") .removeLinesStartingWith("@" + JvmAnnotationNames.METADATA_FQ_NAME.asString()) .run(normalize) } @@ -243,11 +250,18 @@ object LightClassLazinessChecker { private fun methodInfo(method: PsiMethod) = with(method) { MethodInfo( - name, PsiModifier.MODIFIERS.asList().filter { modifierList.hasModifierProperty(it) }, + name, relevantModifiers(), isConstructor, method.parameterList.parametersCount, isVarArgs ) } + private fun PsiMethod.relevantModifiers() + = when { containingClass!!.isInterface -> + PsiModifier.MODIFIERS.filter { it != PsiModifier.ABSTRACT && it != PsiModifier.DEFAULT } + else -> PsiModifier.MODIFIERS.asList() + }.filter { modifierList.hasModifierProperty(it) } + + private fun Array.names() = mapTo(LinkedHashSet()) { it.name!! } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/IdeCompiledLightClassTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/IdeCompiledLightClassTestGenerated.java index 35372a59fcd..9de9c49d2a2 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/IdeCompiledLightClassTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/IdeCompiledLightClassTestGenerated.java @@ -66,12 +66,24 @@ public class IdeCompiledLightClassTestGenerated extends AbstractIdeCompiledLight doTest(fileName); } + @TestMetadata("ExtendingInterfaceWithDefaultImpls.kt") + public void testExtendingInterfaceWithDefaultImpls() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/ExtendingInterfaceWithDefaultImpls.kt"); + doTest(fileName); + } + @TestMetadata("HiddenDeprecated.kt") public void testHiddenDeprecated() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/HiddenDeprecated.kt"); doTest(fileName); } + @TestMetadata("InheritingInterfaceDefaultImpls.kt") + public void testInheritingInterfaceDefaultImpls() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/InheritingInterfaceDefaultImpls.kt"); + doTest(fileName); + } + @TestMetadata("JvmNameOnMember.kt") public void testJvmNameOnMember() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/JvmNameOnMember.kt"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/IdeLightClassTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/IdeLightClassTestGenerated.java index aa1e62c284b..d5900f4ca6a 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/IdeLightClassTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/IdeLightClassTestGenerated.java @@ -66,12 +66,24 @@ public class IdeLightClassTestGenerated extends AbstractIdeLightClassTest { doTest(fileName); } + @TestMetadata("ExtendingInterfaceWithDefaultImpls.kt") + public void testExtendingInterfaceWithDefaultImpls() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/ExtendingInterfaceWithDefaultImpls.kt"); + doTest(fileName); + } + @TestMetadata("HiddenDeprecated.kt") public void testHiddenDeprecated() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/HiddenDeprecated.kt"); doTest(fileName); } + @TestMetadata("InheritingInterfaceDefaultImpls.kt") + public void testInheritingInterfaceDefaultImpls() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/InheritingInterfaceDefaultImpls.kt"); + doTest(fileName); + } + @TestMetadata("JvmNameOnMember.kt") public void testJvmNameOnMember() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/JvmNameOnMember.kt"); diff --git a/plugins/annotation-processing/testData/wrappers/kotlinWrappers/nestedClasses.txt b/plugins/annotation-processing/testData/wrappers/kotlinWrappers/nestedClasses.txt index eec19262b77..65daf606ae5 100644 --- a/plugins/annotation-processing/testData/wrappers/kotlinWrappers/nestedClasses.txt +++ b/plugins/annotation-processing/testData/wrappers/kotlinWrappers/nestedClasses.txt @@ -44,7 +44,7 @@ public final class MyClass { public static abstract interface InnerInterface { public abstract void innerInterfaceFun() - public abstract int innerInterfaceFunWithImpl() + public default int innerInterfaceFunWithImpl() public static final class DefaultImpls { public static int innerInterfaceFunWithImpl(MyClass.InnerInterface $this) diff --git a/plugins/uast-kotlin/testData/DefaultImpls.render.txt b/plugins/uast-kotlin/testData/DefaultImpls.render.txt index 1e912f7b081..347816e5e07 100644 --- a/plugins/uast-kotlin/testData/DefaultImpls.render.txt +++ b/plugins/uast-kotlin/testData/DefaultImpls.render.txt @@ -1,5 +1,5 @@ public abstract interface Foo { - public abstract fun bar() : java.lang.String = "Hello!" + public default fun bar() : java.lang.String = "Hello!" } public final class Baz : Foo {