Added AllowNullableArrayArgsInMain (1.4+) language setting

This commit is contained in:
Vladimir Dolzhenko
2019-10-29 19:20:35 +01:00
parent 6ecc7eab7e
commit 26255f8501
11 changed files with 61 additions and 28 deletions
@@ -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) {
@@ -92,12 +92,16 @@ fun parseLanguageVersionSettings(directives: Map<String, String>): CompilerTestL
fun defaultLanguageVersionSettings(): CompilerTestLanguageVersionSettings =
CompilerTestLanguageVersionSettings(emptyMap(), ApiVersion.LATEST_STABLE, LanguageVersion.LATEST_STABLE)
fun setupLanguageVersionSettingsForMultifileCompilerTests(files: List<File>, environment: KotlinCoreEnvironment) {
fun languageVersionSettingsFromText(fileTexts: List<String>): LanguageVersionSettings {
val allDirectives = HashMap<String, String>()
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<File>, environment: KotlinCoreEnvironment) {
environment.configuration.languageVersionSettings = languageVersionSettingsFromText(files.map { it.readText() })
}
fun setupLanguageVersionSettingsForCompilerTests(originalFileText: String, environment: KotlinCoreEnvironment) {
@@ -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
@@ -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<LanguageVersionSettings> {
return CachedValuesManager.getManager(project).createCachedValue({
CachedValueProvider.Result(
@@ -1,2 +0,0 @@
fun main(args: Array<String?>) { // yes
}
@@ -0,0 +1,4 @@
// !LANGUAGE: +AllowNullableArrayArgsInMain
fun main(args: Array<String?>) { // yes
}
@@ -0,0 +1,2 @@
fun main(args: Array<String?>) { // no
}
@@ -1,2 +0,0 @@
fun main(args: Array<String?>?) { // yes
}
@@ -0,0 +1,4 @@
// !LANGUAGE: +AllowNullableArrayArgsInMain
fun main(args: Array<String?>?) { // yes
}
@@ -0,0 +1,2 @@
fun main(args: Array<String?>?) { // no
}
@@ -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<PsiComment>()?.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())