Light class builder: do not generate methods delegating to DefaultImpls in kotlin classes
Class APIs from java point of view stays the same so we can avoid generating those methods
Otherwise we have to calculate all supertypes when getMethods() is called,
which imposes severe performance penalties
We have to pretend these methods are not 'abstract' (also we consider them 'default' for safety)
so java highlighting does not report "class should be abstract" for all inheritors
We have to manually report "class should be abstract" on some of the java inheritors,
specifically those that are implementing interfaces directly
as opposed to extending kotlin classes implementing those interfaces
This commit is contained in:
@@ -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.JAVA_STRING_TYPE;
|
||||||
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_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.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.Variance.INVARIANT;
|
||||||
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isLocalFunction;
|
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isLocalFunction;
|
||||||
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
|
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
|
||||||
@@ -1282,14 +1283,15 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
|||||||
CodegenUtilKt.reportTarget6InheritanceErrorIfNeeded(descriptor, myClass.getPsiOrParent(), restrictedInheritance, state);
|
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(
|
functionCodegen.generateMethod(
|
||||||
JvmDeclarationOriginKt.DelegationToDefaultImpls(descriptorToDeclaration(traitFun), traitFun),
|
new JvmDeclarationOrigin(CLASS_MEMBER_DELEGATION_TO_DEFAULT_IMPL, descriptorToDeclaration(interfaceFun), interfaceFun),
|
||||||
inheritedFun,
|
inheritedFun,
|
||||||
new FunctionGenerationStrategy.CodegenBased(state) {
|
new FunctionGenerationStrategy.CodegenBased(state) {
|
||||||
@Override
|
@Override
|
||||||
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
|
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
|
||||||
DeclarationDescriptor containingDeclaration = traitFun.getContainingDeclaration();
|
DeclarationDescriptor containingDeclaration = interfaceFun.getContainingDeclaration();
|
||||||
if (!DescriptorUtils.isInterface(containingDeclaration)) return;
|
if (!DescriptorUtils.isInterface(containingDeclaration)) return;
|
||||||
|
|
||||||
DeclarationDescriptor declarationInheritedFun = inheritedFun.getContainingDeclaration();
|
DeclarationDescriptor declarationInheritedFun = inheritedFun.getContainingDeclaration();
|
||||||
@@ -1301,12 +1303,12 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
|||||||
ClassDescriptor containingTrait = (ClassDescriptor) containingDeclaration;
|
ClassDescriptor containingTrait = (ClassDescriptor) containingDeclaration;
|
||||||
Type traitImplType = typeMapper.mapDefaultImpls(containingTrait);
|
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[] argTypes = signature.getAsmMethod().getArgumentTypes();
|
||||||
Type[] originalArgTypes = traitMethod.getArgumentTypes();
|
Type[] originalArgTypes = traitMethod.getArgumentTypes();
|
||||||
assert originalArgTypes.length == argTypes.length + 1 :
|
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;
|
InstructionAdapter iv = codegen.v;
|
||||||
iv.load(0, OBJECT_TYPE);
|
iv.load(0, OBJECT_TYPE);
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ import org.jetbrains.kotlin.name.Name
|
|||||||
import org.jetbrains.kotlin.psi.KtPureClassOrObject
|
import org.jetbrains.kotlin.psi.KtPureClassOrObject
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
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.JvmDeclarationOrigin
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||||
@@ -113,7 +113,10 @@ class InterfaceImplBodyCodegen(
|
|||||||
if (delegateTo is JavaMethodDescriptor) return
|
if (delegateTo is JavaMethodDescriptor) return
|
||||||
|
|
||||||
functionCodegen.generateMethod(
|
functionCodegen.generateMethod(
|
||||||
DelegationToDefaultImpls(DescriptorToSourceUtils.descriptorToDeclaration(descriptor), descriptor),
|
JvmDeclarationOrigin(
|
||||||
|
JvmDeclarationOriginKind.DEFAULT_IMPL_DELEGATION_TO_SUPERINTERFACE_DEFAULT_IMPL,
|
||||||
|
DescriptorToSourceUtils.descriptorToDeclaration(descriptor), descriptor
|
||||||
|
),
|
||||||
descriptor,
|
descriptor,
|
||||||
object : FunctionGenerationStrategy.CodegenBased(state) {
|
object : FunctionGenerationStrategy.CodegenBased(state) {
|
||||||
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||||
|
|||||||
+7
-1
@@ -27,7 +27,7 @@ import org.jetbrains.org.objectweb.asm.FieldVisitor
|
|||||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||||
|
|
||||||
abstract class SignatureCollectingClassBuilderFactory(
|
abstract class SignatureCollectingClassBuilderFactory(
|
||||||
delegate: ClassBuilderFactory
|
delegate: ClassBuilderFactory, val shouldGenerate: (JvmDeclarationOrigin) -> Boolean
|
||||||
) : DelegatingClassBuilderFactory(delegate) {
|
) : DelegatingClassBuilderFactory(delegate) {
|
||||||
|
|
||||||
protected abstract fun handleClashingSignatures(data: ConflictingJvmDeclarationsData)
|
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 {
|
override fun newField(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor {
|
||||||
signatures.putValue(RawSignature(name, desc, MemberKind.FIELD), origin)
|
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)
|
return super.newField(origin, access, name, desc, signature, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun newMethod(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?): MethodVisitor {
|
override fun newMethod(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?): MethodVisitor {
|
||||||
signatures.putValue(RawSignature(name, desc, MemberKind.METHOD), origin)
|
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)
|
return super.newMethod(origin, access, name, desc, signature, exceptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-3
@@ -37,7 +37,7 @@ import org.jetbrains.kotlin.utils.addIfNotNull
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
private val EXTERNAL_SOURCES_KINDS = arrayOf(
|
private val EXTERNAL_SOURCES_KINDS = arrayOf(
|
||||||
JvmDeclarationOriginKind.DELEGATION_TO_DEFAULT_IMPLS,
|
JvmDeclarationOriginKind.CLASS_MEMBER_DELEGATION_TO_DEFAULT_IMPL,
|
||||||
JvmDeclarationOriginKind.DELEGATION,
|
JvmDeclarationOriginKind.DELEGATION,
|
||||||
JvmDeclarationOriginKind.BRIDGE
|
JvmDeclarationOriginKind.BRIDGE
|
||||||
)
|
)
|
||||||
@@ -58,8 +58,9 @@ class BuilderFactoryForDuplicateSignatureDiagnostics(
|
|||||||
bindingContext: BindingContext,
|
bindingContext: BindingContext,
|
||||||
private val diagnostics: DiagnosticSink,
|
private val diagnostics: DiagnosticSink,
|
||||||
fileClassesProvider: JvmFileClassesProvider,
|
fileClassesProvider: JvmFileClassesProvider,
|
||||||
moduleName: String
|
moduleName: String,
|
||||||
) : SignatureCollectingClassBuilderFactory(builderFactory) {
|
shouldGenerate: (JvmDeclarationOrigin) -> Boolean
|
||||||
|
) : SignatureCollectingClassBuilderFactory(builderFactory, shouldGenerate) {
|
||||||
|
|
||||||
// Avoid errors when some classes are not loaded for some reason
|
// Avoid errors when some classes are not loaded for some reason
|
||||||
private val typeMapper = KotlinTypeMapper(
|
private val typeMapper = KotlinTypeMapper(
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ import org.jetbrains.kotlin.psi.KtScript
|
|||||||
import org.jetbrains.kotlin.resolve.*
|
import org.jetbrains.kotlin.resolve.*
|
||||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
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 org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@@ -177,8 +179,10 @@ class GenerationState @JvmOverloads constructor(
|
|||||||
{ OptimizationClassBuilderFactory(it, configuration.get(JVMConfigurationKeys.DISABLE_OPTIMIZATION, false)) },
|
{ OptimizationClassBuilderFactory(it, configuration.get(JVMConfigurationKeys.DISABLE_OPTIMIZATION, false)) },
|
||||||
::CoroutineTransformerClassBuilderFactory,
|
::CoroutineTransformerClassBuilderFactory,
|
||||||
{ BuilderFactoryForDuplicateSignatureDiagnostics(
|
{ BuilderFactoryForDuplicateSignatureDiagnostics(
|
||||||
it, this.bindingContext, diagnostics, fileClassesProvider, this.moduleName
|
it, this.bindingContext, diagnostics,
|
||||||
).apply { duplicateSignatureFactory = this } },
|
fileClassesProvider, this.moduleName,
|
||||||
|
shouldGenerate = { !shouldOnlyCollectSignatures(it) }
|
||||||
|
).apply { duplicateSignatureFactory = this } },
|
||||||
{ BuilderFactoryForDuplicateClassNameDiagnostics(it, diagnostics) },
|
{ BuilderFactoryForDuplicateClassNameDiagnostics(it, diagnostics) },
|
||||||
{ configuration.get(JVMConfigurationKeys.DECLARATIONS_JSON_PATH)
|
{ configuration.get(JVMConfigurationKeys.DECLARATIONS_JSON_PATH)
|
||||||
?.let { destination -> SignatureDumpingBuilderFactory(it, File(destination)) } ?: it }
|
?.let { destination -> SignatureDumpingBuilderFactory(it, File(destination)) } ?: it }
|
||||||
@@ -209,6 +213,9 @@ class GenerationState @JvmOverloads constructor(
|
|||||||
fun destroy() {
|
fun destroy() {
|
||||||
interceptedBuilderFactory.close()
|
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 {
|
private class LazyJvmDiagnostics(compute: () -> Diagnostics): Diagnostics {
|
||||||
|
|||||||
+2
-3
@@ -32,7 +32,8 @@ enum class JvmDeclarationOriginKind {
|
|||||||
OTHER,
|
OTHER,
|
||||||
PACKAGE_PART,
|
PACKAGE_PART,
|
||||||
INTERFACE_DEFAULT_IMPL,
|
INTERFACE_DEFAULT_IMPL,
|
||||||
DELEGATION_TO_DEFAULT_IMPLS,
|
CLASS_MEMBER_DELEGATION_TO_DEFAULT_IMPL,
|
||||||
|
DEFAULT_IMPL_DELEGATION_TO_SUPERINTERFACE_DEFAULT_IMPL,
|
||||||
DELEGATION,
|
DELEGATION,
|
||||||
SAM_DELEGATION,
|
SAM_DELEGATION,
|
||||||
BRIDGE,
|
BRIDGE,
|
||||||
@@ -84,8 +85,6 @@ fun MultifileClassPart(file: KtFile, descriptor: PackageFragmentDescriptor): Jvm
|
|||||||
JvmDeclarationOrigin(MULTIFILE_CLASS_PART, file, descriptor)
|
JvmDeclarationOrigin(MULTIFILE_CLASS_PART, file, descriptor)
|
||||||
|
|
||||||
fun DefaultImpls(element: PsiElement?, descriptor: ClassDescriptor): JvmDeclarationOrigin = JvmDeclarationOrigin(INTERFACE_DEFAULT_IMPL, element, 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)
|
fun Delegation(element: PsiElement?, descriptor: FunctionDescriptor): JvmDeclarationOrigin = JvmDeclarationOrigin(DELEGATION, element, descriptor)
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.asJava.elements
|
|||||||
import com.intellij.psi.*
|
import com.intellij.psi.*
|
||||||
import com.intellij.psi.impl.PsiVariableEx
|
import com.intellij.psi.impl.PsiVariableEx
|
||||||
import org.jetbrains.kotlin.asJava.builder.LightMemberOrigin
|
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.KtDeclaration
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
|
||||||
@@ -33,6 +34,8 @@ interface KtLightDeclaration<out T : KtDeclaration, out D : PsiElement> : KtLigh
|
|||||||
|
|
||||||
interface KtLightMember<out D : PsiMember> : PsiMember, KtLightDeclaration<KtDeclaration, D>, PsiNameIdentifierOwner, PsiDocCommentOwner {
|
interface KtLightMember<out D : PsiMember> : PsiMember, KtLightDeclaration<KtDeclaration, D>, PsiNameIdentifierOwner, PsiDocCommentOwner {
|
||||||
val lightMemberOrigin: LightMemberOrigin?
|
val lightMemberOrigin: LightMemberOrigin?
|
||||||
|
|
||||||
|
override fun getContainingClass(): KtLightClass
|
||||||
}
|
}
|
||||||
|
|
||||||
interface KtLightField : PsiField, KtLightMember<PsiField>, PsiVariableEx
|
interface KtLightField : PsiField, KtLightMember<PsiField>, PsiVariableEx
|
||||||
@@ -40,5 +43,5 @@ interface KtLightField : PsiField, KtLightMember<PsiField>, PsiVariableEx
|
|||||||
interface KtLightMethod : PsiAnnotationMethod, KtLightMember<PsiMethod> {
|
interface KtLightMethod : PsiAnnotationMethod, KtLightMember<PsiMethod> {
|
||||||
val isDelegated: Boolean
|
val isDelegated: Boolean
|
||||||
get() = lightMemberOrigin?.originKind == JvmDeclarationOriginKind.DELEGATION
|
get() = lightMemberOrigin?.originKind == JvmDeclarationOriginKind.DELEGATION
|
||||||
|| lightMemberOrigin?.originKind == JvmDeclarationOriginKind.DELEGATION_TO_DEFAULT_IMPLS
|
|| lightMemberOrigin?.originKind == JvmDeclarationOriginKind.CLASS_MEMBER_DELEGATION_TO_DEFAULT_IMPL
|
||||||
}
|
}
|
||||||
+15
-8
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.idea.KotlinLanguage
|
|||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.hasBody
|
||||||
|
|
||||||
abstract class KtLightMemberImpl<out D : PsiMember>(
|
abstract class KtLightMemberImpl<out D : PsiMember>(
|
||||||
computeRealDelegate: () -> D,
|
computeRealDelegate: () -> D,
|
||||||
@@ -104,18 +105,24 @@ class KtLightModifierList(
|
|||||||
private val clsDelegate by lazyPub { owner.clsDelegate.modifierList!! }
|
private val clsDelegate by lazyPub { owner.clsDelegate.modifierList!! }
|
||||||
private val _annotations by lazyPub { computeAnnotations(this, clsDelegate) }
|
private val _annotations by lazyPub { computeAnnotations(this, clsDelegate) }
|
||||||
|
|
||||||
override fun hasModifierProperty(name: String): Boolean {
|
override fun hasModifierProperty(name: String) = when {
|
||||||
if (dummyDelegate != null) {
|
name == PsiModifier.ABSTRACT && isImplementationInInterface() -> false
|
||||||
if (name in visibilityModifiers) {
|
name == PsiModifier.DEFAULT && isImplementationInInterface() -> true
|
||||||
if (owner.kotlinOrigin?.hasModifier(KtTokens.OVERRIDE_KEYWORD) ?: false) {
|
dummyDelegate != null -> {
|
||||||
return clsDelegate.hasModifierProperty(name)
|
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 hasExplicitModifier(name: String) = hasModifierProperty(name)
|
||||||
|
|
||||||
override fun setModifierProperty(name: String, value: Boolean) = clsDelegate.setModifierProperty(name, value)
|
override fun setModifierProperty(name: String, value: Boolean) = clsDelegate.setModifierProperty(name, value)
|
||||||
|
|||||||
+70
@@ -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 */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -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 */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// p.B
|
||||||
|
package p
|
||||||
|
|
||||||
|
interface A {
|
||||||
|
fun a() = "a"
|
||||||
|
}
|
||||||
|
|
||||||
|
interface B: A {
|
||||||
|
fun b() = "b"
|
||||||
|
}
|
||||||
+13
@@ -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 */ }
|
||||||
|
}
|
||||||
@@ -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 */ }
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -30,6 +30,7 @@ object LightClassTestCommon {
|
|||||||
|
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun testLightClass(
|
fun testLightClass(
|
||||||
|
expectedFile: File,
|
||||||
testDataFile: File,
|
testDataFile: File,
|
||||||
findLightClass: (String) -> PsiClass?,
|
findLightClass: (String) -> PsiClass?,
|
||||||
normalizeText: (String) -> String = { it }
|
normalizeText: (String) -> String = { it }
|
||||||
@@ -42,7 +43,7 @@ object LightClassTestCommon {
|
|||||||
val lightClass = findLightClass(fqName)
|
val lightClass = findLightClass(fqName)
|
||||||
|
|
||||||
val actual = actualText(fqName, lightClass, normalizeText)
|
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 {
|
private fun actualText(fqName: String?, lightClass: PsiClass?, normalizeText: (String) -> String): String {
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ public abstract class AbstractCompilerLightClassTest extends KotlinMultiFileTest
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doMultiFileTest(File file, Map<String, ModuleAndDependencies> modules, List<Void> files) throws IOException {
|
protected void doMultiFileTest(File file, Map<String, ModuleAndDependencies> modules, List<Void> files) throws IOException {
|
||||||
LightClassTestCommon.INSTANCE.testLightClass(file, s -> {
|
File expectedFile = KotlinTestUtils.replaceExtension(file, "java");
|
||||||
|
LightClassTestCommon.INSTANCE.testLightClass(expectedFile, file, s -> {
|
||||||
try {
|
try {
|
||||||
return createFinder(getEnvironment()).findClass(s, GlobalSearchScope.allScope(getEnvironment().getProject()));
|
return createFinder(getEnvironment()).findClass(s, GlobalSearchScope.allScope(getEnvironment().getProject()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,12 +66,24 @@ public class CompilerLightClassTestGenerated extends AbstractCompilerLightClassT
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("HiddenDeprecated.kt")
|
||||||
public void testHiddenDeprecated() throws Exception {
|
public void testHiddenDeprecated() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/HiddenDeprecated.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/HiddenDeprecated.kt");
|
||||||
doTest(fileName);
|
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")
|
@TestMetadata("JvmNameOnMember.kt")
|
||||||
public void testJvmNameOnMember() throws Exception {
|
public void testJvmNameOnMember() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/JvmNameOnMember.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/JvmNameOnMember.kt");
|
||||||
|
|||||||
+77
@@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -516,6 +516,8 @@
|
|||||||
<annotator language="kotlin" implementationClass="org.jetbrains.kotlin.idea.highlighter.PlatformHeaderAnnotator"/>
|
<annotator language="kotlin" implementationClass="org.jetbrains.kotlin.idea.highlighter.PlatformHeaderAnnotator"/>
|
||||||
<problemHighlightFilter implementation="org.jetbrains.kotlin.idea.highlighter.KotlinProblemHighlightFilter"/>
|
<problemHighlightFilter implementation="org.jetbrains.kotlin.idea.highlighter.KotlinProblemHighlightFilter"/>
|
||||||
|
|
||||||
|
<annotator language="JAVA" implementationClass="org.jetbrains.kotlin.idea.java.UnimplementedKotlinInterfaceMemberAnnotator"/>
|
||||||
|
|
||||||
<extendWordSelectionHandler implementation="org.jetbrains.kotlin.idea.editor.wordSelection.KotlinStatementGroupSelectioner"/>
|
<extendWordSelectionHandler implementation="org.jetbrains.kotlin.idea.editor.wordSelection.KotlinStatementGroupSelectioner"/>
|
||||||
<extendWordSelectionHandler implementation="org.jetbrains.kotlin.idea.editor.wordSelection.KotlinCodeBlockSelectioner"/>
|
<extendWordSelectionHandler implementation="org.jetbrains.kotlin.idea.editor.wordSelection.KotlinCodeBlockSelectioner"/>
|
||||||
<extendWordSelectionHandler implementation="org.jetbrains.kotlin.idea.editor.wordSelection.KotlinDocCommentSelectioner"/>
|
<extendWordSelectionHandler implementation="org.jetbrains.kotlin.idea.editor.wordSelection.KotlinDocCommentSelectioner"/>
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
interface <lineMarker descr="*">A</lineMarker> {
|
interface <lineMarker descr="*">A</lineMarker> {
|
||||||
override fun <lineMarker descr="<html><body>Is implemented in <br> C</body></html>"><lineMarker descr="*">toString</lineMarker></lineMarker>() = "A"
|
override fun <lineMarker descr="<html><body>Is overridden in <br> C</body></html>"><lineMarker descr="*">toString</lineMarker></lineMarker>() = "A"
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class <lineMarker descr="*">B</lineMarker> : A
|
abstract class <lineMarker descr="*">B</lineMarker> : A
|
||||||
|
|||||||
+3
-5
@@ -1,11 +1,9 @@
|
|||||||
interface <lineMarker descr="*">A</lineMarker> {
|
interface <lineMarker descr="*">A</lineMarker> {
|
||||||
fun <lineMarker descr="<html><body>Is implemented in <br> C</body></html>">foo</lineMarker>(): String = "A"
|
fun <lineMarker descr="<html><body>Is overridden in <br> C</body></html>">foo</lineMarker>(): String = "A"
|
||||||
|
|
||||||
// TODO: B shoudn't be mentioned
|
val <lineMarker descr="<html><body>Is implemented in <br/> C</body></html>">some</lineMarker>: String? get() = null
|
||||||
val <lineMarker descr="<html><body>Is implemented in <br/> B<br> C</body></html>">some</lineMarker>: String? get() = null
|
|
||||||
|
|
||||||
// TODO: B shoudn't be mentioned
|
var <lineMarker descr="<html><body>Is implemented in <br/> C</body></html>">other</lineMarker>: String?
|
||||||
var <lineMarker descr="<html><body>Is implemented in <br/> B<br> C</body></html>">other</lineMarker>: String?
|
|
||||||
get() = null
|
get() = null
|
||||||
set(value) {}
|
set(value) {}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -4,8 +4,7 @@ interface <lineMarker descr="*">SkipSupport</lineMarker> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public interface <lineMarker descr="*">SkipSupportWithDefaults</lineMarker> : SkipSupport {
|
public interface <lineMarker descr="*">SkipSupportWithDefaults</lineMarker> : SkipSupport {
|
||||||
// TODO: should be "Is overriden in SkipSupportImpl"
|
override fun <lineMarker descr="<html><body>Is overridden in <br> SkipSupportImpl</body></html>"><lineMarker descr="Implements function in 'SkipSupport'">skip</lineMarker></lineMarker>(why: String) {}
|
||||||
override fun <lineMarker descr="<html><body>Is implemented in <br> SkipSupportImpl</body></html>"><lineMarker descr="Implements function in 'SkipSupport'">skip</lineMarker></lineMarker>(why: String) {}
|
|
||||||
|
|
||||||
override fun <lineMarker descr="Implements function in 'SkipSupport'">skip</lineMarker>() {
|
override fun <lineMarker descr="Implements function in 'SkipSupport'">skip</lineMarker>() {
|
||||||
skip("not given")
|
skip("not given")
|
||||||
|
|||||||
Vendored
+1
-1
@@ -3,7 +3,7 @@ interface <lineMarker descr="*">SkipSupport</lineMarker> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public interface <lineMarker descr="*">SkipSupportWithDefaults</lineMarker> : SkipSupport {
|
public interface <lineMarker descr="*">SkipSupportWithDefaults</lineMarker> : SkipSupport {
|
||||||
override fun <lineMarker descr="<html><body>Is implemented in <br> SkipSupportImpl1</body></html>"><lineMarker descr="Implements function in 'SkipSupport'">skip</lineMarker></lineMarker>() {}
|
override fun <lineMarker descr="<html><body>Is overridden in <br> SkipSupportImpl1</body></html>"><lineMarker descr="Implements function in 'SkipSupport'">skip</lineMarker></lineMarker>() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface SkipSupportImpl1 : SkipSupportWithDefaults {
|
public interface SkipSupportImpl1 : SkipSupportWithDefaults {
|
||||||
|
|||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
package test;
|
||||||
|
|
||||||
|
public class ExtendClassWithDefaultImplementationComplext {
|
||||||
|
<error descr="Class 'Test1' must either be declared abstract or implement abstract method 'a()' in 'A'">public static class Test1 implements A</error> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Test2 extends AI implements A {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
<error descr="Class 'Test3' must either be declared abstract or implement abstract method 'b()' in 'B'">public static class Test3 extends AI implements B</error> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Test4 extends BI implements B {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
<error descr="Class 'Test5' must either be declared abstract or implement abstract method 'c()' in 'C'">public static class Test5 extends BI implements C</error> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
<error descr="Class 'Test6' must either be declared abstract or implement abstract method 'd()' in 'D'">public static class Test6 extends BI implements D</error> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Test7 extends BI implements S {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface Test8 extends A {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static abstract class Test9 implements A {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+26
@@ -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
|
||||||
|
}
|
||||||
Vendored
+14
@@ -0,0 +1,14 @@
|
|||||||
|
package test;
|
||||||
|
|
||||||
|
public class ExtendClassWithDefaultImplementation_1_6 {
|
||||||
|
public static class ExtendClass extends KotlinClass {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
<error descr="Class 'ImplementInterface' must either be declared abstract or implement abstract method 'bar()' in 'KotlinInterface'">public static class ImplementInterface implements KotlinInterface</error> {
|
||||||
|
@Override
|
||||||
|
public void f() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
interface KotlinInterface {
|
||||||
|
fun foo() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun f()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
abstract class KotlinClass : KotlinInterface {
|
||||||
|
override fun f() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
// LANGUAGE_LEVEL 1.6
|
||||||
Vendored
+14
@@ -0,0 +1,14 @@
|
|||||||
|
package test;
|
||||||
|
|
||||||
|
public class ExtendClassWithDefaultImplementation_1_8 {
|
||||||
|
public static class ExtendClass extends KotlinClass {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
<error descr="Class 'ImplementInterface' must either be declared abstract or implement abstract method 'bar()' in 'KotlinInterface'">public static class ImplementInterface implements KotlinInterface</error> {
|
||||||
|
@Override
|
||||||
|
public void f() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
interface KotlinInterface {
|
||||||
|
fun foo() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun f()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
abstract class KotlinClass : KotlinInterface {
|
||||||
|
override fun f() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
// LANGUAGE_LEVEL 1.8
|
||||||
+18
@@ -72,6 +72,24 @@ public class JavaAgainstKotlinBinariesCheckerTestGenerated extends AbstractJavaA
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("FunctionInNestedClassInDataFlowInspection.kt")
|
||||||
public void testFunctionInNestedClassInDataFlowInspection() throws Exception {
|
public void testFunctionInNestedClassInDataFlowInspection() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/FunctionInNestedClassInDataFlowInspection.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/FunctionInNestedClassInDataFlowInspection.kt");
|
||||||
|
|||||||
+18
@@ -74,6 +74,24 @@ public class JavaAgainstKotlinSourceCheckerTestGenerated extends AbstractJavaAga
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("FunctionInNestedClassInDataFlowInspection.kt")
|
||||||
public void testFunctionInNestedClassInDataFlowInspection() throws Exception {
|
public void testFunctionInNestedClassInDataFlowInspection() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/FunctionInNestedClassInDataFlowInspection.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kotlinAndJavaChecker/javaAgainstKotlin/FunctionInNestedClassInDataFlowInspection.kt");
|
||||||
|
|||||||
@@ -56,7 +56,8 @@ abstract class AbstractIdeLightClassTest : KotlinLightCodeInsightFixtureTestCase
|
|||||||
myFixture.configureByFiles(*testFiles.toTypedArray())
|
myFixture.configureByFiles(*testFiles.toTypedArray())
|
||||||
|
|
||||||
val ktFile = myFixture.file as KtFile
|
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)
|
val tracker = LightClassLazinessChecker.Tracker(fqName)
|
||||||
project.withServiceRegistered<StubComputationTracker, PsiClass?>(tracker) {
|
project.withServiceRegistered<StubComputationTracker, PsiClass?>(tracker) {
|
||||||
findClass(fqName, ktFile, project)?.apply {
|
findClass(fqName, ktFile, project)?.apply {
|
||||||
@@ -65,7 +66,7 @@ abstract class AbstractIdeLightClassTest : KotlinLightCodeInsightFixtureTestCase
|
|||||||
PsiElementChecker.checkPsiElementStructure(this)
|
PsiElementChecker.checkPsiElementStructure(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun lazinessModeByFileText(testDataPath: String): LightClassLazinessChecker.Mode {
|
private fun lazinessModeByFileText(testDataPath: String): LightClassLazinessChecker.Mode {
|
||||||
@@ -97,17 +98,22 @@ abstract class AbstractIdeCompiledLightClassTest : KotlinDaemonAnalyzerTestCase(
|
|||||||
private fun libName() = "libFor" + getTestName(false)
|
private fun libName() = "libFor" + getTestName(false)
|
||||||
|
|
||||||
fun doTest(testDataPath: String) {
|
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 {
|
findClass(it, null, project)?.apply {
|
||||||
PsiElementChecker.checkPsiElementStructure(this)
|
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(
|
LightClassTestCommon.testLightClass(
|
||||||
File(testDataPath),
|
expected,
|
||||||
|
testData,
|
||||||
findLightClass,
|
findLightClass,
|
||||||
normalizeText = { text ->
|
normalizeText = { text ->
|
||||||
//NOTE: ide and compiler differ in names generated for parameters with unspecified names
|
//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 s)", "java.lang.String p)")
|
||||||
.replace("java.lang.String s1", "java.lang.String p1")
|
.replace("java.lang.String s1", "java.lang.String p1")
|
||||||
.replace("java.lang.String s2", "java.lang.String p2")
|
.replace("java.lang.String s2", "java.lang.String p2")
|
||||||
|
.replace("java.lang.Object o)", "java.lang.Object p)")
|
||||||
.removeLinesStartingWith("@" + JvmAnnotationNames.METADATA_FQ_NAME.asString())
|
.removeLinesStartingWith("@" + JvmAnnotationNames.METADATA_FQ_NAME.asString())
|
||||||
.run(normalize)
|
.run(normalize)
|
||||||
}
|
}
|
||||||
@@ -243,11 +250,18 @@ object LightClassLazinessChecker {
|
|||||||
|
|
||||||
private fun methodInfo(method: PsiMethod) = with(method) {
|
private fun methodInfo(method: PsiMethod) = with(method) {
|
||||||
MethodInfo(
|
MethodInfo(
|
||||||
name, PsiModifier.MODIFIERS.asList().filter { modifierList.hasModifierProperty(it) },
|
name, relevantModifiers(),
|
||||||
isConstructor, method.parameterList.parametersCount, isVarArgs
|
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<out PsiMember>.names() = mapTo(LinkedHashSet()) { it.name!! }
|
private fun Array<out PsiMember>.names() = mapTo(LinkedHashSet()) { it.name!! }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
@@ -66,12 +66,24 @@ public class IdeCompiledLightClassTestGenerated extends AbstractIdeCompiledLight
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("HiddenDeprecated.kt")
|
||||||
public void testHiddenDeprecated() throws Exception {
|
public void testHiddenDeprecated() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/HiddenDeprecated.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/HiddenDeprecated.kt");
|
||||||
doTest(fileName);
|
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")
|
@TestMetadata("JvmNameOnMember.kt")
|
||||||
public void testJvmNameOnMember() throws Exception {
|
public void testJvmNameOnMember() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/JvmNameOnMember.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/JvmNameOnMember.kt");
|
||||||
|
|||||||
@@ -66,12 +66,24 @@ public class IdeLightClassTestGenerated extends AbstractIdeLightClassTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("HiddenDeprecated.kt")
|
||||||
public void testHiddenDeprecated() throws Exception {
|
public void testHiddenDeprecated() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/HiddenDeprecated.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/HiddenDeprecated.kt");
|
||||||
doTest(fileName);
|
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")
|
@TestMetadata("JvmNameOnMember.kt")
|
||||||
public void testJvmNameOnMember() throws Exception {
|
public void testJvmNameOnMember() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/JvmNameOnMember.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/JvmNameOnMember.kt");
|
||||||
|
|||||||
+1
-1
@@ -44,7 +44,7 @@ public final class MyClass {
|
|||||||
public static abstract interface InnerInterface {
|
public static abstract interface InnerInterface {
|
||||||
public abstract void innerInterfaceFun()
|
public abstract void innerInterfaceFun()
|
||||||
|
|
||||||
public abstract int innerInterfaceFunWithImpl()
|
public default int innerInterfaceFunWithImpl()
|
||||||
|
|
||||||
public static final class DefaultImpls {
|
public static final class DefaultImpls {
|
||||||
public static int innerInterfaceFunWithImpl(MyClass.InnerInterface $this)
|
public static int innerInterfaceFunWithImpl(MyClass.InnerInterface $this)
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
public abstract interface Foo {
|
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 {
|
public final class Baz : Foo {
|
||||||
|
|||||||
Reference in New Issue
Block a user