@@ -7,16 +7,29 @@ import org.javacs.kt.index.SymbolIndex
7
7
import org.javacs.kt.position.offset
8
8
import org.javacs.kt.position.position
9
9
import 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
10
16
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
11
17
import org.jetbrains.kotlin.lexer.KtTokens
12
18
import org.jetbrains.kotlin.psi.KtClass
13
19
import org.jetbrains.kotlin.psi.KtDeclaration
14
20
import 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
15
25
import org.jetbrains.kotlin.psi.psiUtil.containingClass
16
26
import org.jetbrains.kotlin.psi.psiUtil.endOffset
17
27
import org.jetbrains.kotlin.psi.psiUtil.isAbstract
18
28
import org.jetbrains.kotlin.psi.psiUtil.startOffset
19
29
import 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
20
33
21
34
private const val DEFAULT_TAB_SIZE = 4
22
35
@@ -27,7 +40,7 @@ class ImplementAbstractFunctionsQuickFix : QuickFix {
27
40
val startCursor = offset(file.content, range.start)
28
41
val endCursor = offset(file.content, range.end)
29
42
val kotlinDiagnostics = file.compile.diagnostics
30
-
43
+
31
44
// If the client side and the server side diagnostics contain a valid diagnostic for this range.
32
45
if (diagnostic != null && anyDiagnosticMatch(kotlinDiagnostics, startCursor, endCursor)) {
33
46
// Get the class with the missing functions
@@ -70,31 +83,47 @@ private fun getAbstractFunctionStubs(file: CompiledFile, kotlinClass: KtClass) =
70
83
// For each of the super types used by this class
71
84
kotlinClass.superTypeListEntries.mapNotNull {
72
85
// 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
+
75
91
// 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 )
80
98
}
81
- // Get stubs for each function
82
- abstractFunctions.map { function -> getFunctionStub(function as KtNamedFunction ) }
83
99
} else {
84
100
null
85
101
}
86
102
}.flatten()
87
103
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()
91
120
92
121
// 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 =
94
123
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 )
98
127
} else {
99
128
true
100
129
}
@@ -104,19 +133,21 @@ private fun overridesDeclaration(kotlinClass: KtClass, declaration: KtDeclaratio
104
133
}
105
134
106
135
// 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) {
109
138
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() ) {
111
140
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
113
144
return false
114
145
}
115
146
}
116
147
117
- if (function.typeParameters.size == functionDeclaration .typeParameters.size) {
148
+ if (function.typeParameters.size == functionDescriptor .typeParameters.size) {
118
149
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) {
120
151
return false
121
152
}
122
153
}
@@ -128,8 +159,28 @@ private fun parametersMatch(function: KtNamedFunction, functionDeclaration: KtNa
128
159
return false
129
160
}
130
161
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)
133
184
134
185
private fun getDeclarationPadding (file : CompiledFile , kotlinClass : KtClass ): String {
135
186
// 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