diff --git a/compiler/frontend/src/org/jetbrains/kotlin/idea/MainFunctionDetector.kt b/compiler/frontend/src/org/jetbrains/kotlin/idea/MainFunctionDetector.kt index d5366eb7821..11c4cad27f8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/idea/MainFunctionDetector.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/idea/MainFunctionDetector.kt @@ -125,7 +125,9 @@ class MainFunctionDetector { if (typeArguments.size != 1) return false val typeArgument = typeArguments[0].type - if (!KotlinBuiltIns.isStringOrNullableString(typeArgument)) { + if (!(languageVersionSettings.supportsFeature(LanguageFeature.AllowNullableArrayArgsInMain) && + KotlinBuiltIns.isStringOrNullableString(typeArgument) || KotlinBuiltIns.isString(typeArgument)) + ) { return false } if (typeArguments[0].projectionKind === Variance.IN_VARIANCE) { diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/checkers/CompilerTestLanguageVersionSettings.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/checkers/CompilerTestLanguageVersionSettings.kt index 6fc5748715c..5413210611a 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/checkers/CompilerTestLanguageVersionSettings.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/checkers/CompilerTestLanguageVersionSettings.kt @@ -92,12 +92,16 @@ fun parseLanguageVersionSettings(directives: Map): CompilerTestL fun defaultLanguageVersionSettings(): CompilerTestLanguageVersionSettings = CompilerTestLanguageVersionSettings(emptyMap(), ApiVersion.LATEST_STABLE, LanguageVersion.LATEST_STABLE) -fun setupLanguageVersionSettingsForMultifileCompilerTests(files: List, environment: KotlinCoreEnvironment) { +fun languageVersionSettingsFromText(fileTexts: List): LanguageVersionSettings { val allDirectives = HashMap() - for (file in files) { - allDirectives.putAll(KotlinTestUtils.parseDirectives(file.readText())) + for (fileText in fileTexts) { + allDirectives.putAll(KotlinTestUtils.parseDirectives(fileText)) } - environment.configuration.languageVersionSettings = parseLanguageVersionSettingsOrDefault(allDirectives) + return parseLanguageVersionSettingsOrDefault(allDirectives) +} + +fun setupLanguageVersionSettingsForMultifileCompilerTests(files: List, environment: KotlinCoreEnvironment) { + environment.configuration.languageVersionSettings = languageVersionSettingsFromText(files.map { it.readText() }) } fun setupLanguageVersionSettingsForCompilerTests(originalFileText: String, environment: KotlinCoreEnvironment) { diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index aa077c23b02..5b32f65b715 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -110,6 +110,7 @@ enum class LanguageFeature( MixedNamedArgumentsInTheirOwnPosition(KOTLIN_1_4), ProhibitTailrecOnVirtualMember(KOTLIN_1_4, kind = BUG_FIX), ProperComputationOrderOfTailrecDefaultParameters(KOTLIN_1_4), + AllowNullableArrayArgsInMain(KOTLIN_1_4), ProperVisibilityForCompanionObjectInstanceField(sinceVersion = null, kind = BUG_FIX), // Temporarily disabled, see KT-27084/KT-22379 diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/Platform.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/Platform.kt index c0c31fb1d14..ba548819682 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/Platform.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/Platform.kt @@ -28,6 +28,7 @@ import com.intellij.psi.PsiElement import com.intellij.psi.util.CachedValue import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager +import org.jetbrains.annotations.TestOnly import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.cli.common.arguments.* import org.jetbrains.kotlin.cli.common.messages.MessageCollector @@ -168,6 +169,17 @@ val Module.languageVersionSettings: LanguageVersionSettings return cachedValue.value } +@TestOnly // public for tests +fun Module.setLanguageVersionSettings(value: LanguageVersionSettings) = + putUserData( + LANGUAGE_VERSION_SETTINGS, + CachedValuesManager.getManager(project).createCachedValue({ + CachedValueProvider.Result( + value, ProjectRootModificationTracker.getInstance(project) + ) + }, false) + ) + private fun Module.createCachedValueForLanguageVersionSettings(): CachedValue { return CachedValuesManager.getManager(project).createCachedValue({ CachedValueProvider.Result( diff --git a/idea/testData/run/MainInTest/module/src/mainNullableArgsInArray.kt b/idea/testData/run/MainInTest/module/src/mainNullableArgsInArray.kt deleted file mode 100644 index 8b26893d33b..00000000000 --- a/idea/testData/run/MainInTest/module/src/mainNullableArgsInArray.kt +++ /dev/null @@ -1,2 +0,0 @@ -fun main(args: Array) { // yes -} diff --git a/idea/testData/run/MainInTest/module/src/mainNullableArgsInArray_after.kt b/idea/testData/run/MainInTest/module/src/mainNullableArgsInArray_after.kt new file mode 100644 index 00000000000..f0458600782 --- /dev/null +++ b/idea/testData/run/MainInTest/module/src/mainNullableArgsInArray_after.kt @@ -0,0 +1,4 @@ +// !LANGUAGE: +AllowNullableArrayArgsInMain + +fun main(args: Array) { // yes +} diff --git a/idea/testData/run/MainInTest/module/src/mainNullableArgsInArray_before.kt b/idea/testData/run/MainInTest/module/src/mainNullableArgsInArray_before.kt new file mode 100644 index 00000000000..816a5a9ffc7 --- /dev/null +++ b/idea/testData/run/MainInTest/module/src/mainNullableArgsInArray_before.kt @@ -0,0 +1,2 @@ +fun main(args: Array) { // no +} diff --git a/idea/testData/run/MainInTest/module/src/mainNullableArgsNullableArray.kt b/idea/testData/run/MainInTest/module/src/mainNullableArgsNullableArray.kt deleted file mode 100644 index c3755dc1040..00000000000 --- a/idea/testData/run/MainInTest/module/src/mainNullableArgsNullableArray.kt +++ /dev/null @@ -1,2 +0,0 @@ -fun main(args: Array?) { // yes -} diff --git a/idea/testData/run/MainInTest/module/src/mainNullableArgsNullableArray_after.kt b/idea/testData/run/MainInTest/module/src/mainNullableArgsNullableArray_after.kt new file mode 100644 index 00000000000..fdf27192b53 --- /dev/null +++ b/idea/testData/run/MainInTest/module/src/mainNullableArgsNullableArray_after.kt @@ -0,0 +1,4 @@ +// !LANGUAGE: +AllowNullableArrayArgsInMain + +fun main(args: Array?) { // yes +} diff --git a/idea/testData/run/MainInTest/module/src/mainNullableArgsNullableArray_before.kt b/idea/testData/run/MainInTest/module/src/mainNullableArgsNullableArray_before.kt new file mode 100644 index 00000000000..2cd7b78d80d --- /dev/null +++ b/idea/testData/run/MainInTest/module/src/mainNullableArgsNullableArray_before.kt @@ -0,0 +1,2 @@ +fun main(args: Array?) { // no +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/run/RunConfigurationTest.kt b/idea/tests/org/jetbrains/kotlin/idea/run/RunConfigurationTest.kt index 231de496ca4..f6ae18ba5b7 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/run/RunConfigurationTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/run/RunConfigurationTest.kt @@ -19,9 +19,11 @@ import com.intellij.psi.PsiManager import com.intellij.refactoring.RefactoringFactory import com.intellij.testFramework.MapDataContext import com.intellij.testFramework.PsiTestUtil +import org.jetbrains.kotlin.checkers.languageVersionSettingsFromText import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl import org.jetbrains.kotlin.idea.MainFunctionDetector import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny +import org.jetbrains.kotlin.idea.project.setLanguageVersionSettings import org.jetbrains.kotlin.idea.search.allScope import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelFunctionFqnNameIndex @@ -34,12 +36,11 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.allChildren import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner -import org.junit.Assert import org.junit.runner.RunWith import java.io.File import java.util.* -private val RUN_PREFIX = "// RUN:" +private const val RUN_PREFIX = "// RUN:" @RunWith(JUnit3WithIdeaConfigurationRunner::class) class RunConfigurationTest: KotlinCodeInsightTestCase() { @@ -56,44 +57,49 @@ class RunConfigurationTest: KotlinCodeInsightTestCase() { val runConfiguration = createConfigurationFromMain("some.main") val javaParameters = getJavaRunParameters(runConfiguration) - Assert.assertTrue(javaParameters.classPath.rootDirs.contains(createResult.srcOutputDir)) - Assert.assertTrue(javaParameters.classPath.rootDirs.contains(createResult.testOutputDir)) + assertTrue(javaParameters.classPath.rootDirs.contains(createResult.srcOutputDir)) + assertTrue(javaParameters.classPath.rootDirs.contains(createResult.testOutputDir)) fun functionVisitor(function: KtNamedFunction) { + val file = function.containingKtFile val options = function.bodyExpression?.allChildren?.filterIsInstance()?.map { it.text.trim().replace("//", "").trim() }?.filter { it.isNotBlank() }?.toList() ?: emptyList() if (options.isNotEmpty()) { val assertIsMain = "yes" in options val assertIsNotMain = "no" in options + val languageVersionSettings = languageVersionSettingsFromText(listOf(file.text)) + createResult.module.setLanguageVersionSettings(languageVersionSettings) val isMainFunction = - MainFunctionDetector(LanguageVersionSettingsImpl.DEFAULT) { it.resolveToDescriptorIfAny() }.isMain(function) + MainFunctionDetector(languageVersionSettings) { it.resolveToDescriptorIfAny() }.isMain(function) if (assertIsMain) { - Assert.assertTrue("The function ${function.fqName?.asString()} should be main", isMainFunction) + assertTrue("$file: The function ${function.fqName?.asString()} should be main", isMainFunction) } if (assertIsNotMain) { - Assert.assertFalse("The function ${function.fqName?.asString()} should NOT be main", isMainFunction) + assertFalse("$file: The function ${function.fqName?.asString()} should NOT be main", isMainFunction) } if (isMainFunction) { createConfigurationFromMain(function.fqName?.asString()!!).checkConfiguration() - Assert.assertNotNull("Kotlin configuration producer should produce configuration for ${function.fqName?.asString()}", - KotlinRunConfigurationProducer.getEntryPointContainer(function)) + assertNotNull( + "$file: Kotlin configuration producer should produce configuration for ${function.fqName?.asString()}", + KotlinRunConfigurationProducer.getEntryPointContainer(function) + ) } else { try { createConfigurationFromMain(function.fqName?.asString()!!).checkConfiguration() - Assert.fail("configuration for function ${function.fqName?.asString()} at least shouldn't pass checkConfiguration()") + fail("$file: configuration for function ${function.fqName?.asString()} at least shouldn't pass checkConfiguration()") } catch (expected: Throwable) { } if (function.containingFile.text.startsWith("// entryPointExists")) { - Assert.assertNotNull( - "Kotlin configuration producer should produce configuration for ${function.fqName?.asString()}", + assertNotNull( + "$file: Kotlin configuration producer should produce configuration for ${function.fqName?.asString()}", KotlinRunConfigurationProducer.getEntryPointContainer(function) ) } else { - Assert.assertNull( + assertNull( "Kotlin configuration producer shouldn't produce configuration for ${function.fqName?.asString()}", KotlinRunConfigurationProducer.getEntryPointContainer(function) ) @@ -132,8 +138,8 @@ class RunConfigurationTest: KotlinCodeInsightTestCase() { val javaParameters = getJavaRunParameters(kotlinRunConfiguration) - Assert.assertTrue(javaParameters.classPath.rootDirs.contains(dependencyModuleSrcDir)) - Assert.assertTrue(javaParameters.classPath.rootDirs.contains(moduleWithDependencySrcDir)) + assertTrue(javaParameters.classPath.rootDirs.contains(dependencyModuleSrcDir)) + assertTrue(javaParameters.classPath.rootDirs.contains(moduleWithDependencySrcDir)) } fun testLongCommandLine() { @@ -170,7 +176,7 @@ class RunConfigurationTest: KotlinCodeInsightTestCase() { val rename = RefactoringFactory.getInstance(getTestProject()).createRename(obj, "Bar") rename.run() - Assert.assertEquals("renameTest.Bar", runConfiguration.MAIN_CLASS_NAME) + assertEquals("renameTest.Bar", runConfiguration.MAIN_CLASS_NAME) } fun testUpdateOnPackageRename() { @@ -183,7 +189,7 @@ class RunConfigurationTest: KotlinCodeInsightTestCase() { val rename = RefactoringFactory.getInstance(getTestProject()).createRename(pkg, "afterRenameTest") rename.run() - Assert.assertEquals("afterRenameTest.Foo", runConfiguration.MAIN_CLASS_NAME) + assertEquals("afterRenameTest.Foo", runConfiguration.MAIN_CLASS_NAME) } fun testWithModuleForJdk6() { @@ -204,7 +210,7 @@ class RunConfigurationTest: KotlinCodeInsightTestCase() { val javaParameters = getJavaRunParameters(createConfigurationFromMain("some.main")) - Assert.assertEquals(moduleName, javaParameters.moduleName) + assertEquals(moduleName, javaParameters.moduleName) } private fun doTest(configureRuntime: (Module, Sdk) -> Unit) { @@ -239,7 +245,7 @@ class RunConfigurationTest: KotlinCodeInsightTestCase() { } } ) - Assert.assertEquals(expectedClasses, actualClasses) + assertEquals(expectedClasses, actualClasses) } finally { ConfigLibraryUtil.unConfigureKotlinRuntimeAndSdk(createModuleResult.module, mockJdk())