diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/findMainClass.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/findMainClass.kt index d554650f041..76dda271119 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/findMainClass.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/findMainClass.kt @@ -9,17 +9,35 @@ import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil import org.jetbrains.kotlin.idea.MainFunctionDetector import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.resolve.BindingContext fun findMainClass(bindingContext: BindingContext, languageVersionSettings: LanguageVersionSettings, files: List): FqName? { val mainFunctionDetector = MainFunctionDetector(bindingContext, languageVersionSettings) return files.asSequence() .map { file -> - if (mainFunctionDetector.hasMain(file.declarations)) - JvmFileClassUtil.getFileClassInfoNoResolve(file).facadeClassFqName - else - null + mainFunctionDetector.findMainFunction(file)?.let { mainFunction -> + if (mainFunction.isTopLevel) { + JvmFileClassUtil.getFileClassInfoNoResolve(file).facadeClassFqName + } else { + val parent = mainFunction.getParentOfType(strict = true) + if (parent is KtObjectDeclaration && parent.isCompanion()) { + mainFunction.fqName?.parent()?.parent() + } else { + mainFunction.fqName?.parent() + } + } + } } .singleOrNull { it != null } } + +private fun MainFunctionDetector.findMainFunction(container: KtDeclarationContainer): KtNamedFunction? = + container.declarations.mapNotNull { declaration -> + when (declaration) { + is KtNamedFunction -> declaration.takeIf(::isMain) + is KtDeclarationContainer -> findMainFunction(declaration) + else -> null + } + }.singleOrNull() diff --git a/compiler/frontend/src/org/jetbrains/kotlin/idea/MainFunctionDetector.kt b/compiler/frontend/src/org/jetbrains/kotlin/idea/MainFunctionDetector.kt index 300a09429ae..3c0eef4a88c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/idea/MainFunctionDetector.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/idea/MainFunctionDetector.kt @@ -52,10 +52,6 @@ class MainFunctionDetector { this.languageVersionSettings = languageVersionSettings } - fun hasMain(declarations: List): Boolean { - return findMainFunction(declarations) != null - } - @JvmOverloads fun isMain( function: KtNamedFunction, @@ -178,9 +174,6 @@ class MainFunctionDetector { return null } - private fun findMainFunction(declarations: List) = - declarations.filterIsInstance().find { isMain(it) } - private fun isParameterNumberSuitsForMain( parametersCount: Int, isTopLevel: Boolean, diff --git a/compiler/tests/org/jetbrains/kotlin/cli/CustomCliTest.kt b/compiler/tests/org/jetbrains/kotlin/cli/CustomCliTest.kt index d97f3601d27..81efb4fef02 100644 --- a/compiler/tests/org/jetbrains/kotlin/cli/CustomCliTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/cli/CustomCliTest.kt @@ -39,6 +39,71 @@ class CustomCliTest : TestCaseWithTmpdir() { compileAndCheckMainClass(listOf(main1Kt, main2Kt), expectedMainClass = null) } + fun testObjectJvmStaticFunctionMainClass() { + val mainKt = tmpdir.resolve("main.kt").apply { + writeText( + """ + object ObjectMain { + @JvmStatic + fun main(args: Array) = println("hello") + } + """ + ) + } + compileAndCheckMainClass(listOf(mainKt), expectedMainClass = "ObjectMain") + } + + fun testCompanionObjectJvmStaticFunctionMainClass() { + val mainKt = tmpdir.resolve("main.kt").apply { + writeText( + """ + class Test { + companion object { + @JvmStatic + fun main(args: Array) = println("hello") + } + } + """ + ) + } + compileAndCheckMainClass(listOf(mainKt), expectedMainClass = "Test") + } + + fun testInterfaceCompanionObjectJvmStaticFunctionMainClass() { + val mainKt = tmpdir.resolve("main.kt").apply { + writeText( + """ + interface Test { + companion object { + @JvmStatic + fun main(args: Array) = println("hello") + } + } + """ + ) + } + compileAndCheckMainClass(listOf(mainKt), expectedMainClass = "Test") + } + + fun testMultipleMainsInOneFile() { + val mainKt = tmpdir.resolve("main.kt").apply { + writeText( + """ + object ObjectMain { + @JvmStatic + fun main(args: Array) = println("hello") + } + object ObjectMain2 { + @JvmStatic + fun main(args: Array) = println("hello2") + } + fun main(args: Array) = println("hello3") + """ + ) + } + compileAndCheckMainClass(listOf(mainKt), expectedMainClass = null) + } + private fun compileAndCheckMainClass(sourceFiles: List, expectedMainClass: String?) { val jarFile = tmpdir.resolve("output.jar") val args = listOf("-include-runtime", "-d", jarFile.absolutePath) + sourceFiles.map { it.absolutePath }