Support main entry-point without arguments in frontend
#KT-26574 In Progress
This commit is contained in:
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.annotations.hasJvmStaticAnnotation
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
@@ -48,16 +49,19 @@ class MainFunctionDetector {
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun isMain(function: KtNamedFunction, checkJvmStaticAnnotation: Boolean = true): Boolean {
|
||||
fun isMain(
|
||||
function: KtNamedFunction,
|
||||
checkJvmStaticAnnotation: Boolean = true,
|
||||
allowParameterless: Boolean = true
|
||||
): Boolean {
|
||||
if (function.isLocal) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
var parametersCount = function.valueParameters.size
|
||||
if (function.receiverTypeReference != null) parametersCount++
|
||||
|
||||
if (parametersCount != 1) {
|
||||
if (!isParameterNumberSuitsForMain(parametersCount, function.isTopLevel, allowParameterless)) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -76,13 +80,14 @@ class MainFunctionDetector {
|
||||
}
|
||||
|
||||
val functionDescriptor = getFunctionDescriptor(function) ?: return false
|
||||
return isMain(functionDescriptor, checkJvmStaticAnnotation)
|
||||
return isMain(functionDescriptor, checkJvmStaticAnnotation, allowParameterless = allowParameterless)
|
||||
}
|
||||
|
||||
fun isMain(
|
||||
descriptor: DeclarationDescriptor,
|
||||
checkJvmStaticAnnotation: Boolean = true,
|
||||
checkReturnType: Boolean = true
|
||||
checkReturnType: Boolean = true,
|
||||
allowParameterless: Boolean = true
|
||||
): Boolean {
|
||||
if (descriptor !is FunctionDescriptor) return false
|
||||
|
||||
@@ -93,20 +98,40 @@ class MainFunctionDetector {
|
||||
val parameters = descriptor.valueParameters.mapTo(mutableListOf()) { it.type }
|
||||
descriptor.extensionReceiverParameter?.type?.let { parameters += it }
|
||||
|
||||
if (parameters.size != 1 || !descriptor.typeParameters.isEmpty()) return false
|
||||
|
||||
val parameterType = parameters[0]
|
||||
if (!KotlinBuiltIns.isArray(parameterType)) return false
|
||||
|
||||
val typeArguments = parameterType.arguments
|
||||
if (typeArguments.size != 1) return false
|
||||
|
||||
val typeArgument = typeArguments[0].type
|
||||
if (!KotlinBuiltIns.isString(typeArgument)) {
|
||||
if (!isParameterNumberSuitsForMain(
|
||||
parameters.size,
|
||||
DescriptorUtils.isTopLevelDeclaration(descriptor),
|
||||
allowParameterless
|
||||
)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
if (typeArguments[0].projectionKind === Variance.IN_VARIANCE) {
|
||||
return false
|
||||
|
||||
if (descriptor.typeParameters.isNotEmpty()) return false
|
||||
|
||||
if (parameters.size == 1) {
|
||||
val parameterType = parameters[0]
|
||||
if (!KotlinBuiltIns.isArray(parameterType)) return false
|
||||
|
||||
val typeArguments = parameterType.arguments
|
||||
if (typeArguments.size != 1) return false
|
||||
|
||||
val typeArgument = typeArguments[0].type
|
||||
if (!KotlinBuiltIns.isString(typeArgument)) {
|
||||
return false
|
||||
}
|
||||
if (typeArguments[0].projectionKind === Variance.IN_VARIANCE) {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
assert(parameters.size == 0) { "Parameter list is expected to be empty" }
|
||||
assert(DescriptorUtils.isTopLevelDeclaration(descriptor)) { "main without parameters works only for top-level" }
|
||||
val containingFile = DescriptorToSourceUtils.getContainingFile(descriptor)
|
||||
// We do not support parameterless entry points having JvmName("name") but different real names
|
||||
if (descriptor.name.asString() != "main") return false
|
||||
if (containingFile?.declarations?.any { declaration -> isMainWithParameter(declaration, checkJvmStaticAnnotation) } == true) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if (checkReturnType && !isMainReturnType(descriptor)) return false
|
||||
@@ -119,6 +144,10 @@ class MainFunctionDetector {
|
||||
&& (descriptor.hasJvmStaticAnnotation() || !checkJvmStaticAnnotation)
|
||||
}
|
||||
|
||||
private fun isMainWithParameter(
|
||||
declaration: KtDeclaration,
|
||||
checkJvmStaticAnnotation: Boolean
|
||||
) = declaration is KtNamedFunction && isMain(declaration, checkJvmStaticAnnotation, allowParameterless = false)
|
||||
|
||||
fun getMainFunction(module: ModuleDescriptor): FunctionDescriptor? = getMainFunction(module, module.getPackage(FqName.ROOT))
|
||||
|
||||
@@ -141,6 +170,16 @@ class MainFunctionDetector {
|
||||
declarations.filterIsInstance<KtNamedFunction>().find { isMain(it) }
|
||||
|
||||
companion object {
|
||||
private fun isParameterNumberSuitsForMain(
|
||||
parametersCount: Int,
|
||||
isTopLevel: Boolean,
|
||||
allowParameterless: Boolean
|
||||
) = when (parametersCount) {
|
||||
1 -> true
|
||||
0 -> isTopLevel && allowParameterless
|
||||
else -> false
|
||||
}
|
||||
|
||||
private fun isMainReturnType(descriptor: FunctionDescriptor): Boolean {
|
||||
val returnType = descriptor.returnType
|
||||
return returnType != null && KotlinBuiltIns.isUnit(returnType)
|
||||
|
||||
Reference in New Issue
Block a user