Skip to content

Commit b100af3

Browse files
committed
Add support for Kotlin top-level functions
1 parent a0738ed commit b100af3

File tree

4 files changed

+26
-12
lines changed

4 files changed

+26
-12
lines changed

utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,6 @@ fun Method.invokeCatching(obj: Any?, args: List<Any?>) = try {
1414

1515
val KClass<*>.allNestedClasses: List<KClass<*>>
1616
get() = listOf(this) + nestedClasses.flatMap { it.allNestedClasses }
17+
18+
val Class<*>.allNestedClasses: List<Class<*>>
19+
get() = listOf(this) + this.declaredClasses.flatMap { it.declaredClasses.toList() }

utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ import org.utbot.framework.plugin.api.util.UtContext
2121
import org.utbot.framework.plugin.api.util.executableId
2222
import org.utbot.framework.plugin.api.util.id
2323
import org.utbot.framework.plugin.api.util.jClass
24+
import org.utbot.framework.plugin.api.util.method
2425
import org.utbot.framework.plugin.services.JdkInfo
2526
import org.utbot.framework.process.generated.*
26-
import org.utbot.framework.util.Conflict
2727
import org.utbot.framework.util.ConflictTriggers
2828
import org.utbot.instrumentation.util.KryoHelper
2929
import org.utbot.rd.CallsSynchronizer
@@ -33,14 +33,10 @@ import org.utbot.rd.loggers.UtRdKLoggerFactory
3333
import org.utbot.sarif.RdSourceFindingStrategyFacade
3434
import org.utbot.sarif.SarifReport
3535
import org.utbot.summary.summarize
36-
import soot.SootMethod
37-
import soot.UnitPatchingChain
38-
import soot.util.HashChain
3936
import java.io.File
4037
import java.net.URLClassLoader
4138
import java.nio.file.Paths
42-
import java.util.*
43-
import kotlin.reflect.full.functions
39+
import kotlin.reflect.jvm.kotlinFunction
4440
import kotlin.time.Duration.Companion.seconds
4541

4642
private val messageFromMainTimeoutMillis = 120.seconds
@@ -158,8 +154,8 @@ private fun EngineProcessModel.setup(
158154
synchronizer.measureExecutionForTermination(findMethodsInClassMatchingSelected) { params ->
159155
val classId = kryoHelper.readObject<ClassId>(params.classId)
160156
val selectedSignatures = params.signatures.map { Signature(it.name, it.parametersTypes) }
161-
FindMethodsInClassMatchingSelectedResult(kryoHelper.writeObject(classId.jClass.kotlin.allNestedClasses.flatMap { clazz ->
162-
clazz.functions.sortedWith(compareBy { selectedSignatures.indexOf(it.signature()) })
157+
FindMethodsInClassMatchingSelectedResult(kryoHelper.writeObject(classId.jClass.allNestedClasses.flatMap { clazz ->
158+
clazz.id.allMethods.mapNotNull { it.method.kotlinFunction }.sortedWith(compareBy { selectedSignatures.indexOf(it.signature()) })
163159
.filter { it.signature().normalized() in selectedSignatures }
164160
.map { it.executableId }
165161
}))
@@ -168,7 +164,7 @@ private fun EngineProcessModel.setup(
168164
val classId = kryoHelper.readObject<ClassId>(params.classId)
169165
val bySignature = kryoHelper.readObject<Map<Signature, List<String>>>(params.bySignature)
170166
FindMethodParamNamesResult(kryoHelper.writeObject(
171-
classId.jClass.kotlin.allNestedClasses.flatMap { it.functions }
167+
classId.jClass.allNestedClasses.flatMap { clazz -> clazz.id.allMethods.mapNotNull { it.method.kotlinFunction } }
172168
.mapNotNull { method -> bySignature[method.signature()]?.let { params -> method.executableId to params } }
173169
.toMap()
174170
))

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/actions/GenerateTestsAction.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,18 @@ import com.intellij.openapi.roots.ModuleRootManager
1616
import com.intellij.openapi.roots.ProjectFileIndex
1717
import com.intellij.openapi.vfs.VirtualFile
1818
import com.intellij.psi.*
19+
import com.intellij.psi.impl.PsiJavaParserFacadeImpl
1920
import com.intellij.psi.util.PsiTreeUtil
2021
import com.intellij.refactoring.util.classMembers.MemberInfo
22+
import org.jetbrains.kotlin.asJava.findFacadeClass
2123
import org.jetbrains.kotlin.idea.core.getPackage
2224
import org.jetbrains.kotlin.idea.core.util.toPsiDirectory
2325
import org.jetbrains.kotlin.idea.core.util.toPsiFile
2426
import org.utbot.intellij.plugin.util.extractFirstLevelMembers
2527
import org.utbot.intellij.plugin.util.isVisible
2628
import java.util.*
2729
import org.jetbrains.kotlin.j2k.getContainingClass
30+
import org.jetbrains.kotlin.psi.KtFile
2831
import org.jetbrains.kotlin.utils.addIfNotNull
2932
import org.utbot.intellij.plugin.models.packageName
3033
import org.utbot.intellij.plugin.ui.InvalidClassNotifier
@@ -53,11 +56,13 @@ class GenerateTestsAction : AnAction(), UpdateInBackground {
5356
if (editor != null) {
5457
//The action is being called from editor
5558
val file = e.getData(CommonDataKeys.PSI_FILE) ?: return null
59+
//file.setName("abcdef")
5660
val element = findPsiElement(file, editor) ?: return null
5761

5862
val psiElementHandler = PsiElementHandler.makePsiElementHandler(file)
5963

60-
if (psiElementHandler.isCreateTestActionAvailable(element)) {
64+
// When in Kotlin file, we should propose top-level functions for testing
65+
if (psiElementHandler.isCreateTestActionAvailable(element) || file is KtFile) {
6166
val srcClass = psiElementHandler.containingClass(element) ?: return null
6267
val srcSourceRoot = srcClass.getSourceRoot() ?: return null
6368
val srcMembers = srcClass.extractFirstLevelMembers(false)
@@ -220,6 +225,7 @@ class GenerateTestsAction : AnAction(), UpdateInBackground {
220225
return emptySet()
221226
}
222227
val allClasses = psiFiles.flatMap { getClassesFromFile(it) }.toMutableSet()
228+
allClasses.addAll(psiFiles.mapNotNull { (it as? KtFile)?.findFacadeClass() })
223229
for (psiDir in psiDirectories) allClasses += getAllClasses(psiDir)
224230

225231
return allClasses

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/KotlinPsiElementHandler.kt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ package org.utbot.intellij.plugin.ui.utils
22

33
import com.intellij.psi.PsiClass
44
import com.intellij.psi.PsiElement
5+
import com.intellij.psi.util.findParentOfType
6+
import org.jetbrains.kotlin.asJava.findFacadeClass
57
import org.jetbrains.kotlin.idea.testIntegration.KotlinCreateTestIntention
68
import org.jetbrains.kotlin.psi.KtClass
79
import org.jetbrains.kotlin.psi.KtClassOrObject
810
import org.jetbrains.kotlin.psi.KtFile
911
import org.jetbrains.kotlin.psi.KtNamedDeclaration
1012
import org.jetbrains.kotlin.psi.KtNamedFunction
1113
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
14+
import org.jetbrains.kotlin.toKtPsiSourceElement
1215
import org.jetbrains.uast.toUElement
1316

1417
class KotlinPsiElementHandler(
@@ -31,6 +34,12 @@ class KotlinPsiElementHandler(
3134
element?.parentsWithSelf
3235
?.firstOrNull { it is KtClassOrObject || it is KtNamedDeclaration && it.parent is KtFile } as? KtNamedDeclaration
3336

34-
override fun containingClass(element: PsiElement): PsiClass? =
35-
element.parentsWithSelf.firstOrNull { it is KtClassOrObject }?.let { toPsi(it, PsiClass::class.java) }
37+
override fun containingClass(element: PsiElement): PsiClass? {
38+
element.findParentOfType<KtClassOrObject>(strict=false)?.let {
39+
return toPsi(it, PsiClass::class.java)
40+
}
41+
return element.findParentOfType<KtFile>(strict=false)?.findFacadeClass()?.let {
42+
toPsi(it, PsiClass::class.java)
43+
}
44+
}
3645
}

0 commit comments

Comments
 (0)