mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
[microservices] IDEA-353802 The ability to provide custom isMain
This allows us to remove kotlin-specific checks for the main method from the common side GitOrigin-RevId: 4a3fca78220bdc0e67abfe96c5ea219aa350027e
This commit is contained in:
committed by
intellij-monorepo-bot
parent
1a50519778
commit
cfe75d3aa7
@@ -4,8 +4,11 @@ package com.intellij.codeInsight.runner;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.project.PossiblyDumbAware;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.util.ClassUtil;
|
||||
import com.intellij.psi.util.PsiMethodUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -46,4 +49,17 @@ public interface JavaMainMethodProvider extends PossiblyDumbAware {
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
@Nullable PsiMethod findMainInClass(@NotNull PsiClass clazz);
|
||||
|
||||
@Contract(pure = true)
|
||||
default @Nullable String getMainClassName(@NotNull PsiClass clazz) {
|
||||
return ClassUtil.getJVMClassName(clazz);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
default boolean isMain(@NotNull PsiElement psiElement) {
|
||||
PsiMethod psiMethod = PsiTreeUtil.getParentOfType(psiElement, PsiMethod.class);
|
||||
if (psiMethod == null) return false;
|
||||
|
||||
return "main".equals(psiMethod.getName()) && PsiMethodUtil.isMainMethod(psiMethod);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,14 +28,12 @@ public final class PsiMethodUtil {
|
||||
|
||||
@Nullable
|
||||
public static PsiMethod findMainMethod(final PsiClass aClass) {
|
||||
List<JavaMainMethodProvider> extensionList = JavaMainMethodProvider.EP_NAME.getExtensionList();
|
||||
DumbService dumbService = DumbService.getInstance(aClass.getProject());
|
||||
for (JavaMainMethodProvider provider : dumbService.filterByDumbAwareness(extensionList)) {
|
||||
if (provider.isApplicable(aClass)) {
|
||||
return provider.findMainInClass(aClass);
|
||||
}
|
||||
JavaMainMethodProvider mainMethodProvider = getApplicableMainMethodProvider(aClass);
|
||||
if (mainMethodProvider != null) {
|
||||
return mainMethodProvider.findMainInClass(aClass);
|
||||
}
|
||||
|
||||
DumbService dumbService = DumbService.getInstance(aClass.getProject());
|
||||
try {
|
||||
return dumbService.computeWithAlternativeResolveEnabled((ThrowableComputable<PsiMethod, Throwable>)() -> {
|
||||
final PsiMethod[] mainMethods = aClass.findMethodsByName("main", true);
|
||||
@@ -132,13 +130,12 @@ public final class PsiMethodUtil {
|
||||
}
|
||||
|
||||
public static boolean hasMainMethod(final PsiClass psiClass) {
|
||||
DumbService dumbService = DumbService.getInstance(psiClass.getProject());
|
||||
for (JavaMainMethodProvider provider : dumbService.filterByDumbAwareness(JavaMainMethodProvider.EP_NAME.getExtensionList())) {
|
||||
if (provider.isApplicable(psiClass)) {
|
||||
return provider.hasMainMethod(psiClass);
|
||||
}
|
||||
JavaMainMethodProvider mainMethodProvider = getApplicableMainMethodProvider(psiClass);
|
||||
if (mainMethodProvider != null) {
|
||||
return mainMethodProvider.hasMainMethod(psiClass);
|
||||
}
|
||||
|
||||
DumbService dumbService = DumbService.getInstance(psiClass.getProject());
|
||||
try {
|
||||
return dumbService.computeWithAlternativeResolveEnabled((ThrowableComputable<Boolean, Throwable>)() -> {
|
||||
final PsiMethod[] mainMethods = psiClass.findMethodsByName("main", true);
|
||||
@@ -150,9 +147,49 @@ public final class PsiMethodUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isMainMethodWithProvider(@NotNull PsiClass psiClass, @NotNull PsiElement psiElement) {
|
||||
JavaMainMethodProvider mainMethodProvider = getApplicableMainMethodProvider(psiClass);
|
||||
if (mainMethodProvider != null) {
|
||||
return mainMethodProvider.isMain(psiElement);
|
||||
}
|
||||
|
||||
DumbService dumbService = DumbService.getInstance(psiElement.getProject());
|
||||
try {
|
||||
return dumbService.computeWithAlternativeResolveEnabled((ThrowableComputable<Boolean, Throwable>)() -> {
|
||||
PsiMethod psiMethod = PsiTreeUtil.getParentOfType(psiElement, PsiMethod.class);
|
||||
if (psiMethod == null) return false;
|
||||
|
||||
return "main".equals(psiMethod.getName()) && isMainMethod(psiMethod);
|
||||
});
|
||||
}
|
||||
catch (IndexNotReadyException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getMainJVMClassName(@NotNull PsiClass psiClass) {
|
||||
JavaMainMethodProvider mainMethodProvider = getApplicableMainMethodProvider(psiClass);
|
||||
if (mainMethodProvider != null) {
|
||||
return mainMethodProvider.getMainClassName(psiClass);
|
||||
}
|
||||
|
||||
return ClassUtil.getJVMClassName(psiClass);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiMethod findMainInClass(final PsiClass aClass) {
|
||||
if (!MAIN_CLASS.value(aClass)) return null;
|
||||
return findMainMethod(aClass);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static JavaMainMethodProvider getApplicableMainMethodProvider(@NotNull PsiClass aClass) {
|
||||
DumbService dumbService = DumbService.getInstance(aClass.getProject());
|
||||
|
||||
List<JavaMainMethodProvider> javaMainMethodProviders =
|
||||
dumbService.filterByDumbAwareness(JavaMainMethodProvider.EP_NAME.getExtensionList());
|
||||
|
||||
return ContainerUtil.find(javaMainMethodProviders, provider -> provider.isApplicable(aClass));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,4 +52,21 @@ class KotlinMainMethodProvider : JavaMainMethodProvider {
|
||||
val classOrObject = lightClassBase?.kotlinOrigin ?: return@runReadAction null
|
||||
mainFunctionDetector.findMain(classOrObject)?.toLightMethods()?.firstOrNull()
|
||||
}
|
||||
override fun getMainClassName(clazz: PsiClass): String? {
|
||||
return when (clazz) {
|
||||
is KtLightClassForFacadeBase -> clazz.facadeClassFqName.asString()
|
||||
is KtLightClassBase -> {
|
||||
val classOrObject = clazz.kotlinOrigin ?: return null
|
||||
KotlinRunConfigurationProducer.getMainClassJvmName(classOrObject)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun isMain(psiElement: PsiElement): Boolean {
|
||||
val ktNamedFunction = psiElement.parentOfType<KtNamedFunction>() ?: return false
|
||||
|
||||
val mainFunctionDetector = KotlinMainFunctionDetector.getInstanceDumbAware(psiElement.project)
|
||||
return runReadAction { mainFunctionDetector.isMain(ktNamedFunction) }
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,9 @@
|
||||
|
||||
package org.jetbrains.uast
|
||||
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.source.tree.LeafPsiElement
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.psi.util.PsiMethodUtil
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.uast.visitor.UastVisitor
|
||||
|
||||
/**
|
||||
@@ -208,65 +207,24 @@ fun UElement.asRecursiveLogString(render: (UElement) -> String = { it.asLogStrin
|
||||
return stringBuilder.toString()
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
message = "This method is deprecated, use PsiMethodUtil.findMainInClass instead",
|
||||
replaceWith = ReplaceWith("uClass?.let { PsiMethodUtil.findMainInClass(it.javaPsi) }")
|
||||
)
|
||||
fun findMainInClass(uClass: UClass?): PsiMethod? {
|
||||
val javaPsi = uClass?.javaPsi ?: return null
|
||||
PsiMethodUtil.findMainInClass(javaPsi)?.let { return it }
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* @return method's containing class if the given method is main method,
|
||||
* or companion object's containing class if the given method is main method annotated with [kotlin.jvm.JvmStatic] in companion object,
|
||||
* otherwise *null*.
|
||||
*/
|
||||
@Deprecated(message = "Use PsiMethodUtil.isMainMethodWithProvider instead")
|
||||
fun getMainMethodClass(uMainMethod: UMethod): PsiClass? {
|
||||
if ("main" != uMainMethod.name) return null
|
||||
val containingClass = uMainMethod.uastParent as? UClass ?: return null
|
||||
val mainMethod = uMainMethod.javaPsi
|
||||
if (PsiMethodUtil.isMainMethod(mainMethod)) return containingClass.javaPsi
|
||||
|
||||
//a workaround for KT-33956
|
||||
if (isKotlinParameterlessMain(mainMethod)) return containingClass.javaPsi
|
||||
|
||||
if (isKotlinSuspendMain(uMainMethod)) return containingClass.javaPsi
|
||||
|
||||
// Check for @JvmStatic main method in companion object
|
||||
val parentClassForCompanionObject = (containingClass.uastParent as? UClass)?.javaPsi ?: return null
|
||||
|
||||
val mainInClass = PsiMethodUtil.findMainInClass(parentClassForCompanionObject)
|
||||
|
||||
if (mainMethod.manager.areElementsEquivalent(mainMethod, mainInClass)) {
|
||||
return parentClassForCompanionObject
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun isKotlinParameterlessMain(mainMethod: PsiMethod) =
|
||||
mainMethod.language.id == "kotlin"
|
||||
&& mainMethod.parameterList.parameters.isEmpty()
|
||||
&& PsiTypes.voidType() == mainMethod.returnType
|
||||
&& mainMethod.hasModifierProperty(PsiModifier.STATIC)
|
||||
|
||||
private fun isKotlinSuspendMain(uMainMethod: UMethod): Boolean {
|
||||
val sourcePsi = uMainMethod.sourcePsi ?: return false
|
||||
if (sourcePsi.language.id != "kotlin") return false
|
||||
val child = sourcePsi.children.firstOrNull() ?: return false
|
||||
if (!SyntaxTraverser.psiTraverser(child).any { it is LeafPsiElement && it.textMatches("suspend") }) return false
|
||||
val method = uMainMethod.javaPsi
|
||||
val parameters: Array<PsiParameter> = method.parameterList.parameters
|
||||
if (parameters.size > 2) return false
|
||||
|
||||
// suspend main method has additional parameter kotlin.coroutines.Continuation but it could be seen as java.lang.Object
|
||||
// on the PSI level, so we don't check it directly
|
||||
if (parameters.size == 2) {
|
||||
val argsType = parameters[0].type as? PsiArrayType ?: return false
|
||||
if (!argsType.componentType.equalsToText(CommonClassNames.JAVA_LANG_STRING)) return false
|
||||
}
|
||||
return method.hasModifierProperty(PsiModifier.STATIC) && method.returnType?.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) == true
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
fun findMainInClass(uClass: UClass?): PsiMethod? {
|
||||
val javaPsi = uClass?.javaPsi ?: return null
|
||||
PsiMethodUtil.findMainInClass(javaPsi)?.let { return it }
|
||||
|
||||
//a workaround for KT-33956
|
||||
javaPsi.methods.find(::isKotlinParameterlessMain)?.let { return it }
|
||||
uClass.methods.find(::isKotlinSuspendMain)?.javaPsi?.let { return it }
|
||||
return null
|
||||
val containingClass = uMainMethod.getContainingUClass() ?: return null
|
||||
return containingClass.javaPsi.takeIf { psiClass -> PsiMethodUtil.isMainMethodWithProvider(psiClass, uMainMethod.javaPsi) }
|
||||
}
|
||||
Reference in New Issue
Block a user