@@ -7,16 +7,29 @@ import org.javacs.kt.index.SymbolIndex
77import org.javacs.kt.position.offset
88import org.javacs.kt.position.position
99import org.javacs.kt.util.toPath
10+ import org.jetbrains.kotlin.descriptors.ClassDescriptor
11+ import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
12+ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
13+ import org.jetbrains.kotlin.descriptors.FunctionDescriptor
14+ import org.jetbrains.kotlin.descriptors.isInterface
15+ import org.jetbrains.kotlin.descriptors.Modality
1016import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
1117import org.jetbrains.kotlin.lexer.KtTokens
1218import org.jetbrains.kotlin.psi.KtClass
1319import org.jetbrains.kotlin.psi.KtDeclaration
1420import org.jetbrains.kotlin.psi.KtNamedFunction
21+ import org.jetbrains.kotlin.psi.KtSimpleNameExpression
22+ import org.jetbrains.kotlin.psi.KtSuperTypeListEntry
23+ import org.jetbrains.kotlin.psi.KtTypeArgumentList
24+ import org.jetbrains.kotlin.psi.KtTypeReference
1525import org.jetbrains.kotlin.psi.psiUtil.containingClass
1626import org.jetbrains.kotlin.psi.psiUtil.endOffset
1727import org.jetbrains.kotlin.psi.psiUtil.isAbstract
1828import org.jetbrains.kotlin.psi.psiUtil.startOffset
1929import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
30+ import org.jetbrains.kotlin.types.KotlinType
31+ import org.jetbrains.kotlin.types.TypeProjection
32+ import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
2033
2134private const val DEFAULT_TAB_SIZE = 4
2235
@@ -27,7 +40,7 @@ class ImplementAbstractFunctionsQuickFix : QuickFix {
2740 val startCursor = offset(file.content, range.start)
2841 val endCursor = offset(file.content, range.end)
2942 val kotlinDiagnostics = file.compile.diagnostics
30-
43+
3144 // If the client side and the server side diagnostics contain a valid diagnostic for this range.
3245 if (diagnostic != null && anyDiagnosticMatch(kotlinDiagnostics, startCursor, endCursor)) {
3346 // Get the class with the missing functions
@@ -70,31 +83,47 @@ private fun getAbstractFunctionStubs(file: CompiledFile, kotlinClass: KtClass) =
7083 // For each of the super types used by this class
7184 kotlinClass.superTypeListEntries.mapNotNull {
7285 // Find the definition of this super type
73- val descriptor = file.referenceAtPoint(it.startOffset)?.second
74- val superClass = descriptor?.findPsi()
86+ val referenceAtPoint = file.referenceExpressionAtPoint(it.startOffset)
87+ val descriptor = referenceAtPoint?.second
88+
89+ val classDescriptor = getClassDescriptor(descriptor)
90+
7591 // If the super class is abstract or an interface
76- if (superClass is KtClass && (superClass.isAbstract() || superClass.isInterface())) {
77- // Get the abstract functions of this super type that are currently not implemented by this class
78- val abstractFunctions = superClass.declarations.filter {
79- declaration -> isAbstractFunction(declaration) && ! overridesDeclaration(kotlinClass, declaration)
92+ if (null != classDescriptor && (classDescriptor.kind.isInterface || classDescriptor.modality == Modality .ABSTRACT )) {
93+ val superClassTypeArguments = getSuperClassTypeProjections(file, it)
94+ classDescriptor.getMemberScope(superClassTypeArguments).getContributedDescriptors().filter { classMember ->
95+ classMember is FunctionDescriptor && classMember.modality == Modality .ABSTRACT && ! overridesDeclaration(kotlinClass, classMember)
96+ }.map { function ->
97+ createFunctionStub(function as FunctionDescriptor )
8098 }
81- // Get stubs for each function
82- abstractFunctions.map { function -> getFunctionStub(function as KtNamedFunction ) }
8399 } else {
84100 null
85101 }
86102 }.flatten()
87103
88- private fun isAbstractFunction (declaration : KtDeclaration ): Boolean =
89- declaration is KtNamedFunction && ! declaration.hasBody()
90- && (declaration.containingClass()?.isInterface() ? : false || declaration.hasModifier(KtTokens .ABSTRACT_KEYWORD ))
104+ // interfaces are ClassDescriptors by default. When calling AbstractClass super methods, we get a ClassConstructorDescriptor
105+ private fun getClassDescriptor (descriptor : DeclarationDescriptor ? ): ClassDescriptor ? = if (descriptor is ClassDescriptor ) {
106+ descriptor
107+ } else if (descriptor is ClassConstructorDescriptor ) {
108+ descriptor.containingDeclaration
109+ } else {
110+ null
111+ }
112+
113+ private fun getSuperClassTypeProjections (file : CompiledFile , superType : KtSuperTypeListEntry ): List <TypeProjection > = superType.typeReference?.typeElement?.children?.filter {
114+ it is KtTypeArgumentList
115+ }?.flatMap {
116+ (it as KtTypeArgumentList ).arguments
117+ }?.mapNotNull {
118+ (file.referenceExpressionAtPoint(it?.startOffset ? : 0 )?.second as ? ClassDescriptor )?.defaultType?.asTypeProjection()
119+ } ? : emptyList()
91120
92121// Checks if the class overrides the given declaration
93- private fun overridesDeclaration (kotlinClass : KtClass , declaration : KtDeclaration ): Boolean =
122+ private fun overridesDeclaration (kotlinClass : KtClass , descriptor : FunctionDescriptor ): Boolean =
94123 kotlinClass.declarations.any {
95- if (it.name == declaration .name && it.hasModifier(KtTokens .OVERRIDE_KEYWORD )) {
96- if (it is KtNamedFunction && declaration is KtNamedFunction ) {
97- parametersMatch(it, declaration )
124+ if (it.name == descriptor .name.asString() && it.hasModifier(KtTokens .OVERRIDE_KEYWORD )) {
125+ if (it is KtNamedFunction ) {
126+ parametersMatch(it, descriptor )
98127 } else {
99128 true
100129 }
@@ -104,19 +133,21 @@ private fun overridesDeclaration(kotlinClass: KtClass, declaration: KtDeclaratio
104133 }
105134
106135// Checks if two functions have matching parameters
107- private fun parametersMatch (function : KtNamedFunction , functionDeclaration : KtNamedFunction ): Boolean {
108- if (function.valueParameters.size == functionDeclaration .valueParameters.size) {
136+ private fun parametersMatch (function : KtNamedFunction , functionDescriptor : FunctionDescriptor ): Boolean {
137+ if (function.valueParameters.size == functionDescriptor .valueParameters.size) {
109138 for (index in 0 until function.valueParameters.size) {
110- if (function.valueParameters[index].name != functionDeclaration .valueParameters[index].name) {
139+ if (function.valueParameters[index].name != functionDescriptor .valueParameters[index].name.asString() ) {
111140 return false
112- } else if (function.valueParameters[index].typeReference?.name != functionDeclaration.valueParameters[index].typeReference?.name) {
141+ } else if (function.valueParameters[index].typeReference?.typeName() != functionDescriptor.valueParameters[index].type.unwrappedType().toString()) {
142+ // Note: Since we treat Java overrides as non nullable by default, the above test will fail when the user has made the type nullable.
143+ // TODO: look into this
113144 return false
114145 }
115146 }
116147
117- if (function.typeParameters.size == functionDeclaration .typeParameters.size) {
148+ if (function.typeParameters.size == functionDescriptor .typeParameters.size) {
118149 for (index in 0 until function.typeParameters.size) {
119- if (function.typeParameters[index].variance != functionDeclaration .typeParameters[index].variance) {
150+ if (function.typeParameters[index].variance != functionDescriptor .typeParameters[index].variance) {
120151 return false
121152 }
122153 }
@@ -128,8 +159,28 @@ private fun parametersMatch(function: KtNamedFunction, functionDeclaration: KtNa
128159 return false
129160}
130161
131- private fun getFunctionStub (function : KtNamedFunction ): String =
132- " override fun" + function.text.substringAfter(" fun" ) + " { }"
162+ private fun KtTypeReference.typeName (): String? = this .name ? : this .typeElement?.children?.filter {
163+ it is KtSimpleNameExpression
164+ }?.map {
165+ (it as KtSimpleNameExpression ).getReferencedName()
166+ }?.firstOrNull()
167+
168+ private fun createFunctionStub (function : FunctionDescriptor ): String {
169+ val name = function.name
170+ val arguments = function.valueParameters.map { argument ->
171+ val argumentName = argument.name
172+ val argumentType = argument.type.unwrappedType()
173+
174+ " $argumentName : $argumentType "
175+ }.joinToString(" , " )
176+ val returnType = function.returnType?.unwrappedType()?.toString()?.takeIf { " Unit" != it }
177+
178+ return " override fun $name ($arguments )${returnType?.let { " : $it " } ? : " " } { }"
179+ }
180+
181+ // about types: regular Kotlin types are marked T or T?, but types from Java are (T..T?) because nullability cannot be decided.
182+ // Therefore we have to unpack in case we have the Java type. Fortunately, the Java types are not marked nullable, so we default to non nullable types. Let the user decide if they want nullable types instead. With this implementation Kotlin types also keeps their nullability
183+ private fun KotlinType.unwrappedType (): KotlinType = this .unwrap().makeNullableAsSpecified(this .isMarkedNullable)
133184
134185private fun getDeclarationPadding (file : CompiledFile , kotlinClass : KtClass ): String {
135186 // If the class is not empty, the amount of padding is the same as the one in the last declaration of the class
0 commit comments