From a3538dabe0a668bce4d45931b442b02c7ac830d0 Mon Sep 17 00:00:00 2001 From: Ivan Volkov Date: Mon, 24 Oct 2022 00:34:25 +0300 Subject: [PATCH] Fix some issues found by review --- .../src/main/kotlin/org/utbot/common/KClassUtil.kt | 7 +------ .../main/kotlin/org/utbot/common/ReflectionUtil.kt | 8 +++++++- .../org/utbot/framework/plugin/api/util/IdUtil.kt | 3 +++ .../constructor/tree/CgCallableAccessManager.kt | 6 +++--- .../codegen/model/visitor/CgAbstractRenderer.kt | 9 ++++++--- .../codegen/model/visitor/CgJavaRenderer.kt | 4 ++-- .../codegen/model/visitor/CgKotlinRenderer.kt | 4 ++-- .../plugin/ui/actions/GenerateTestsAction.kt | 13 +++---------- .../plugin/ui/utils/KotlinPsiElementHandler.kt | 14 +++++++++++--- .../intellij/plugin/ui/utils/PsiElementHandler.kt | 9 +++++++++ 10 files changed, 47 insertions(+), 30 deletions(-) diff --git a/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt b/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt index 8520b014a2..84c4f1d5aa 100644 --- a/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt +++ b/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt @@ -3,13 +3,8 @@ package org.utbot.common import java.lang.reflect.InvocationTargetException import java.lang.reflect.Method -val Class<*>.nameOfPackage: String get() = `package`?.name?:"" - fun Method.invokeCatching(obj: Any?, args: List) = try { Result.success(invoke(obj, *args.toTypedArray())) } catch (e: InvocationTargetException) { Result.failure(e.targetException) -} - -val Class<*>.allNestedClasses: List> - get() = listOf(this) + this.declaredClasses.flatMap { it.allNestedClasses.toList() } \ No newline at end of file +} \ No newline at end of file diff --git a/utbot-core/src/main/kotlin/org/utbot/common/ReflectionUtil.kt b/utbot-core/src/main/kotlin/org/utbot/common/ReflectionUtil.kt index c0e92d97ef..0b55259a9b 100644 --- a/utbot-core/src/main/kotlin/org/utbot/common/ReflectionUtil.kt +++ b/utbot-core/src/main/kotlin/org/utbot/common/ReflectionUtil.kt @@ -111,4 +111,10 @@ val Class<*>.isFinal get() = Modifier.isFinal(modifiers) val Class<*>.isProtected - get() = Modifier.isProtected(modifiers) \ No newline at end of file + get() = Modifier.isProtected(modifiers) + +val Class<*>.nameOfPackage: String + get() = `package`?.name?:"" + +val Class<*>.allNestedClasses: List> + get() = listOf(this) + this.declaredClasses.flatMap { it.allNestedClasses } \ No newline at end of file diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt index 4e5da6f776..51641d8506 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt @@ -441,6 +441,9 @@ val MethodId.method: Method ?: error("Can't find method $signature in ${declaringClass.name}") } +/** + * See [KCallable.extensionReceiverParameter] for more details + */ val MethodId.extensionReceiverParameterIndex: Int? get() = this.method.kotlinFunction?.extensionReceiverParameter?.index diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgCallableAccessManager.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgCallableAccessManager.kt index 75a0fdea5c..fd8e334f06 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgCallableAccessManager.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgCallableAccessManager.kt @@ -205,9 +205,9 @@ internal class CgCallableAccessManagerImpl(val context: CgContext) : CgCallableA */ private fun CgMethodCall.takeCallerFromArgumentsIfNeeded(): CgMethodCall { if (codegenLanguage == CodegenLanguage.KOTLIN) { - // TODO: reflection calls for util and some of mockito methods produce exceptions => currently runCatching is needed - // (but their reflection may be supported, alternatively maybe get rid of reflection somehow here) - runCatching { + // TODO: reflection calls for util and some of mockito methods produce exceptions => here we suppose that + // methods for BuiltinClasses are not extensions by default (which should be true as long as we suppose them to be java methods) + if (executableId.classId !is BuiltinClassId) { executableId.extensionReceiverParameterIndex?.let { receiverIndex -> require(caller == null) { "${executableId.humanReadableName} is an extension function but it already has a non-static caller provided" } val args = arguments.toMutableList() diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgAbstractRenderer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgAbstractRenderer.kt index 177604d4c3..e2f3aef97c 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgAbstractRenderer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgAbstractRenderer.kt @@ -131,10 +131,13 @@ internal abstract class CgAbstractRenderer( } } - protected abstract val ClassId.shouldBeOmittedWhenUsedAsCaller: Boolean + /** + * Returns true if one can call methods of this class without specifying a caller (for example if ClassId represents this instance) + */ + protected abstract val ClassId.methodsAreAccessibleAsTopLevel: Boolean private val MethodId.accessibleByName: Boolean - get() = (context.shouldOptimizeImports && this in context.importedStaticMethods) || classId == context.generatedClass || classId.shouldBeOmittedWhenUsedAsCaller + get() = (context.shouldOptimizeImports && this in context.importedStaticMethods) || classId.methodsAreAccessibleAsTopLevel override fun visit(element: CgElement) { val error = @@ -656,7 +659,7 @@ internal abstract class CgAbstractRenderer( } override fun visit(element: CgStaticFieldAccess) { - if (!element.declaringClass.shouldBeOmittedWhenUsedAsCaller) { + if (!element.declaringClass.methodsAreAccessibleAsTopLevel) { print(element.declaringClass.asString()) print(".") } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgJavaRenderer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgJavaRenderer.kt index a7eaf699e5..8581409160 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgJavaRenderer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgJavaRenderer.kt @@ -60,8 +60,8 @@ internal class CgJavaRenderer(context: CgRendererContext, printer: CgPrinter = C override val langPackage: String = "java.lang" - override val ClassId.shouldBeOmittedWhenUsedAsCaller: Boolean - get() = false + override val ClassId.methodsAreAccessibleAsTopLevel: Boolean + get() = this == context.generatedClass override fun visit(element: AbstractCgClass<*>) { for (annotation in element.annotations) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgKotlinRenderer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgKotlinRenderer.kt index a2730b41c2..63c5bbb258 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgKotlinRenderer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgKotlinRenderer.kt @@ -73,8 +73,8 @@ internal class CgKotlinRenderer(context: CgRendererContext, printer: CgPrinter = override val langPackage: String = "kotlin" - override val ClassId.shouldBeOmittedWhenUsedAsCaller: Boolean - get() = isKotlinFile + override val ClassId.methodsAreAccessibleAsTopLevel: Boolean + get() = (this == context.generatedClass) || isKotlinFile override fun visit(element: AbstractCgClass<*>) { for (annotation in element.annotations) { diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/actions/GenerateTestsAction.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/actions/GenerateTestsAction.kt index abb8cb7501..aa764a5328 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/actions/GenerateTestsAction.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/actions/GenerateTestsAction.kt @@ -66,8 +66,7 @@ class GenerateTestsAction : AnAction(), UpdateInBackground { val psiElementHandler = PsiElementHandler.makePsiElementHandler(file) - // When in Kotlin file, we should propose top-level functions for testing - if (psiElementHandler.isCreateTestActionAvailable(element) || file is KtFile) { + if (psiElementHandler.isCreateTestActionAvailable(element)) { val srcClass = psiElementHandler.containingClass(element) ?: return null val srcSourceRoot = srcClass.getSourceRoot() ?: return null val srcMembers = srcClass.extractFirstLevelMembers(false) @@ -221,7 +220,7 @@ class GenerateTestsAction : AnAction(), UpdateInBackground { } private fun getAllClasses(directory: PsiDirectory): Set { - val allClasses = directory.files.flatMap { getClassesFromFile(it) }.toMutableSet() + val allClasses = directory.files.flatMap { PsiElementHandler.makePsiElementHandler(it).getClassesFromFile(it) }.toMutableSet() for (subDir in directory.subdirectories) allClasses += getAllClasses(subDir) return allClasses } @@ -234,16 +233,10 @@ class GenerateTestsAction : AnAction(), UpdateInBackground { if (!dirsArePackages) { return emptySet() } - val allClasses = psiFiles.flatMap { getClassesFromFile(it) }.toMutableSet() + val allClasses = psiFiles.flatMap { PsiElementHandler.makePsiElementHandler(it).getClassesFromFile(it) }.toMutableSet() allClasses.addAll(psiFiles.mapNotNull { (it as? KtFile)?.findFacadeClass() }) for (psiDir in psiDirectories) allClasses += getAllClasses(psiDir) return allClasses } - - private fun getClassesFromFile(psiFile: PsiFile): List { - val psiElementHandler = PsiElementHandler.makePsiElementHandler(psiFile) - return PsiTreeUtil.getChildrenOfTypeAsList(psiFile, psiElementHandler.classClass) - .map { psiElementHandler.toPsi(it, PsiClass::class.java) } - } } \ No newline at end of file diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/KotlinPsiElementHandler.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/KotlinPsiElementHandler.kt index a17608265f..1ea7d3e110 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/KotlinPsiElementHandler.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/KotlinPsiElementHandler.kt @@ -2,6 +2,7 @@ package org.utbot.intellij.plugin.ui.utils import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile import com.intellij.psi.util.findParentOfType import org.jetbrains.kotlin.asJava.findFacadeClass import org.jetbrains.kotlin.idea.testIntegration.KotlinCreateTestIntention @@ -11,7 +12,6 @@ import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtNamedDeclaration import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf -import org.jetbrains.kotlin.toKtPsiSourceElement import org.jetbrains.uast.toUElement class KotlinPsiElementHandler( @@ -27,8 +27,16 @@ class KotlinPsiElementHandler( return element.toUElement()?.javaPsi as? T ?: error("Could not cast $element to $clazz") } - override fun isCreateTestActionAvailable(element: PsiElement): Boolean = - getTarget(element)?.let { KotlinCreateTestIntention().applicabilityRange(it) != null } ?: false + override fun getClassesFromFile(psiFile: PsiFile): List { + return listOfNotNull((psiFile as? KtFile)?.findFacadeClass()) + super.getClassesFromFile(psiFile) + } + + override fun isCreateTestActionAvailable(element: PsiElement): Boolean { + getTarget(element)?.let { + return KotlinCreateTestIntention().applicabilityRange(it) != null + } + return (element.containingFile as? KtFile)?.findFacadeClass() != null + } private fun getTarget(element: PsiElement?): KtNamedDeclaration? = element?.parentsWithSelf diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/PsiElementHandler.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/PsiElementHandler.kt index 6455cbbf55..eecfda5945 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/PsiElementHandler.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/PsiElementHandler.kt @@ -3,6 +3,7 @@ package org.utbot.intellij.plugin.ui.utils import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile +import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.psi.KtFile /** @@ -36,6 +37,14 @@ interface PsiElementHandler { */ fun toPsi(element: PsiElement, clazz: Class): T + /** + * Returns all classes that are declared in the [psiFile] + */ + fun getClassesFromFile(psiFile: PsiFile): List { + return PsiTreeUtil.getChildrenOfTypeAsList(psiFile, classClass) + .map { toPsi(it, PsiClass::class.java) } + } + /** * Get java class of the Class in the corresponding syntax tree (PsiClass, KtClass, e.t.c). */