Skip to content

Commit 0e5ab96

Browse files
committed
Refactoring and minor cleanup.
1 parent f94a586 commit 0e5ab96

File tree

3 files changed

+22
-30
lines changed

3 files changed

+22
-30
lines changed

server/src/main/kotlin/org/javacs/kt/codeaction/quickfix/ImplementAbstractFunctionsQuickFix.kt

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import org.javacs.kt.CompiledFile
66
import org.javacs.kt.index.SymbolIndex
77
import org.javacs.kt.position.offset
88
import org.javacs.kt.position.position
9-
import org.javacs.kt.LOG
109
import org.javacs.kt.util.toPath
1110
import org.jetbrains.kotlin.descriptors.ClassDescriptor
1211
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
@@ -28,6 +27,7 @@ import org.jetbrains.kotlin.psi.psiUtil.endOffset
2827
import org.jetbrains.kotlin.psi.psiUtil.isAbstract
2928
import org.jetbrains.kotlin.psi.psiUtil.startOffset
3029
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
30+
import org.jetbrains.kotlin.types.KotlinType
3131
import org.jetbrains.kotlin.types.TypeProjection
3232
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
3333

@@ -40,8 +40,6 @@ class ImplementAbstractFunctionsQuickFix : QuickFix {
4040
val startCursor = offset(file.content, range.start)
4141
val endCursor = offset(file.content, range.end)
4242
val kotlinDiagnostics = file.compile.diagnostics
43-
44-
//LOG.info("start: {}, end: {}, range {}, diagnostics {}", startCursor, endCursor, range, diagnostics)
4543

4644
// If the client side and the server side diagnostics contain a valid diagnostic for this range.
4745
if (diagnostic != null && anyDiagnosticMatch(kotlinDiagnostics, startCursor, endCursor)) {
@@ -89,10 +87,10 @@ private fun getAbstractFunctionStubs(file: CompiledFile, kotlinClass: KtClass) =
8987
val descriptor = referenceAtPoint?.second
9088

9189
val classDescriptor = getClassDescriptor(descriptor)
92-
val superClassTypeArguments = getSuperClassTypeArguments(file, it)
9390

9491
// If the super class is abstract or an interface
95-
if(null != classDescriptor && (classDescriptor.kind.isInterface || classDescriptor.modality == Modality.ABSTRACT)) {
92+
if (null != classDescriptor && (classDescriptor.kind.isInterface || classDescriptor.modality == Modality.ABSTRACT)) {
93+
val superClassTypeArguments = getSuperClassTypeProjections(file, it)
9694
classDescriptor.getMemberScope(superClassTypeArguments).getContributedDescriptors().filter { classMember ->
9795
classMember is FunctionDescriptor && classMember.modality == Modality.ABSTRACT && !overridesDeclaration(kotlinClass, classMember)
9896
}.map { function ->
@@ -103,18 +101,16 @@ private fun getAbstractFunctionStubs(file: CompiledFile, kotlinClass: KtClass) =
103101
}
104102
}.flatten()
105103

106-
// TODO: better name?
107104
// interfaces are ClassDescriptors by default. When calling AbstractClass super methods, we get a ClassConstructorDescriptor
108-
private fun getClassDescriptor(descriptor: DeclarationDescriptor?): ClassDescriptor? = if(descriptor is ClassDescriptor) {
105+
private fun getClassDescriptor(descriptor: DeclarationDescriptor?): ClassDescriptor? = if (descriptor is ClassDescriptor) {
109106
descriptor
110-
} else if(descriptor is ClassConstructorDescriptor) {
107+
} else if (descriptor is ClassConstructorDescriptor) {
111108
descriptor.containingDeclaration
112109
} else {
113110
null
114111
}
115-
116-
// TODO: better name?
117-
private fun getSuperClassTypeArguments(file: CompiledFile, superType: KtSuperTypeListEntry): List<TypeProjection> = superType.typeReference?.typeElement?.children?.filter {
112+
113+
private fun getSuperClassTypeProjections(file: CompiledFile, superType: KtSuperTypeListEntry): List<TypeProjection> = superType.typeReference?.typeElement?.children?.filter {
118114
it is KtTypeArgumentList
119115
}?.flatMap {
120116
(it as KtTypeArgumentList).arguments
@@ -142,8 +138,9 @@ private fun parametersMatch(function: KtNamedFunction, functionDescriptor: Funct
142138
for (index in 0 until function.valueParameters.size) {
143139
if (function.valueParameters[index].name != functionDescriptor.valueParameters[index].name.asString()) {
144140
return false
145-
} else if (getTypeNameFromTypeReference(function.valueParameters[index].typeReference) != functionDescriptor.valueParameters[index].type.toString()) {
146-
// TODO: here we have exactly the old issue of type as above. Maybe a common method that can be called to get the correct one? or do we maybe have to check both null and not null types here?
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
147144
return false
148145
}
149146
}
@@ -162,29 +159,29 @@ private fun parametersMatch(function: KtNamedFunction, functionDescriptor: Funct
162159
return false
163160
}
164161

165-
// typeReference.name is often null. This fetches the name directly from the KtSimpleNameExpression instead
166-
private fun getTypeNameFromTypeReference(typeReference: KtTypeReference?): String? = typeReference?.typeElement?.children?.filter {
162+
private fun KtTypeReference.typeName(): String? = this.name ?: this.typeElement?.children?.filter {
167163
it is KtSimpleNameExpression
168164
}?.map {
169165
(it as KtSimpleNameExpression).getReferencedName()
170-
}?.get(0)
166+
}?.firstOrNull()
171167

172168
private fun createFunctionStub(function: FunctionDescriptor): String {
173-
// TODO: clean
174169
val name = function.name
175170
val arguments = function.valueParameters.map { argument ->
176171
val argumentName = argument.name
177-
// about argument.type: regular Kotlin types are marked T or T?, but types from Java are (T..T?) because nullability cannot be decided.
178-
// 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
179-
val argumentType = argument.type.unwrap().makeNullableAsSpecified(argument.type.isMarkedNullable)
172+
val argumentType = argument.type.unwrappedType()
180173

181174
"$argumentName: $argumentType"
182175
}.joinToString(", ")
183-
val returnType = function.returnType?.unwrap()?.makeNullableAsSpecified(function.returnType?.isMarkedNullable ?: false)?.toString()
176+
val returnType = function.returnType?.unwrappedType()?.toString()?.takeIf { "Unit" != it }
184177

185-
return "override fun $name($arguments)${returnType?.takeIf { "Unit" != it }?.let { ": $it" } ?: ""} { }"
178+
return "override fun $name($arguments)${returnType?.let { ": $it" } ?: ""} { }"
186179
}
187180

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)
184+
188185
private fun getDeclarationPadding(file: CompiledFile, kotlinClass: KtClass): String {
189186
// If the class is not empty, the amount of padding is the same as the one in the last declaration of the class
190187
val paddingSize = if (kotlinClass.declarations.isNotEmpty()) {

server/src/test/kotlin/org/javacs/kt/CodeActionTest.kt

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import org.eclipse.lsp4j.*
44
import org.javacs.kt.SingleFileTestFixture
55
import org.junit.Test
66
import org.junit.Assert.assertThat
7-
import org.junit.Assert.fail
8-
import org.hamcrest.Matchers.*
7+
import org.hamcrest.Matchers.equalTo
8+
import org.hamcrest.Matchers.hasSize
99

1010
class ImplementAbstractMembersQuickFixSameFileTest : SingleFileTestFixture("codeactions", "implementabstract_samefile.kt") {
1111

@@ -53,7 +53,6 @@ class ImplementAbstractMembersQuickFixSameFileTest : SingleFileTestFixture("code
5353
val codeAction = codeActionResult[0].right
5454
assertThat(codeAction.kind, equalTo(CodeActionKind.QuickFix))
5555
assertThat(codeAction.title, equalTo("Implement abstract functions"))
56-
//assertThat(codeAction.diagnostics, equalTo(listOf(diagnostics[0])))
5756

5857
val textEdit = codeAction.edit.changes
5958
val key = workspaceRoot.resolve(file).toUri().toString()
@@ -80,7 +79,6 @@ class ImplementAbstractMembersQuickFixSameFileTest : SingleFileTestFixture("code
8079
val codeAction = codeActionResult[0].right
8180
assertThat(codeAction.kind, equalTo(CodeActionKind.QuickFix))
8281
assertThat(codeAction.title, equalTo("Implement abstract functions"))
83-
//assertThat(codeAction.diagnostics, equalTo(listOf(diagnostics[0])))
8482

8583
val textEdit = codeAction.edit.changes
8684
val key = workspaceRoot.resolve(file).toUri().toString()
@@ -103,7 +101,6 @@ class ImplementAbstractMembersQuickFixSameFileTest : SingleFileTestFixture("code
103101
val codeAction = codeActionResult[0].right
104102
assertThat(codeAction.kind, equalTo(CodeActionKind.QuickFix))
105103
assertThat(codeAction.title, equalTo("Implement abstract functions"))
106-
//assertThat(codeAction.diagnostics, equalTo(listOf(diagnostics[0])))
107104

108105
val textEdit = codeAction.edit.changes
109106
val key = workspaceRoot.resolve(file).toUri().toString()
@@ -128,7 +125,6 @@ class ImplementAbstractMembersQuickFixExternalLibraryTest : SingleFileTestFixtur
128125
val codeAction = codeActionResult[0].right
129126
assertThat(codeAction.kind, equalTo(CodeActionKind.QuickFix))
130127
assertThat(codeAction.title, equalTo("Implement abstract functions"))
131-
//assertThat(codeAction.diagnostics, equalTo(listOf(diagnostics[0])))
132128

133129
val textEdit = codeAction.edit.changes
134130
val key = workspaceRoot.resolve(file).toUri().toString()
@@ -151,7 +147,6 @@ class ImplementAbstractMembersQuickFixExternalLibraryTest : SingleFileTestFixtur
151147
val codeAction = codeActionResult[0].right
152148
assertThat(codeAction.kind, equalTo(CodeActionKind.QuickFix))
153149
assertThat(codeAction.title, equalTo("Implement abstract functions"))
154-
//assertThat(codeAction.diagnostics, equalTo(listOf(diagnostics[0])))
155150

156151
val textEdit = codeAction.edit.changes
157152
val key = workspaceRoot.resolve(file).toUri().toString()

server/src/test/resources/codeactions/implementabstract_samefile.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class OtherPrintableClass : CanPrint(), MyInterface {
1919
}
2020

2121
interface NullMethodAndReturn<T> {
22-
fun myMethod(myStr: T?): T?
22+
fun myMethod(myStr: T?): T?
2323
}
2424

2525
class NullClass : NullMethodAndReturn<String> {}

0 commit comments

Comments
 (0)