From 890ae3e91467b744042f2517f92036912b70b3ec Mon Sep 17 00:00:00 2001 From: Michael Nedzelsky Date: Thu, 15 Oct 2015 00:26:37 +0300 Subject: [PATCH] Make single-file classes tolerant to having many `main()`'s --- ...FactoryForDuplicateSignatureDiagnostics.kt | 26 ++++++++++++++----- .../kotlin/fileClasses/JvmFileClassUtil.kt | 7 ++++- .../checkers/JvmFieldApplicabilityChecker.kt | 6 +---- .../kotlin/resolve/OverloadResolver.java | 2 ++ .../codegen/boxMultiFile/mainInFiles/1.kt | 21 +++++++++++++++ .../codegen/boxMultiFile/mainInFiles/a.kt | 5 ++++ .../codegen/boxMultiFile/mainInFiles/b.kt | 5 ++++ .../RedeclarationMainInMultiFile.kt | 5 ++++ .../RedeclarationMainInMultiFile.txt | 4 +++ .../CallToMainRedeclaredInMultiFile.kt | 8 ++++++ .../CallToMainRedeclaredInMultiFile.txt | 5 ++++ .../RedeclarationMainInMultiFileClass.kt | 11 ++++++++ .../RedeclarationMainInMultiFileClass.txt | 4 +++ .../integration/ant/jvm/mainInFiles/a.kt | 5 ++++ .../integration/ant/jvm/mainInFiles/b.kt | 5 ++++ .../ant/jvm/mainInFiles/build.log.expected | 12 +++++++++ .../integration/ant/jvm/mainInFiles/build.xml | 23 ++++++++++++++++ .../checkers/JetDiagnosticsTestGenerated.java | 6 +++++ ...JetDiagnosticsTestWithStdLibGenerated.java | 12 +++++++++ ...BlackBoxMultiFileCodegenTestGenerated.java | 6 +++++ .../integration/AntTaskTestGenerated.java | 6 +++++ .../kotlin/resolve/DescriptorUtils.java | 15 +++++++++++ 22 files changed, 186 insertions(+), 13 deletions(-) create mode 100644 compiler/testData/codegen/boxMultiFile/mainInFiles/1.kt create mode 100644 compiler/testData/codegen/boxMultiFile/mainInFiles/a.kt create mode 100644 compiler/testData/codegen/boxMultiFile/mainInFiles/b.kt create mode 100644 compiler/testData/diagnostics/tests/redeclarations/RedeclarationMainInMultiFile.kt create mode 100644 compiler/testData/diagnostics/tests/redeclarations/RedeclarationMainInMultiFile.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/CallToMainRedeclaredInMultiFile.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/CallToMainRedeclaredInMultiFile.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/RedeclarationMainInMultiFileClass.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/RedeclarationMainInMultiFileClass.txt create mode 100644 compiler/testData/integration/ant/jvm/mainInFiles/a.kt create mode 100644 compiler/testData/integration/ant/jvm/mainInFiles/b.kt create mode 100644 compiler/testData/integration/ant/jvm/mainInFiles/build.log.expected create mode 100644 compiler/testData/integration/ant/jvm/mainInFiles/build.xml diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/BuilderFactoryForDuplicateSignatureDiagnostics.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/BuilderFactoryForDuplicateSignatureDiagnostics.kt index b9b546a47a1..d5e7dbe9be8 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/BuilderFactoryForDuplicateSignatureDiagnostics.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/BuilderFactoryForDuplicateSignatureDiagnostics.kt @@ -19,20 +19,23 @@ package org.jetbrains.kotlin.codegen.state import com.intellij.psi.PsiElement import com.intellij.util.containers.MultiMap import org.jetbrains.kotlin.codegen.ClassBuilderFactory +import org.jetbrains.kotlin.codegen.ClassBuilderMode import org.jetbrains.kotlin.codegen.SignatureCollectingClassBuilderFactory import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.jvm.diagnostics.* -import java.util.* import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DELEGATION import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.FAKE_OVERRIDE -import org.jetbrains.kotlin.utils.addIfNotNull -import org.jetbrains.kotlin.codegen.ClassBuilderMode -import org.jetbrains.kotlin.load.java.descriptors.SamAdapterDescriptor -import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider +import org.jetbrains.kotlin.fileClasses.isInsideJvmMultifileClassFile +import org.jetbrains.kotlin.load.java.descriptors.SamAdapterDescriptor import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache +import org.jetbrains.kotlin.psi.JetDeclaration +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.jvm.diagnostics.* +import org.jetbrains.kotlin.utils.addIfNotNull +import java.util.* private val EXTERNAL_SOURCES_KINDS = arrayOf( JvmDeclarationOriginKind.DELEGATION_TO_DEFAULT_IMPLS, @@ -61,9 +64,18 @@ class BuilderFactoryForDuplicateSignatureDiagnostics( else { for (origin in data.signatureOrigins) { var element = origin.element + + // TODO Remove this code after dropping package facades + val descriptor = origin.descriptor + if (descriptor != null && element is JetDeclaration && DescriptorUtils.isTopLevelMainFunction(descriptor) && + !element.isInsideJvmMultifileClassFile()) { + return + } + if (element == null || origin.originKind in EXTERNAL_SOURCES_KINDS) { element = data.classOrigin.element } + elements.addIfNotNull(element) } } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/fileClasses/JvmFileClassUtil.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/fileClasses/JvmFileClassUtil.kt index 93bd3fa27d2..02e28a51cd2 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/fileClasses/JvmFileClassUtil.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/fileClasses/JvmFileClassUtil.kt @@ -135,4 +135,9 @@ public val JetFile.javaFileFacadeFqName: FqName else JvmFileClassUtil.getFileClassInfoNoResolve(this).facadeClassFqName CachedValueProvider.Result(facadeFqName, this) } - } \ No newline at end of file + } + +public fun JetDeclaration.isInsideJvmMultifileClassFile() = JvmFileClassUtil.findAnnotationEntryOnFileNoResolve( + getContainingJetFile(), + JvmFileClassUtil.JVM_MULTIFILE_CLASS_SHORT +) != null diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmFieldApplicabilityChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmFieldApplicabilityChecker.kt index 08d37f90ec1..e89bfdad1c3 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmFieldApplicabilityChecker.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmFieldApplicabilityChecker.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil +import org.jetbrains.kotlin.fileClasses.isInsideJvmMultifileClassFile import org.jetbrains.kotlin.psi.JetDeclaration import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DeclarationChecker @@ -74,11 +75,6 @@ class JvmFieldApplicabilityChecker : DeclarationChecker { diagnosticHolder.report(ErrorsJvm.INAPPLICABLE_JVM_FIELD.on(annotationEntry, problem.errorMessage)) } - private fun JetDeclaration.isInsideJvmMultifileClassFile() = JvmFileClassUtil.findAnnotationEntryOnFileNoResolve( - getContainingJetFile(), - JvmFileClassUtil.JVM_MULTIFILE_CLASS_SHORT - ) != null - private fun PropertyDescriptor.hasCustomAccessor() = !(getter?.isDefault ?: true) || !(setter?.isDefault ?: true) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverloadResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverloadResolver.java index 2e7bd8747cd..7f91bc1c5cf 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverloadResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverloadResolver.java @@ -195,6 +195,8 @@ public class OverloadResolver { @NotNull Set> redeclarations) { for (Pair redeclaration : redeclarations) { CallableMemberDescriptor memberDescriptor = redeclaration.getSecond(); + if (DescriptorUtils.isTopLevelMainFunction(memberDescriptor)) break; + JetDeclaration jetDeclaration = redeclaration.getFirst(); if (memberDescriptor instanceof PropertyDescriptor) { trace.report(Errors.REDECLARATION.on(jetDeclaration, memberDescriptor.getName().asString())); diff --git a/compiler/testData/codegen/boxMultiFile/mainInFiles/1.kt b/compiler/testData/codegen/boxMultiFile/mainInFiles/1.kt new file mode 100644 index 00000000000..f8b6659280a --- /dev/null +++ b/compiler/testData/codegen/boxMultiFile/mainInFiles/1.kt @@ -0,0 +1,21 @@ +package test + +class A {} + +fun getMain(className: String): java.lang.reflect.Method { + val classLoader = A().javaClass.classLoader + return classLoader.loadClass(className).getDeclaredMethod("main", Array::class.java) +} + +fun box(): String { + val bMain = getMain("pkg.AKt") + val cMain = getMain("pkg.BKt") + + val args = Array(1, { "" }) + + bMain.invoke(null, args) + cMain.invoke(null, args) + + return args[0] +} + diff --git a/compiler/testData/codegen/boxMultiFile/mainInFiles/a.kt b/compiler/testData/codegen/boxMultiFile/mainInFiles/a.kt new file mode 100644 index 00000000000..a22ae536ac8 --- /dev/null +++ b/compiler/testData/codegen/boxMultiFile/mainInFiles/a.kt @@ -0,0 +1,5 @@ +package pkg + +fun main(args: Array) { + args[0] += "O" +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxMultiFile/mainInFiles/b.kt b/compiler/testData/codegen/boxMultiFile/mainInFiles/b.kt new file mode 100644 index 00000000000..1d15647a4b5 --- /dev/null +++ b/compiler/testData/codegen/boxMultiFile/mainInFiles/b.kt @@ -0,0 +1,5 @@ +package pkg + +fun main(args: Array) { + args[0] += "K" +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/redeclarations/RedeclarationMainInMultiFile.kt b/compiler/testData/diagnostics/tests/redeclarations/RedeclarationMainInMultiFile.kt new file mode 100644 index 00000000000..315f24d42af --- /dev/null +++ b/compiler/testData/diagnostics/tests/redeclarations/RedeclarationMainInMultiFile.kt @@ -0,0 +1,5 @@ +// FILE: a.kt +fun main(args: Array) {} + +// FILE: b.kt +fun main(args: Array) {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/redeclarations/RedeclarationMainInMultiFile.txt b/compiler/testData/diagnostics/tests/redeclarations/RedeclarationMainInMultiFile.txt new file mode 100644 index 00000000000..abafee5d3ef --- /dev/null +++ b/compiler/testData/diagnostics/tests/redeclarations/RedeclarationMainInMultiFile.txt @@ -0,0 +1,4 @@ +package + +public fun main(/*0*/ args: kotlin.Array): kotlin.Unit +public fun main(/*0*/ args: kotlin.Array): kotlin.Unit diff --git a/compiler/testData/diagnostics/testsWithStdLib/CallToMainRedeclaredInMultiFile.kt b/compiler/testData/diagnostics/testsWithStdLib/CallToMainRedeclaredInMultiFile.kt new file mode 100644 index 00000000000..35289768bc3 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/CallToMainRedeclaredInMultiFile.kt @@ -0,0 +1,8 @@ +// FILE: a.kt +fun main(args: Array) {} + +// FILE: b.kt +fun main(args: Array) {} + +// FILE: c.kt +fun foo() { main(arrayOf("a", "b")) } \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/CallToMainRedeclaredInMultiFile.txt b/compiler/testData/diagnostics/testsWithStdLib/CallToMainRedeclaredInMultiFile.txt new file mode 100644 index 00000000000..ba0e270ffe6 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/CallToMainRedeclaredInMultiFile.txt @@ -0,0 +1,5 @@ +package + +public fun foo(): kotlin.Unit +public fun main(/*0*/ args: kotlin.Array): kotlin.Unit +public fun main(/*0*/ args: kotlin.Array): kotlin.Unit diff --git a/compiler/testData/diagnostics/testsWithStdLib/RedeclarationMainInMultiFileClass.kt b/compiler/testData/diagnostics/testsWithStdLib/RedeclarationMainInMultiFileClass.kt new file mode 100644 index 00000000000..c884b35734c --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/RedeclarationMainInMultiFileClass.kt @@ -0,0 +1,11 @@ +// FILE: a.kt +@file:JvmName("Util") +@file:JvmMultifileClass + +fun main(args: Array) {} + +// FILE: b.kt +@file:JvmName("Util") +@file:JvmMultifileClass + +fun main(args: Array) {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/RedeclarationMainInMultiFileClass.txt b/compiler/testData/diagnostics/testsWithStdLib/RedeclarationMainInMultiFileClass.txt new file mode 100644 index 00000000000..abafee5d3ef --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/RedeclarationMainInMultiFileClass.txt @@ -0,0 +1,4 @@ +package + +public fun main(/*0*/ args: kotlin.Array): kotlin.Unit +public fun main(/*0*/ args: kotlin.Array): kotlin.Unit diff --git a/compiler/testData/integration/ant/jvm/mainInFiles/a.kt b/compiler/testData/integration/ant/jvm/mainInFiles/a.kt new file mode 100644 index 00000000000..486f8f21eac --- /dev/null +++ b/compiler/testData/integration/ant/jvm/mainInFiles/a.kt @@ -0,0 +1,5 @@ +package pkg + +fun main(args : Array) { + println("A.main Ok") +} diff --git a/compiler/testData/integration/ant/jvm/mainInFiles/b.kt b/compiler/testData/integration/ant/jvm/mainInFiles/b.kt new file mode 100644 index 00000000000..35572138f55 --- /dev/null +++ b/compiler/testData/integration/ant/jvm/mainInFiles/b.kt @@ -0,0 +1,5 @@ +package pkg + +fun main(args : Array) { + println("B.main Ok") +} diff --git a/compiler/testData/integration/ant/jvm/mainInFiles/build.log.expected b/compiler/testData/integration/ant/jvm/mainInFiles/build.log.expected new file mode 100644 index 00000000000..3f79f3dd7fb --- /dev/null +++ b/compiler/testData/integration/ant/jvm/mainInFiles/build.log.expected @@ -0,0 +1,12 @@ +OUT: +Buildfile: [TestData]/build.xml + +build: + [kotlinc] Compiling [[TestData]] => [[Temp]/hello.jar] + [java] A.main Ok + [java] B.main Ok + +BUILD SUCCESSFUL +Total time: [time] + +Return code: 0 diff --git a/compiler/testData/integration/ant/jvm/mainInFiles/build.xml b/compiler/testData/integration/ant/jvm/mainInFiles/build.xml new file mode 100644 index 00000000000..7200da3b10b --- /dev/null +++ b/compiler/testData/integration/ant/jvm/mainInFiles/build.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index 5aea61388ce..f6ec2c63bca 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -11802,6 +11802,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("RedeclarationMainInMultiFile.kt") + public void testRedeclarationMainInMultiFile() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/redeclarations/RedeclarationMainInMultiFile.kt"); + doTest(fileName); + } + @TestMetadata("Redeclarations.kt") public void testRedeclarations() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/redeclarations/Redeclarations.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java index 600decbf468..b1e3946059c 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java @@ -35,6 +35,12 @@ public class JetDiagnosticsTestWithStdLibGenerated extends AbstractJetDiagnostic JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("CallToMainRedeclaredInMultiFile.kt") + public void testCallToMainRedeclaredInMultiFile() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/CallToMainRedeclaredInMultiFile.kt"); + doTest(fileName); + } + @TestMetadata("commonCollections.kt") public void testCommonCollections() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/commonCollections.kt"); @@ -77,6 +83,12 @@ public class JetDiagnosticsTestWithStdLibGenerated extends AbstractJetDiagnostic doTest(fileName); } + @TestMetadata("RedeclarationMainInMultiFileClass.kt") + public void testRedeclarationMainInMultiFileClass() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/RedeclarationMainInMultiFileClass.kt"); + doTest(fileName); + } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/annotations") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxMultiFileCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxMultiFileCodegenTestGenerated.java index 3656a86504b..dba5251b4dd 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxMultiFileCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxMultiFileCodegenTestGenerated.java @@ -89,6 +89,12 @@ public class BlackBoxMultiFileCodegenTestGenerated extends AbstractBlackBoxCodeg doTestMultiFile(fileName); } + @TestMetadata("mainInFiles") + public void testMainInFiles() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxMultiFile/mainInFiles/"); + doTestMultiFile(fileName); + } + @TestMetadata("multifileClassPartsInitialization") public void testMultifileClassPartsInitialization() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxMultiFile/multifileClassPartsInitialization/"); diff --git a/compiler/tests/org/jetbrains/kotlin/integration/AntTaskTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/integration/AntTaskTestGenerated.java index 46d019aad90..b12621b44fc 100644 --- a/compiler/tests/org/jetbrains/kotlin/integration/AntTaskTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/integration/AntTaskTestGenerated.java @@ -71,6 +71,12 @@ public class AntTaskTestGenerated extends AbstractAntTaskTest { doTest(fileName); } + @TestMetadata("mainInFiles") + public void testMainInFiles() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/integration/ant/jvm/mainInFiles/"); + doTest(fileName); + } + @TestMetadata("moduleName") public void testModuleName() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/integration/ant/jvm/moduleName/"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java index 44bf4db53cb..7eb2ca79f85 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java @@ -630,6 +630,21 @@ public class DescriptorUtils { return SourceFile.NO_SOURCE_FILE; } + public static boolean isTopLevelMainFunction(@NotNull DeclarationDescriptor descriptor) { + if (!(descriptor instanceof FunctionDescriptor) || + !isTopLevelDeclaration(descriptor) || + !"main".equals(descriptor.getName().asString())) return false; + + FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor; + List valueParameterDescriptors = functionDescriptor.getValueParameters(); + if (valueParameterDescriptors.size() != 1) return false; + + JetType type = valueParameterDescriptors.get(0).getType(); + + return KotlinBuiltIns.isArray(type) && type.getArguments().size() == 1 && KotlinBuiltIns.isString(type.getArguments().get(0).getType()); + } + + private static void getSubPackagesFqNames(PackageViewDescriptor packageView, Set result) { FqName fqName = packageView.getFqName(); if (!fqName.isRoot()) {